batshit crazy

This commit is contained in:
MyNameIsBatman 2021-11-27 04:48:13 -03:00
parent 357e60bba2
commit 78cd946335
31 changed files with 535 additions and 84 deletions

View File

@ -30,7 +30,7 @@
"system.animation.fps": 24, "system.animation.fps": 24,
"system.limits.fps": false, "system.limits.fps": false,
"system.dispatcher.log": false, "system.dispatcher.log": false,
"system.packet.log": false, "system.packet.log": true,
"system.pong.manually": true, "system.pong.manually": true,
"system.pong.interval.ms": 20000, "system.pong.interval.ms": 20000,
"room.color.skip.transition": true, "room.color.skip.transition": true,

View File

@ -5,7 +5,9 @@
"thumbnails.url": "https://nitro.nitrots.co/camera/thumbnail/%thumbnail%.png", "thumbnails.url": "https://nitro.nitrots.co/camera/thumbnail/%thumbnail%.png",
"url.prefix": "http://localhost:3000", "url.prefix": "http://localhost:3000",
"floorplan.tile.url": "${asset.url}/floorplan-editor/tiles.json", "floorplan.tile.url": "${asset.url}/floorplan-editor/tiles.json",
"habbopages.url": "http://localhost:3000/", "habbopages.url": "${url.prefix}/",
"group.homepage.url": "${url.prefix}/groups/%groupid%/id",
"guide.help.alpha.groupid": 0,
"chat.viewer.height.percentage": 0.40, "chat.viewer.height.percentage": 0.40,
"widget.dimmer.colorwheel": false, "widget.dimmer.colorwheel": false,
"hotelview": { "hotelview": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

View File

@ -0,0 +1,10 @@
import { NitroEvent } from '@nitrots/nitro-renderer';
export class GuideToolEvent extends NitroEvent
{
public static readonly SHOW_GUIDE_TOOL: string = 'GTE_SHOW_GUIDE_TOOL';
public static readonly HIDE_GUIDE_TOOL: string = 'GTE_HIDE_GUIDE_TOOL';
public static readonly TOGGLE_GUIDE_TOOL: string = 'GTE_TOGGLE_GUIDE_TOOL';
public static readonly CREATE_HELP_REQUEST: string = 'GTE_CREATE_HELP_REQUEST';
public static readonly CREATE_BULLY_REQUEST: string = 'GTE_CREATE_BULLY_REQUEST';
}

View File

@ -0,0 +1 @@
export * from './GuideToolEvent';

View File

@ -2,6 +2,7 @@ export * from './avatar-editor';
export * from './camera'; export * from './camera';
export * from './catalog'; export * from './catalog';
export * from './friends'; export * from './friends';
export * from './guide-tool';
export * from './inventory'; export * from './inventory';
export * from './navigator'; export * from './navigator';
export * from './notification-center'; export * from './notification-center';

View File

@ -24,3 +24,4 @@
@import './help/HelpView'; @import './help/HelpView';
@import './floorplan-editor/FloorplanEditorView'; @import './floorplan-editor/FloorplanEditorView';
@import './nitropedia/NitropediaView'; @import './nitropedia/NitropediaView';
@import './guide-tool/GuideToolView';

View File

@ -0,0 +1,27 @@
.nitro-guide-tool {
width: 250px;
.duty-status {
border-radius: 0.25rem;
border-color: #B6BEC5 !important;
background-color: #CDD3D9;
border: 2px solid;
}
.duty-switch {
width: 38px;
height: 21px;
cursor: pointer;
background-image: url(../../assets/images/guide-tool/guide_tool_duty_switch.png);
&.off {
background-position: 0px -22px;
}
}
.info-icon {
width: 23px;
height: 24px;
background-image: url(../../assets/images/guide-tool/guide_tool_info_icon.png);
}
}

View File

@ -0,0 +1,216 @@
import { GuideOnDutyStatusMessageEvent, GuideSessionAttachedMessageEvent, GuideSessionOnDutyUpdateMessageComposer, GuideSessionStartedMessageEvent, ILinkEventTracker, PerkAllowancesMessageEvent, PerkEnum } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { AddEventLinkTracker, GetConfiguration, LocalizeText, RemoveLinkEventTracker } from '../../api';
import { GuideToolEvent, NotificationAlertEvent } from '../../events';
import { CreateMessageHook, dispatchUiEvent, SendMessageHook, useUiEvent } from '../../hooks';
import { NitroCardHeaderView, NitroCardView } from '../../layout';
import { GuideSessionState } from './common';
import { GuideToolAcceptView } from './views/guide-accept/GuideToolAcceptView';
import { GuideToolMenuView } from './views/guide-tool-menu/GuideToolMenuView';
import { GuideToolOngoingView } from './views/ongoing/GuideToolOngoingView';
import { GuildToolUserCreateRequestView } from './views/user-create-request/GuildToolUserCreateRequestView';
export const GuideToolView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState<boolean>(false);
const [ headerText, setHeaderText ] = useState<string>(LocalizeText('guide.help.guide.tool.title'));
const [ sessionState, setSessionState ] = useState<string>(GuideSessionState.GUIDE_TOOL_MENU);
const [ isOnDuty, setIsOnDuty ] = useState<boolean>(false);
const [ isHandlingBullyReports, setIsHandlingBullyReports ] = useState<boolean>(false);
const [ isHandlingGuideRequests, setIsHandlingGuideRequests ] = useState<boolean>(false);
const [ isHandlingHelpRequests, setIsHandlingHelpRequests ] = useState<boolean>(false);
const [ helpersOnDuty, setHelpersOnDuty ] = useState<number>(0);
const [ guidesOnDuty, setGuidesOnDuty ] = useState<number>(0);
const [ guardiansOnDuty, setGuardiansOnDuty ] = useState<number>(0);
const [ helpRequestDescription, setHelpRequestDescription ] = useState<string>(null);
const [ helpRequestCountdown, setHelpRequestCountdown ] = useState<number>(0);
const [ ongoingUserId, setOngoingUserId ] = useState<number>(0);
const [ ongoingUsername, setOngoingUsername ] = useState<string>(null);
const [ ongoingFigure, setOngoingFigure ] = useState<string>(null);
const onGuideToolEvent = useCallback((event: GuideToolEvent) =>
{
switch(event.type)
{
case GuideToolEvent.SHOW_GUIDE_TOOL:
setIsVisible(true);
return;
case GuideToolEvent.HIDE_GUIDE_TOOL:
setIsVisible(false);
return;
case GuideToolEvent.TOGGLE_GUIDE_TOOL:
setIsVisible(value => !value);
return;
case GuideToolEvent.CREATE_HELP_REQUEST:
setSessionState(GuideSessionState.USER_CREATE);
setHeaderText(LocalizeText('guide.help.request.user.create.title'));
setIsVisible(true);
return;
}
}, []);
useUiEvent(GuideToolEvent.SHOW_GUIDE_TOOL, onGuideToolEvent);
useUiEvent(GuideToolEvent.HIDE_GUIDE_TOOL, onGuideToolEvent);
useUiEvent(GuideToolEvent.TOGGLE_GUIDE_TOOL, onGuideToolEvent);
useUiEvent(GuideToolEvent.CREATE_HELP_REQUEST, onGuideToolEvent);
const onPerkAllowancesMessageEvent = useCallback((event: PerkAllowancesMessageEvent) =>
{
const parser = event.getParser();
if(!parser.isAllowed(PerkEnum.USE_GUIDE_TOOL) && isOnDuty)
{
setIsOnDuty(false);
SendMessageHook(new GuideSessionOnDutyUpdateMessageComposer(false, false, false, false));
}
}, [ isOnDuty, setIsOnDuty ]);
CreateMessageHook(PerkAllowancesMessageEvent, onPerkAllowancesMessageEvent);
const onGuideOnDutyStatusMessageEvent = useCallback((event: GuideOnDutyStatusMessageEvent) =>
{
const parser = event.getParser();
setIsOnDuty(parser.onDuty);
setGuidesOnDuty(parser.guidesOnDuty);
setHelpersOnDuty(parser.helpersOnDuty);
setGuardiansOnDuty(parser.guardiansOnDuty);
}, [ setIsOnDuty, setHelpersOnDuty, setGuidesOnDuty, setGuardiansOnDuty ]);
CreateMessageHook(GuideOnDutyStatusMessageEvent, onGuideOnDutyStatusMessageEvent);
const onGuideSessionAttachedMessageEvent = useCallback((event: GuideSessionAttachedMessageEvent) =>
{
const parser = event.getParser();
if(parser.asGuide)
{
if(!isOnDuty) return;
setSessionState(GuideSessionState.GUIDE_ACCEPT);
setHeaderText(LocalizeText('guide.help.request.guide.accept.title'));
setHelpRequestDescription(parser.helpRequestDescription);
setHelpRequestCountdown(parser.roleSpecificWaitTime);
}
else
{
setSessionState(GuideSessionState.USER_PENDING);
setHeaderText(LocalizeText('guide.help.request.user.pending.title'));
setHelpRequestDescription(parser.helpRequestDescription);
}
setIsVisible(true);
}, [ isOnDuty ]);
CreateMessageHook(GuideSessionAttachedMessageEvent, onGuideSessionAttachedMessageEvent);
const onGuideSessionStartedMessageEvent = useCallback((event: GuideSessionStartedMessageEvent) =>
{
const parser = event.getParser();
if(isOnDuty)
{
setSessionState(GuideSessionState.GUIDE_ONGOING);
setHeaderText(LocalizeText('guide.help.request.guide.ongoing.title', ['name'], [parser.requesterName]));
setOngoingUserId(parser.requesterUserId);
setOngoingUsername(parser.requesterName);
setOngoingFigure(parser.requesterFigure);
}
else
{
setSessionState(GuideSessionState.USER_ONGOING);
setHeaderText(LocalizeText('guide.help.request.user.ongoing.title', ['name'], [parser.guideName]));
setOngoingUserId(parser.guideUserId);
setOngoingUsername(parser.guideName);
setOngoingFigure(parser.guideFigure);
}
setIsVisible(true);
}, [ isOnDuty ]);
CreateMessageHook(GuideSessionStartedMessageEvent, onGuideSessionStartedMessageEvent);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'tour':
//Create Tour Request
return;
}
}, []);
useEffect(() =>
{
const linkTracker: ILinkEventTracker = {
linkReceived,
eventUrlPrefix: 'help/'
};
AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived ]);
const processAction = useCallback((action: string) =>
{
switch(action)
{
case 'close':
setIsVisible(false);
return;
case 'toggle_duty':
if(!isHandlingBullyReports && !isHandlingGuideRequests && !isHandlingHelpRequests)
{
dispatchUiEvent(new NotificationAlertEvent([LocalizeText('guide.help.guide.tool.noqueueselected.message')], null, null, null, LocalizeText('guide.help.guide.tool.noqueueselected.caption'), null));
return;
}
setIsOnDuty(v =>
{
SendMessageHook(new GuideSessionOnDutyUpdateMessageComposer(!v, v ? false : isHandlingGuideRequests, v ? false : isHandlingHelpRequests, v ? false : isHandlingBullyReports));
return !v;
});
return;
case 'forum_link':
const url: string = GetConfiguration<string>('group.homepage.url', '').replace('%groupid%', GetConfiguration<string>('guide.help.alpha.groupid', '0'));
window.open(url);
return;
}
}, [isHandlingBullyReports, isHandlingGuideRequests, isHandlingHelpRequests]);
if(!isVisible) return null;
return (
<NitroCardView className="nitro-guide-tool" simple={ true }>
<NitroCardHeaderView headerText={ headerText } onCloseClick={ () => processAction('close') } />
{ sessionState === GuideSessionState.GUIDE_TOOL_MENU &&
<GuideToolMenuView isOnDuty={ isOnDuty }
isHandlingGuideRequests={ isHandlingGuideRequests }
setIsHandlingGuideRequests={ setIsHandlingGuideRequests }
isHandlingHelpRequests={ isHandlingHelpRequests }
setIsHandlingHelpRequests={ setIsHandlingHelpRequests }
isHandlingBullyReports={ isHandlingBullyReports }
setIsHandlingBullyReports={ setIsHandlingBullyReports }
guidesOnDuty={ guidesOnDuty }
helpersOnDuty={ helpersOnDuty }
guardiansOnDuty={ guardiansOnDuty }
processAction={ processAction }
/> }
{ sessionState === GuideSessionState.GUIDE_ACCEPT &&
<GuideToolAcceptView helpRequestDescription={ helpRequestDescription } helpRequestCountdown={ helpRequestCountdown } processAction={ processAction } /> }
{ [ GuideSessionState.GUIDE_ONGOING, GuideSessionState.USER_ONGOING].includes(sessionState) &&
<GuideToolOngoingView isGuide={ isOnDuty } userId={ ongoingUserId } userName={ ongoingUsername } userFigure={ ongoingFigure } /> }
{ sessionState === GuideSessionState.USER_CREATE && <GuildToolUserCreateRequestView /> }
</NitroCardView>
);
};

View File

@ -0,0 +1,21 @@
export class GuideSessionState
{
public static readonly NONE: string = 'NONE';
public static readonly ERROR: string = 'ERROR';
public static readonly REJECTED: string = 'REJECTED';
public static readonly USER_CREATE: string = 'USER_CREATE';
public static readonly USER_PENDING: string = 'USER_PENDING';
public static readonly USER_ONGOING: string = 'USER_ONGOING';
public static readonly USER_FEEDBACK: string = 'USER_FEEDBACK';
public static readonly USER_THANKS: string = 'USER_THANKS';
public static readonly USER_GUIDE_DISCONNECTED: string = 'USER_GUIDE_DISCONNECTED';
public static readonly GUIDE_TOOL_MENU: string = 'GUIDE_TOOL_MENU';
public static readonly GUIDE_ACCEPT: string = 'GUIDE_ACCEPT';
public static readonly GUIDE_ONGOING: string = 'GUIDE_ONGOING';
public static readonly GUIDE_CLOSED: string = 'GUIDE_CLOSED';
public static readonly GUARDIAN_CHAT_REVIEW_ACCEPT: string = 'GUARDIAN_CHAT_REVIEW_ACCEPT';
public static readonly GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS: string = 'GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS';
public static readonly GUARDIAN_CHAT_REVIEW_VOTE: string = 'GUARDIAN_CHAT_REVIEW_VOTE';
public static readonly GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS: string = 'GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS';
public static readonly GUARDIAN_CHAT_REVIEW_RESULTS: string = 'GUARDIAN_CHAT_REVIEW_RESULTS';
}

View File

@ -0,0 +1 @@
export * from './GuideSessionState';

View File

@ -0,0 +1,30 @@
import { GuideSessionGuideDecidesMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../api';
import { SendMessageHook } from '../../../../hooks';
import { NitroCardContentView } from '../../../../layout';
import { GuideToolAcceptViewProps } from './GuideToolAcceptView.types';
export const GuideToolAcceptView: FC<GuideToolAcceptViewProps> = props =>
{
const { helpRequestDescription = null, helpRequestCountdown = 0, processAction = null } = props;
const answerRequest = useCallback((response: boolean) =>
{
SendMessageHook(new GuideSessionGuideDecidesMessageComposer(response));
}, []);
return (
<NitroCardContentView className="text-black flex flex-column gap-2">
<div className="duty-status py-2 px-3">
<div className="fw-bold">{ LocalizeText('guide.help.request.guide.accept.request.title') }</div>
<div className="text-muted">{ LocalizeText('guide.help.request.type.1') }</div>
<div className="text-wrap text-break">{ helpRequestDescription }</div>
</div>
<div className="d-flex flex-column gap-2 w-100">
<button className="btn btn-success btn-sm" onClick={ () => answerRequest(true) }>{ LocalizeText('guide.help.request.guide.accept.accept.button') }</button>
<button className="btn btn-danger btn-sm" onClick={ () => answerRequest(false) }>{ LocalizeText('guide.help.request.guide.accept.skip.link') }</button>
</div>
</NitroCardContentView>
);
};

View File

@ -0,0 +1,6 @@
export interface GuideToolAcceptViewProps
{
helpRequestDescription: string;
helpRequestCountdown: number;
processAction: (action: string) => void;
}

View File

@ -0,0 +1,66 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { NitroCardContentView } from '../../../../layout';
import { GuideToolMenuViewProps } from './GuideToolMenuView.types';
export const GuideToolMenuView: FC<GuideToolMenuViewProps> = props =>
{
const {
isOnDuty = false,
isHandlingGuideRequests = false,
setIsHandlingGuideRequests = null,
isHandlingHelpRequests = false,
setIsHandlingHelpRequests = null,
isHandlingBullyReports = false,
setIsHandlingBullyReports = null,
guidesOnDuty = 0,
helpersOnDuty = 0,
guardiansOnDuty = 0,
processAction = null
} = props;
return (
<>
<NitroCardContentView className="text-black flex flex-column gap-2">
<div className="duty-status py-2 px-3 d-flex gap-2 align-items-center">
<div>
<div className={ 'duty-switch' + (isOnDuty ? '' : ' off') } onClick={ () => processAction('toggle_duty') } />
</div>
<div>
<div className="fw-bold">{ LocalizeText('guide.help.guide.tool.yourstatus') }</div>
<div>{ LocalizeText(`guide.help.guide.tool.duty.${(isOnDuty ? 'on' : 'off')}`) }</div>
</div>
</div>
<div>
<div className="fw-bold">{ LocalizeText('guide.help.guide.tool.tickettypeselection.caption') }</div>
<div className="form-check">
<input className="form-check-input" disabled={ isOnDuty } type="checkbox" checked={ isHandlingGuideRequests } onChange={ e => { setIsHandlingGuideRequests(e.target.checked) } } />
<label className="form-check-label">{ LocalizeText('guide.help.guide.tool.tickettypeselection.guiderequests') }</label>
</div>
<div className="form-check">
<input className="form-check-input" disabled={ isOnDuty } type="checkbox" checked={ isHandlingHelpRequests } onChange={ e => { setIsHandlingHelpRequests(e.target.checked) } } />
<label className="form-check-label">{ LocalizeText('guide.help.guide.tool.tickettypeselection.onlyhelprequests') }</label>
</div>
<div className="form-check">
<input className="form-check-input" disabled={ isOnDuty } type="checkbox" checked={ isHandlingBullyReports } onChange={ e => { setIsHandlingBullyReports(e.target.checked) } } />
<label className="form-check-label">{ LocalizeText('guide.help.guide.tool.tickettypeselection.bullyreports') }</label>
</div>
</div>
<hr className="bg-dark m-0" />
<div className="d-flex align-items-center justify-content-center gap-2">
<div className="info-icon" />
<div>
<div dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.guidesonduty', ['amount'], [guidesOnDuty.toString()]) } } />
<div dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.helpersonduty', ['amount'], [helpersOnDuty.toString()]) } } />
<div dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.guardiansonduty', ['amount'], [guardiansOnDuty.toString()]) } } />
</div>
</div>
<hr className="bg-dark m-0 mt-auto" />
<div className="d-flex gap-2 w-100">
<button className="btn btn-primary btn-sm w-100 text-nowrap" onClick={ () => processAction('forum_link') }>{ LocalizeText('guide.help.guide.tool.forum.link') }</button>
<button className="btn btn-primary btn-sm w-100 text-nowrap" disabled>{ LocalizeText('guide.help.guide.tool.skill.link') }</button>
</div>
</NitroCardContentView>
</>
);
}

View File

@ -0,0 +1,14 @@
export interface GuideToolMenuViewProps
{
isOnDuty: boolean;
isHandlingGuideRequests: boolean;
setIsHandlingGuideRequests: (value: boolean) => void;
isHandlingHelpRequests: boolean;
setIsHandlingHelpRequests: (value: boolean) => void;
isHandlingBullyReports: boolean;
setIsHandlingBullyReports: (value: boolean) => void;
guidesOnDuty: number;
helpersOnDuty: number;
guardiansOnDuty: number;
processAction: (action: string) => void;
}

View File

@ -0,0 +1,27 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { NitroCardContentView } from '../../../../layout';
import { GuideToolOngoingViewProps } from './GuideToolOngoingView.types';
export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
{
const { isGuide = false, userId = 0, userName = null, userFigure = null } = props;
return (
<NitroCardContentView className="text-black flex flex-column gap-2">
<div className="d-flex gap-2 align-items-center">
{ isGuide && <button className="btn btn-primary btn-sm">{ LocalizeText('guide.help.request.guide.ongoing.visit.button') }</button> }
{ isGuide && <button className="btn btn-primary btn-sm">{ LocalizeText('guide.help.request.guide.ongoing.invite.button') }</button> }
{ !isGuide && <div>
<div className="fw-bold">{ userName }</div>
<div>{ LocalizeText('guide.help.request.user.ongoing.guide.desc') }</div>
</div> }
<div className="ms-auto text-decoration-underline cursor-pointer text-nowrap">{ LocalizeText('guide.help.common.report.link') }</div>
</div>
<hr className="bg-dark m-0" />
<div>chat</div>
<hr className="bg-dark m-0" />
<div className="btn btn-success btn-sm">{ LocalizeText('guide.help.request.' + (isGuide ? 'guide' : 'user') + '.ongoing.close.link') }</div>
</NitroCardContentView>
);
};

View File

@ -0,0 +1,7 @@
export interface GuideToolOngoingViewProps
{
isGuide: boolean;
userId: number;
userName: string;
userFigure: string;
}

View File

@ -0,0 +1,15 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { NitroCardContentView } from '../../../../layout';
export const GuildToolUserCreateRequestView: FC<{}> = props =>
{
return (
<NitroCardContentView className="text-black flex flex-column gap-2">
<div className="duty-status p-2 text-center">
{ LocalizeText('guide.help.request.user.create.help') }
</div>
</NitroCardContentView>
);
};

View File

@ -1,6 +1,5 @@
.nitro-help { .nitro-help {
height: 430px; width: 280px;
width: 300px;
.index-image { .index-image {
background: url('../../assets/images/help/help_index.png'); background: url('../../assets/images/help/help_index.png');

View File

@ -68,7 +68,7 @@ export const HelpView: FC<{}> = props =>
<HelpContextProvider value={ { helpReportState, setHelpReportState } }> <HelpContextProvider value={ { helpReportState, setHelpReportState } }>
<HelpMessageHandler /> <HelpMessageHandler />
{isVisible && {isVisible &&
<NitroCardView className="nitro-help"> <NitroCardView className="nitro-help" simple={true}>
<NitroCardHeaderView headerText={LocalizeText('help.button.cfh')} onCloseClick={() => setIsVisible(false)} /> <NitroCardHeaderView headerText={LocalizeText('help.button.cfh')} onCloseClick={() => setIsVisible(false)} />
<NitroCardContentView className="text-black"> <NitroCardContentView className="text-black">
<CurrentStepView /> <CurrentStepView />

View File

@ -34,17 +34,14 @@ export const DescribeReportView: FC<{}> = props =>
}, [helpReportState, message, setHelpReportState]); }, [helpReportState, message, setHelpReportState]);
return ( return (
<> <div className="d-flex flex-column gap-2 h-100">
<div className="d-grid col-12 mx-auto justify-content-center"> <h3 className="text-center m-0">{LocalizeText('help.emergency.chat_report.subtitle')}</h3>
<div className="col-12"><h3 className="fw-bold">{LocalizeText('help.emergency.chat_report.subtitle')}</h3></div> <div className="text-wrap">{LocalizeText('help.cfh.input.text')}</div>
<div className="text-wrap">{LocalizeText('help.cfh.input.text')}</div>
</div>
<div className="form-group mb-2"> <div className="form-group mb-2">
<textarea className="form-control" value={message} onChange={event => setMessage(event.target.value)} /> <textarea className="form-control" value={message} onChange={event => setMessage(event.target.value)} />
</div> </div>
<button className="btn btn-danger mt-2" type="button" disabled={message.length < 15} onClick={submitReport}>{LocalizeText('help.bully.submit')}</button> <button className="btn btn-danger" disabled={message.length < 15} onClick={submitReport}>{LocalizeText('help.bully.submit')}</button>
</> </div>
); );
} }

View File

@ -1,7 +1,8 @@
import { GetCfhStatusMessageComposer } from '@nitrots/nitro-renderer'; import { GetCfhStatusMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../api'; import { LocalizeText } from '../../../api';
import { SendMessageHook } from '../../../hooks'; import { GuideToolEvent } from '../../../events';
import { dispatchUiEvent, SendMessageHook } from '../../../hooks';
import { useHelpContext } from '../context/HelpContext'; import { useHelpContext } from '../context/HelpContext';
export const HelpIndexView: FC<{}> = props => export const HelpIndexView: FC<{}> = props =>
@ -20,24 +21,21 @@ export const HelpIndexView: FC<{}> = props =>
SendMessageHook(new GetCfhStatusMessageComposer(false)); SendMessageHook(new GetCfhStatusMessageComposer(false));
}, []); }, []);
const onNewHelpRequestClick = useCallback(() =>
{
dispatchUiEvent(new GuideToolEvent(GuideToolEvent.CREATE_HELP_REQUEST));
}, []);
return ( return (
<> <div className="d-flex flex-column gap-2">
<div className="d-grid col-12 mx-auto justify-content-center"> <div className="index-image align-self-center"></div>
<div className="col-12"><h3>{LocalizeText('help.main.frame.title')}</h3></div> <h3 className="m-0 text-center">{ LocalizeText('help.main.frame.title') }</h3>
<div className="index-image align-self-center"></div> <div className="d-flex flex-column gap-1">
<p className="text-center">{LocalizeText('help.main.self.description')}</p> <button className="btn btn-primary" onClick={onNewHelpRequestClick}>{LocalizeText('help.main.help.title')}</button>
<button className="btn btn-primary" onClick={onReportClick}>{LocalizeText('help.main.bully.subtitle')}</button>
<button className="btn btn-primary" disabled>{LocalizeText('help.main.self.tips.title')}</button>
<button className="btn btn-primary" onClick={onRequestMySanctionStatusClick}>{LocalizeText('help.main.my.sanction.status')}</button>
</div> </div>
</div>
<div className="d-grid gap-2 col-8 mx-auto">
<button className="btn btn-primary" type="button" onClick={onReportClick}>{LocalizeText('help.main.bully.subtitle')}</button>
<button className="btn btn-primary" type="button" disabled={true}>{LocalizeText('help.main.help.title')}</button>
<button className="btn btn-primary" type="button" disabled={true}>{LocalizeText('help.main.self.tips.title')}</button>
</div>
<div className="d-grid gap-2 col-8 mx-auto">
<button className="btn btn-link" type="button" onClick={onRequestMySanctionStatusClick}>{LocalizeText('help.main.my.sanction.status')}</button>
</div>
</>
) )
} }

View File

@ -54,18 +54,12 @@ export const SelectReportedChatsView: FC<{}> = props =>
}, [helpReportState, setHelpReportState]); }, [helpReportState, setHelpReportState]);
return ( return (
<> <div className="d-flex flex-column gap-2 h-100">
<div className="d-grid col-12 mx-auto justify-content-center"> <h3 className="text-center m-0">{LocalizeText('help.emergency.chat_report.subtitle')}</h3>
<div className="col-12"><h3 className="fw-bold">{LocalizeText('help.emergency.chat_report.subtitle')}</h3></div> { (userChats.length === 0) && <div className="bg-muted rounded text-center p-2">{LocalizeText('help.cfh.error.no_user_data')}</div> }
{ userChats.length > 0 && { (userChats.length > 0) &&
<div className="text-wrap">{LocalizeText('help.emergency.chat_report.description')}</div>
}
</div>
{
(userChats.length === 0) && <div>{LocalizeText('help.cfh.error.no_user_data')}</div>
}
{ userChats.length > 0 &&
<> <>
<div className="text-wrap">{LocalizeText('help.emergency.chat_report.description')}</div>
<NitroCardGridView columns={1}> <NitroCardGridView columns={1}>
{userChats.map((chat, index) => {userChats.map((chat, index) =>
{ {
@ -76,13 +70,12 @@ export const SelectReportedChatsView: FC<{}> = props =>
) )
})} })}
</NitroCardGridView> </NitroCardGridView>
<div className="d-flex gap-2 justify-content-between mt-auto"> <div className="d-flex gap-2 justify-content-between mt-auto">
<button className="btn btn-secondary mt-2" type="button" onClick={back}>{LocalizeText('generic.back')}</button> <button className="btn btn-primary w-100" type="button" onClick={back}>{LocalizeText('generic.back')}</button>
<button className="btn btn-primary mt-2" type="button" disabled={selectedChats.size <= 0} onClick={submitChats}>{LocalizeText('help.emergency.main.submit.button')}</button> <button className="btn btn-success w-100" type="button" disabled={selectedChats.size <= 0} onClick={submitChats}>{LocalizeText('help.emergency.main.submit.button')}</button>
</div> </div>
</> </>
} }
</> </div>
); );
} }

View File

@ -55,16 +55,11 @@ export const SelectReportedUserView: FC<{}> = props =>
}, [helpReportState, setHelpReportState]); }, [helpReportState, setHelpReportState]);
return ( return (
<> <div className="d-flex flex-column gap-2 h-100">
<div className="d-grid col-12 mx-auto justify-content-center"> <h3 className="text-center m-0">{LocalizeText('help.emergency.main.step.two.title')}</h3>
<h3 className="fw-bold">{LocalizeText('help.emergency.main.step.two.title')}</h3> <div>{(availableUsers.length > 0) ? LocalizeText('report.user.pick.user') : ''}</div>
<p>{(availableUsers.length > 0) ? LocalizeText('report.user.pick.user') : ''}</p> { (availableUsers.length <= 0) && <div className="bg-muted rounded text-center p-2">{LocalizeText('report.user.error.nolist')}</div> }
</div> { (availableUsers.length > 0) &&
{
(availableUsers.length <= 0) && <div>{LocalizeText('report.user.error.nolist')}</div>
}
{
(availableUsers.length > 0) &&
<> <>
<NitroCardGridView columns={1}> <NitroCardGridView columns={1}>
{availableUsers.map((user, index) => {availableUsers.map((user, index) =>
@ -76,13 +71,12 @@ export const SelectReportedUserView: FC<{}> = props =>
) )
})} })}
</NitroCardGridView> </NitroCardGridView>
<div className="d-flex gap-2 justify-content-between mt-auto">
<button className="btn btn-secondary mt-2" type="button" onClick={back}>{LocalizeText('generic.back')}</button>
<button className="btn btn-primary mt-2" type="button" disabled={selectedUserId <= 0} onClick={submitUser}>{LocalizeText('help.emergency.main.submit.button')}</button>
</div>
</> </>
} }
</> <div className="d-flex gap-2 justify-content-between mt-auto">
<button className="btn btn-secondary mt-2 w-100" type="button" onClick={back}>{LocalizeText('generic.back')}</button>
{ (availableUsers.length > 0) && <button className="btn btn-success mt-2 w-100" type="button" disabled={selectedUserId <= 0} onClick={submitUser}>{LocalizeText('help.emergency.main.submit.button')}</button> }
</div>
</div>
) )
} }

View File

@ -28,37 +28,44 @@ export const SelectTopicView: FC<{}> = props =>
const back = useCallback(() => const back = useCallback(() =>
{ {
const reportState = Object.assign({}, helpReportState); if(selectedCategory < 0)
reportState.currentStep = --reportState.currentStep; {
setHelpReportState(reportState); const reportState = Object.assign({}, helpReportState);
}, [helpReportState, setHelpReportState]); reportState.currentStep = --reportState.currentStep;
setHelpReportState(reportState);
}
else
{
setSelectedCategory(-1);
setSelectedTopic(-1);
}
}, [ helpReportState, selectedCategory, setHelpReportState ]);
return ( return (
<> <div className="d-flex flex-column gap-2 h-100">
<div className="d-grid col-12 mx-auto justify-content-center"> <h3 className="text-center m-0">{LocalizeText('help.emergency.chat_report.subtitle')}</h3>
<div className="col-12"><h3 className="fw-bold">{LocalizeText('help.emergency.chat_report.subtitle')}</h3></div> <div className="text-wrap">{LocalizeText('help.cfh.pick.topic')}</div>
<div className="text-wrap">{LocalizeText('help.cfh.pick.topic')}</div> <div className="d-flex flex-column gap-2">
</div>
<div className="d-grid gap-2 col-8 mx-auto">
{(selectedCategory < 0) && {(selectedCategory < 0) &&
cfhCategories.map((category, index) => cfhCategories.map((category, index) =>
{ {
return <button key={index} className="btn btn-danger" type="button" onClick={() => setSelectedCategory(index)}>{LocalizeText(`help.cfh.reason.${category.name}`)}</button> return <button key={index} className="btn btn-danger" onClick={() => setSelectedCategory(index)}>{LocalizeText(`help.cfh.reason.${category.name}`)}</button>
}) })
} }
{(selectedCategory >= 0) && {(selectedCategory >= 0) &&
cfhCategories[selectedCategory].topics.map((topic, index) => cfhCategories[selectedCategory].topics.map((topic, index) =>
{ {
return <button key={index} className="btn btn-danger" type="button" onClick={() => setSelectedTopic(index)}>{LocalizeText('help.cfh.topic.' + topic.id)}</button> return <button key={index} className="btn btn-danger" onClick={() => setSelectedTopic(index)}>{LocalizeText('help.cfh.topic.' + topic.id)}</button>
}) })
} }
</div> </div>
<div className="d-flex gap-2 justify-content-between mt-auto"> <div className="d-flex gap-2 justify-content-between mt-2">
<button className="btn btn-secondary mt-2" type="button" onClick={back}>{LocalizeText('generic.back')}</button> <button className="btn btn-primary w-100" onClick={back}>{LocalizeText('generic.back')}</button>
<button className="btn btn-primary mt-2" type="button" disabled={selectedTopic < 0} onClick={submitTopic}>{LocalizeText('help.emergency.main.submit.button')}</button> {(selectedCategory >= 0) && <button className="btn btn-success w-100" disabled={selectedTopic < 0} onClick={submitTopic}>{LocalizeText('help.emergency.main.submit.button')}</button> }
</div> </div>
</> </div>
); );
} }

View File

@ -11,6 +11,7 @@ import { ChatHistoryView } from '../chat-history/ChatHistoryView';
import { FloorplanEditorView } from '../floorplan-editor/FloorplanEditorView'; import { FloorplanEditorView } from '../floorplan-editor/FloorplanEditorView';
import { FriendsView } from '../friends/FriendsView'; import { FriendsView } from '../friends/FriendsView';
import { GroupsView } from '../groups/GroupsView'; import { GroupsView } from '../groups/GroupsView';
import { GuideToolView } from '../guide-tool/GuideToolView';
import { HelpView } from '../help/HelpView'; import { HelpView } from '../help/HelpView';
import { HotelView } from '../hotel-view/HotelView'; import { HotelView } from '../hotel-view/HotelView';
import { InventoryView } from '../inventory/InventoryView'; import { InventoryView } from '../inventory/InventoryView';
@ -77,6 +78,7 @@ export const MainView: FC<MainViewProps> = props =>
<HelpView /> <HelpView />
<FloorplanEditorView /> <FloorplanEditorView />
<NitropediaView /> <NitropediaView />
<GuideToolView />
</div> </div>
); );
} }

View File

@ -1,7 +1,7 @@
import { Dispose, DropBounce, EaseOut, FigureUpdateEvent, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserInfoDataParser, UserInfoEvent, Wait } from '@nitrots/nitro-renderer'; import { Dispose, DropBounce, EaseOut, FigureUpdateEvent, JumpBy, Motions, NitroToolbarAnimateIconEvent, PerkAllowancesMessageEvent, PerkEnum, Queue, UserInfoDataParser, UserInfoEvent, Wait } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { CreateLinkEvent, GetRoomSession, GetRoomSessionManager, GetSessionDataManager, GetUserProfile, GoToDesktop, OpenMessengerChat } from '../../api'; import { CreateLinkEvent, GetRoomSession, GetRoomSessionManager, GetSessionDataManager, GetUserProfile, GoToDesktop, OpenMessengerChat } from '../../api';
import { AvatarEditorEvent, CatalogEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events'; import { AvatarEditorEvent, CatalogEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, GuideToolEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events/achievements'; import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events/achievements';
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent'; import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent'; import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
@ -26,6 +26,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
const [ userInfo, setUserInfo ] = useState<UserInfoDataParser>(null); const [ userInfo, setUserInfo ] = useState<UserInfoDataParser>(null);
const [ userFigure, setUserFigure ] = useState<string>(null); const [ userFigure, setUserFigure ] = useState<string>(null);
const [ isMeExpanded, setMeExpanded ] = useState(false); const [ isMeExpanded, setMeExpanded ] = useState(false);
const [ useGuideTool, setUseGuideTool ] = useState(false);
const [ chatIconType, setChatIconType ] = useState(CHAT_ICON_HIDDEN); const [ chatIconType, setChatIconType ] = useState(CHAT_ICON_HIDDEN);
const [ unseenInventoryCount, setUnseenInventoryCount ] = useState(0); const [ unseenInventoryCount, setUnseenInventoryCount ] = useState(0);
const [ unseenAchievementCount, setUnseenAchievementCount ] = useState(0); const [ unseenAchievementCount, setUnseenAchievementCount ] = useState(0);
@ -51,6 +52,15 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
CreateMessageHook(FigureUpdateEvent, onUserFigureEvent); CreateMessageHook(FigureUpdateEvent, onUserFigureEvent);
const onPerkAllowancesMessageEvent = useCallback((event: PerkAllowancesMessageEvent) =>
{
const parser = event.getParser();
setUseGuideTool(parser.isAllowed(PerkEnum.USE_GUIDE_TOOL));
}, [ setUseGuideTool ]);
CreateMessageHook(PerkAllowancesMessageEvent, onPerkAllowancesMessageEvent);
const onFriendsMessengerIconEvent = useCallback((event: FriendsMessengerIconEvent) => const onFriendsMessengerIconEvent = useCallback((event: FriendsMessengerIconEvent) =>
{ {
setChatIconType(event.iconType); setChatIconType(event.iconType);
@ -158,6 +168,10 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
dispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS)); dispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS));
setMeExpanded(false); setMeExpanded(false);
return; return;
case ToolbarViewItems.GUIDE_TOOL_ITEM:
dispatchUiEvent(new GuideToolEvent(GuideToolEvent.TOGGLE_GUIDE_TOOL));
setMeExpanded(false);
return;
case ToolbarViewItems.FRIEND_CHAT_ITEM: case ToolbarViewItems.FRIEND_CHAT_ITEM:
OpenMessengerChat(); OpenMessengerChat();
return; return;
@ -175,7 +189,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
return ( return (
<div className="nitro-toolbar-container"> <div className="nitro-toolbar-container">
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isMeExpanded } timeout={ 300 }> <TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isMeExpanded } timeout={ 300 }>
<ToolbarMeView unseenAchievementCount={ unseenAchievementCount } handleToolbarItemClick={ handleToolbarItemClick } /> <ToolbarMeView useGuideTool={ useGuideTool } unseenAchievementCount={ unseenAchievementCount } handleToolbarItemClick={ handleToolbarItemClick } />
</TransitionAnimation> </TransitionAnimation>
<div className="d-flex justify-content-between align-items-center nitro-toolbar py-1 px-3"> <div className="d-flex justify-content-between align-items-center nitro-toolbar py-1 px-3">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">

View File

@ -16,4 +16,5 @@ export class ToolbarViewItems
public static ACHIEVEMENTS_ITEM: string = 'TVI_ACHIEVEMENTS_ITEM'; public static ACHIEVEMENTS_ITEM: string = 'TVI_ACHIEVEMENTS_ITEM';
public static PROFILE_ITEM: string = 'TVI_PROFILE_ITEM'; public static PROFILE_ITEM: string = 'TVI_PROFILE_ITEM';
public static SETTINGS_ITEM: string = 'TVI_SETTINGS_ITEM'; public static SETTINGS_ITEM: string = 'TVI_SETTINGS_ITEM';
public static GUIDE_TOOL_ITEM: string = 'TVI_GUIDE_TOOL_ITEM';
} }

View File

@ -7,7 +7,7 @@ import { ToolbarMeViewProps } from './ToolbarMeView.types';
export const ToolbarMeView: FC<ToolbarMeViewProps> = props => export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
{ {
const { unseenAchievementCount = 0, handleToolbarItemClick = null } = props; const { useGuideTool = false, unseenAchievementCount = 0, handleToolbarItemClick = null } = props;
useEffect(() => useEffect(() =>
{ {
@ -24,9 +24,9 @@ export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
<div className="navigation-item"> <div className="navigation-item">
<i className="icon icon-me-talents"></i> <i className="icon icon-me-talents"></i>
</div> </div>
<div className="navigation-item"> { useGuideTool && <div className="navigation-item" onClick={ () => handleToolbarItemClick(ToolbarViewItems.GUIDE_TOOL_ITEM) }>
<i className="icon icon-me-helper-tool"></i> <i className="icon icon-me-helper-tool"></i>
</div> </div> }
<div className="navigation-item" onClick={ () => handleToolbarItemClick(ToolbarViewItems.ACHIEVEMENTS_ITEM) }> <div className="navigation-item" onClick={ () => handleToolbarItemClick(ToolbarViewItems.ACHIEVEMENTS_ITEM) }>
<i className="icon icon-me-achievements"></i> <i className="icon icon-me-achievements"></i>
{ (unseenAchievementCount > 0) && { (unseenAchievementCount > 0) &&

View File

@ -1,6 +1,7 @@
export interface ToolbarMeViewProps export interface ToolbarMeViewProps
{ {
useGuideTool: boolean;
unseenAchievementCount: number; unseenAchievementCount: number;
handleToolbarItemClick: (item: string) => void; handleToolbarItemClick: (item: string) => void;
} }