mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 14:40:50 +01:00
Merge branch 'dev' into @update/groups
This commit is contained in:
commit
0824cafb86
@ -28,7 +28,7 @@
|
||||
"system.animation.fps": 24,
|
||||
"system.limits.fps": false,
|
||||
"system.dispatcher.log": false,
|
||||
"system.packet.log": false,
|
||||
"system.packet.log": true,
|
||||
"system.pong.manually": true,
|
||||
"system.pong.interval.ms": 20000,
|
||||
"room.color.skip.transition": true,
|
||||
|
@ -5,7 +5,9 @@
|
||||
"thumbnails.url": "https://camera.com/thumbnail/%thumbnail%.png",
|
||||
"url.prefix": "https://website.com",
|
||||
"floorplan.tile.url": "${asset.url}/floorplan-editor/tiles.json",
|
||||
"habbopages.url": "https://website.com/habbopages/",
|
||||
"habbopages.url": "${url.prefix}/",
|
||||
"group.homepage.url": "${url.prefix}/groups/%groupid%/id",
|
||||
"guide.help.alpha.groupid": 0,
|
||||
"chat.viewer.height.percentage": 0.40,
|
||||
"widget.dimmer.colorwheel": false,
|
||||
"avatar.wardrobe.max.slots": 10,
|
||||
|
BIN
src/assets/images/guide-tool/guide_tool_duty_switch.png
Normal file
BIN
src/assets/images/guide-tool/guide_tool_duty_switch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 537 B |
BIN
src/assets/images/guide-tool/guide_tool_info_icon.png
Normal file
BIN
src/assets/images/guide-tool/guide_tool_info_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 B |
@ -1 +1 @@
|
||||
export type ColorVariantType = 'primary' | 'success' | 'danger' | 'secondary' | 'link' | 'black' | 'white' | 'dark' | 'warning' | 'muted';
|
||||
export type ColorVariantType = 'primary' | 'success' | 'danger' | 'secondary' | 'link' | 'black' | 'white' | 'dark' | 'warning' | 'muted' | 'light';
|
||||
|
81
src/components/guide-tool/GuideToolView.scss
Normal file
81
src/components/guide-tool/GuideToolView.scss
Normal file
@ -0,0 +1,81 @@
|
||||
.nitro-guide-tool {
|
||||
width: 250px;
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
|
||||
.message-avatar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
.avatar-image {
|
||||
position: absolute;
|
||||
margin-left: -22px;
|
||||
margin-top: -25px;
|
||||
}
|
||||
}
|
||||
|
||||
.messages-group-left {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-right: 8px solid rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.messages-group-right {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.request-message {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
height: 90px;
|
||||
min-height: 90px;
|
||||
max-height: 90px;
|
||||
border: none;
|
||||
resize: none;
|
||||
outline: none;
|
||||
line-height: 17px;
|
||||
}
|
||||
}
|
343
src/components/guide-tool/GuideToolView.tsx
Normal file
343
src/components/guide-tool/GuideToolView.tsx
Normal file
@ -0,0 +1,343 @@
|
||||
import { GuideOnDutyStatusMessageEvent, GuideSessionAttachedMessageEvent, GuideSessionDetachedMessageEvent, GuideSessionEndedMessageEvent, GuideSessionInvitedToGuideRoomMessageEvent, GuideSessionMessageMessageEvent, GuideSessionOnDutyUpdateMessageComposer, GuideSessionPartnerIsTypingMessageEvent, GuideSessionStartedMessageEvent, ILinkEventTracker, PerkAllowancesMessageEvent, PerkEnum } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { AddEventLinkTracker, GetConfiguration, GetSessionDataManager, LocalizeText, RemoveLinkEventTracker } from '../../api';
|
||||
import { GuideToolEvent, NotificationAlertEvent } from '../../events';
|
||||
import { CreateMessageHook, dispatchUiEvent, SendMessageHook, useUiEvent } from '../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../layout';
|
||||
import { GuideSessionState } from './common/GuideSessionState';
|
||||
import { GuideToolMessage } from './common/GuideToolMessage';
|
||||
import { GuideToolMessageGroup } from './common/GuideToolMessageGroup';
|
||||
import { GuideToolAcceptView } from './views/GuideToolAcceptView';
|
||||
import { GuideToolMenuView } from './views/GuideToolMenuView';
|
||||
import { GuideToolOngoingView } from './views/GuideToolOngoingView';
|
||||
import { GuideToolUserCreateRequestView } from './views/GuideToolUserCreateRequestView';
|
||||
import { GuideToolUserFeedbackView } from './views/GuideToolUserFeedbackView';
|
||||
import { GuideToolUserPendingView } from './views/GuideToolUserPendingView';
|
||||
import { GuideToolUserThanksView } from './views/GuideToolUserThanksView';
|
||||
|
||||
export const GuideToolView: FC<{}> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ headerText, setHeaderText ] = useState<string>(LocalizeText('guide.help.guide.tool.title'));
|
||||
const [ noCloseButton, setNoCloseButton ] = useState<boolean>(false);
|
||||
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 [ userRequest, setUserRequest ] = useState<string>('');
|
||||
|
||||
const [ helpRequestDescription, setHelpRequestDescription ] = useState<string>(null);
|
||||
const [ helpRequestAverageTime, setHelpRequestAverageTime ] = useState<number>(0);
|
||||
|
||||
const [ ongoingUserId, setOngoingUserId ] = useState<number>(0);
|
||||
const [ ongoingUsername, setOngoingUsername ] = useState<string>(null);
|
||||
const [ ongoingFigure, setOngoingFigure ] = useState<string>(null);
|
||||
const [ ongoingIsTyping, setOngoingIsTyping ] = useState<boolean>(false);
|
||||
const [ ongoingMessageGroups, setOngoingMessageGroups ] = useState<GuideToolMessageGroup[]>([]);
|
||||
|
||||
const updateSessionState = useCallback((newState: string, replacement?: string) =>
|
||||
{
|
||||
switch(newState)
|
||||
{
|
||||
case GuideSessionState.GUIDE_TOOL_MENU:
|
||||
setHeaderText(LocalizeText('guide.help.guide.tool.title'));
|
||||
setNoCloseButton(false);
|
||||
break;
|
||||
case GuideSessionState.GUIDE_ACCEPT:
|
||||
setHeaderText(LocalizeText('guide.help.request.guide.accept.title'));
|
||||
setNoCloseButton(true);
|
||||
break;
|
||||
case GuideSessionState.GUIDE_ONGOING:
|
||||
setHeaderText(LocalizeText('guide.help.request.guide.ongoing.title', ['name'], [replacement]));
|
||||
setNoCloseButton(true);
|
||||
break;
|
||||
case GuideSessionState.USER_CREATE:
|
||||
setHeaderText(LocalizeText('guide.help.request.user.create.title'));
|
||||
setNoCloseButton(false);
|
||||
break;
|
||||
case GuideSessionState.USER_PENDING:
|
||||
setHeaderText(LocalizeText('guide.help.request.user.pending.title'));
|
||||
setNoCloseButton(true);
|
||||
break;
|
||||
case GuideSessionState.USER_ONGOING:
|
||||
setHeaderText(LocalizeText('guide.help.request.user.ongoing.title', ['name'], [replacement]));
|
||||
setNoCloseButton(true);
|
||||
break;
|
||||
case GuideSessionState.USER_FEEDBACK:
|
||||
setHeaderText(LocalizeText('guide.help.request.user.feedback.title'));
|
||||
setNoCloseButton(true);
|
||||
break;
|
||||
case GuideSessionState.USER_THANKS:
|
||||
setHeaderText(LocalizeText('guide.help.request.user.thanks.title'));
|
||||
setNoCloseButton(false);
|
||||
break;
|
||||
}
|
||||
|
||||
setSessionState(newState);
|
||||
setIsVisible(true);
|
||||
}, []);
|
||||
|
||||
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:
|
||||
updateSessionState(GuideSessionState.USER_CREATE);
|
||||
return;
|
||||
}
|
||||
}, [ updateSessionState ]);
|
||||
|
||||
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();
|
||||
|
||||
setHelpRequestDescription(parser.helpRequestDescription);
|
||||
setHelpRequestAverageTime(parser.roleSpecificWaitTime);
|
||||
|
||||
if(parser.asGuide && isOnDuty) updateSessionState(GuideSessionState.GUIDE_ACCEPT);
|
||||
|
||||
if(!parser.asGuide) updateSessionState(GuideSessionState.USER_PENDING);
|
||||
|
||||
}, [ isOnDuty, updateSessionState ]);
|
||||
|
||||
CreateMessageHook(GuideSessionAttachedMessageEvent, onGuideSessionAttachedMessageEvent);
|
||||
|
||||
const onGuideSessionStartedMessageEvent = useCallback((event: GuideSessionStartedMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(isOnDuty)
|
||||
{
|
||||
setOngoingUserId(parser.requesterUserId);
|
||||
setOngoingUsername(parser.requesterName);
|
||||
setOngoingFigure(parser.requesterFigure);
|
||||
updateSessionState(GuideSessionState.GUIDE_ONGOING, parser.requesterName);
|
||||
}
|
||||
else
|
||||
{
|
||||
setOngoingUserId(parser.guideUserId);
|
||||
setOngoingUsername(parser.guideName);
|
||||
setOngoingFigure(parser.guideFigure);
|
||||
updateSessionState(GuideSessionState.USER_ONGOING, parser.guideName);
|
||||
}
|
||||
}, [ isOnDuty, updateSessionState ]);
|
||||
|
||||
CreateMessageHook(GuideSessionStartedMessageEvent, onGuideSessionStartedMessageEvent);
|
||||
|
||||
const onGuideSessionPartnerIsTypingMessageEvent = useCallback((event: GuideSessionPartnerIsTypingMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setOngoingIsTyping(parser.isTyping);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(GuideSessionPartnerIsTypingMessageEvent, onGuideSessionPartnerIsTypingMessageEvent);
|
||||
|
||||
const onGuideSessionMessageMessageEvent = useCallback((event: GuideSessionMessageMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const messageGroups = [...ongoingMessageGroups];
|
||||
|
||||
let lastGroup = messageGroups[messageGroups.length - 1];
|
||||
|
||||
if(!lastGroup || lastGroup.userId !== parser.senderId)
|
||||
{
|
||||
lastGroup = new GuideToolMessageGroup(parser.senderId);
|
||||
messageGroups.push(lastGroup);
|
||||
}
|
||||
|
||||
lastGroup.addChat(new GuideToolMessage(parser.chatMessage));
|
||||
setOngoingMessageGroups(messageGroups);
|
||||
}, [ ongoingMessageGroups ]);
|
||||
|
||||
CreateMessageHook(GuideSessionMessageMessageEvent, onGuideSessionMessageMessageEvent);
|
||||
|
||||
const onGuideSessionInvitedToGuideRoomMessageEvent = useCallback((event: GuideSessionInvitedToGuideRoomMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const messageGroups = [...ongoingMessageGroups];
|
||||
|
||||
let lastGroup = messageGroups[messageGroups.length - 1];
|
||||
|
||||
const guideId = (isOnDuty ? GetSessionDataManager().userId : ongoingUserId);
|
||||
|
||||
if(!lastGroup || lastGroup.userId !== guideId)
|
||||
{
|
||||
lastGroup = new GuideToolMessageGroup(guideId);
|
||||
messageGroups.push(lastGroup);
|
||||
}
|
||||
|
||||
lastGroup.addChat(new GuideToolMessage(parser.roomName, parser.roomId));
|
||||
setOngoingMessageGroups(messageGroups);
|
||||
}, [isOnDuty, ongoingMessageGroups, ongoingUserId]);
|
||||
|
||||
CreateMessageHook(GuideSessionInvitedToGuideRoomMessageEvent, onGuideSessionInvitedToGuideRoomMessageEvent);
|
||||
|
||||
const onGuideSessionEndedMessageEvent = useCallback((event: GuideSessionEndedMessageEvent) =>
|
||||
{
|
||||
if(isOnDuty)
|
||||
{
|
||||
setOngoingUserId(0);
|
||||
setOngoingUsername(null);
|
||||
setOngoingFigure(null);
|
||||
setOngoingIsTyping(false);
|
||||
setOngoingMessageGroups([]);
|
||||
updateSessionState(GuideSessionState.GUIDE_TOOL_MENU);
|
||||
}
|
||||
else
|
||||
{
|
||||
updateSessionState(GuideSessionState.USER_FEEDBACK);
|
||||
}
|
||||
}, [ isOnDuty, updateSessionState ]);
|
||||
|
||||
CreateMessageHook(GuideSessionEndedMessageEvent, onGuideSessionEndedMessageEvent);
|
||||
|
||||
const onGuideSessionDetachedMessageEvent = useCallback((event: GuideSessionDetachedMessageEvent) =>
|
||||
{
|
||||
setOngoingUserId(0);
|
||||
setOngoingUsername(null);
|
||||
setOngoingFigure(null);
|
||||
setOngoingIsTyping(false);
|
||||
setOngoingMessageGroups([]);
|
||||
|
||||
if(isOnDuty)
|
||||
{
|
||||
|
||||
updateSessionState(GuideSessionState.GUIDE_TOOL_MENU);
|
||||
}
|
||||
else
|
||||
{
|
||||
updateSessionState(GuideSessionState.USER_THANKS);
|
||||
}
|
||||
}, [ isOnDuty, updateSessionState ]);
|
||||
|
||||
CreateMessageHook(GuideSessionDetachedMessageEvent, onGuideSessionDetachedMessageEvent);
|
||||
|
||||
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);
|
||||
setUserRequest('');
|
||||
setSessionState(GuideSessionState.GUIDE_TOOL_MENU);
|
||||
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>
|
||||
<NitroCardHeaderView headerText={ headerText } onCloseClick={ event => processAction('close') } noCloseButton={ noCloseButton } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{ (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 } helpRequestAverageTime={ helpRequestAverageTime } /> }
|
||||
{ [ GuideSessionState.GUIDE_ONGOING, GuideSessionState.USER_ONGOING ].includes(sessionState) &&
|
||||
<GuideToolOngoingView isGuide={ isOnDuty } userId={ ongoingUserId } userName={ ongoingUsername } userFigure={ ongoingFigure } isTyping={ ongoingIsTyping } messageGroups={ ongoingMessageGroups } /> }
|
||||
{ (sessionState === GuideSessionState.USER_CREATE) &&
|
||||
<GuideToolUserCreateRequestView userRequest={ userRequest } setUserRequest={ setUserRequest } /> }
|
||||
{ (sessionState === GuideSessionState.USER_PENDING) &&
|
||||
<GuideToolUserPendingView helpRequestDescription={ helpRequestDescription } helpRequestAverageTime={ helpRequestAverageTime } /> }
|
||||
{ (sessionState === GuideSessionState.USER_FEEDBACK) &&
|
||||
<GuideToolUserFeedbackView userName={ ongoingUsername } /> }
|
||||
{ (sessionState === GuideSessionState.USER_THANKS) &&
|
||||
<GuideToolUserThanksView /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
21
src/components/guide-tool/common/GuideSessionState.ts
Normal file
21
src/components/guide-tool/common/GuideSessionState.ts
Normal 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';
|
||||
}
|
21
src/components/guide-tool/common/GuideToolMessage.ts
Normal file
21
src/components/guide-tool/common/GuideToolMessage.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export class GuideToolMessage
|
||||
{
|
||||
private _message: string;
|
||||
private _roomId: number;
|
||||
|
||||
constructor(message: string, roomId?: number)
|
||||
{
|
||||
this._message = message;
|
||||
this._roomId = roomId;
|
||||
}
|
||||
|
||||
public get message(): string
|
||||
{
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public get roomId(): number
|
||||
{
|
||||
return this._roomId;
|
||||
}
|
||||
}
|
28
src/components/guide-tool/common/GuideToolMessageGroup.ts
Normal file
28
src/components/guide-tool/common/GuideToolMessageGroup.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { GuideToolMessage } from './GuideToolMessage';
|
||||
|
||||
export class GuideToolMessageGroup
|
||||
{
|
||||
private _userId: number;
|
||||
private _messages: GuideToolMessage[];
|
||||
|
||||
constructor(userId: number)
|
||||
{
|
||||
this._userId = userId;
|
||||
this._messages = [];
|
||||
}
|
||||
|
||||
public addChat(message: GuideToolMessage): void
|
||||
{
|
||||
this._messages.push(message);
|
||||
}
|
||||
|
||||
public get userId(): number
|
||||
{
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
public get messages(): GuideToolMessage[]
|
||||
{
|
||||
return this._messages;
|
||||
}
|
||||
}
|
36
src/components/guide-tool/views/GuideToolAcceptView.tsx
Normal file
36
src/components/guide-tool/views/GuideToolAcceptView.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { GuideSessionGuideDecidesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Button, Column, Text } from '../../../common';
|
||||
import { SendMessageHook } from '../../../hooks';
|
||||
|
||||
interface GuideToolAcceptViewProps
|
||||
{
|
||||
helpRequestDescription: string;
|
||||
helpRequestAverageTime: number;
|
||||
}
|
||||
|
||||
export const GuideToolAcceptView: FC<GuideToolAcceptViewProps> = props =>
|
||||
{
|
||||
const { helpRequestDescription = null, helpRequestAverageTime = 0 } = props;
|
||||
|
||||
const answerRequest = (response: boolean) => SendMessageHook(new GuideSessionGuideDecidesMessageComposer(response));
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Column gap={ 0 } className="bg-muted p-2 rounded">
|
||||
<Text bold>{ LocalizeText('guide.help.request.guide.accept.request.title') }</Text>
|
||||
<Text variant="muted">{ LocalizeText('guide.help.request.type.1') }</Text>
|
||||
<Text wrap textBreak>{ helpRequestDescription }</Text>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Button variant="success" onClick={ event => answerRequest(true) }>
|
||||
{ LocalizeText('guide.help.request.guide.accept.accept.button') }
|
||||
</Button>
|
||||
<Button variant="danger" onClick={ event => answerRequest(false) }>
|
||||
{ LocalizeText('guide.help.request.guide.accept.skip.link') }
|
||||
</Button>
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
};
|
76
src/components/guide-tool/views/GuideToolMenuView.tsx
Normal file
76
src/components/guide-tool/views/GuideToolMenuView.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Base, Button, Column, Flex, Text } from '../../../common';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (
|
||||
<Column>
|
||||
<Flex alignItems="center" gap={ 2 } className="bg-muted p-2 rounded">
|
||||
<Base className={ 'duty-switch' + (isOnDuty ? '' : ' off') } onClick={ event => processAction('toggle_duty') } />
|
||||
<Column gap={ 0 }>
|
||||
<Text bold>{ LocalizeText('guide.help.guide.tool.yourstatus') }</Text>
|
||||
<Text>{ LocalizeText(`guide.help.guide.tool.duty.${ (isOnDuty ? 'on' : 'off') }`) }</Text>
|
||||
</Column>
|
||||
</Flex>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{ LocalizeText('guide.help.guide.tool.tickettypeselection.caption') }</Text>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" disabled={ isOnDuty } type="checkbox" checked={ isHandlingGuideRequests } onChange={ event => setIsHandlingGuideRequests(event.target.checked) } />
|
||||
<Text>{ LocalizeText('guide.help.guide.tool.tickettypeselection.guiderequests') }</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" disabled={ isOnDuty } type="checkbox" checked={ isHandlingHelpRequests } onChange={ event => setIsHandlingHelpRequests(event.target.checked) } />
|
||||
<Text>{ LocalizeText('guide.help.guide.tool.tickettypeselection.onlyhelprequests') }</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" disabled={ isOnDuty } type="checkbox" checked={ isHandlingBullyReports } onChange={ event => setIsHandlingBullyReports(event.target.checked) } />
|
||||
<Text>{ LocalizeText('guide.help.guide.tool.tickettypeselection.bullyreports') }</Text>
|
||||
</Flex>
|
||||
</Column>
|
||||
<hr className="bg-dark m-0" />
|
||||
<Flex center gap={ 2 }>
|
||||
<Base className="info-icon" />
|
||||
<Column gap={ 1 }>
|
||||
<Base dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.guidesonduty', [ 'amount' ], [ guidesOnDuty.toString() ]) } } />
|
||||
<Base dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.helpersonduty', [ 'amount' ], [ helpersOnDuty.toString() ]) } } />
|
||||
<Base dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.guardiansonduty', [ 'amount' ], [ guardiansOnDuty.toString() ]) } } />
|
||||
</Column>
|
||||
</Flex>
|
||||
<hr className="bg-dark m-0" />
|
||||
<Flex justifyContent="between" gap={ 2 }>
|
||||
<Button onClick={ event => processAction('forum_link') }>{ LocalizeText('guide.help.guide.tool.forum.link') }</Button>
|
||||
<Button disabled>{ LocalizeText('guide.help.guide.tool.skill.link') }</Button>
|
||||
</Flex>
|
||||
</Column>
|
||||
);
|
||||
}
|
127
src/components/guide-tool/views/GuideToolOngoingView.tsx
Normal file
127
src/components/guide-tool/views/GuideToolOngoingView.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
import { GuideSessionGetRequesterRoomMessageComposer, GuideSessionInviteRequesterMessageComposer, GuideSessionRequesterRoomMessageEvent, GuideSessionResolvedMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { GuideSessionMessageMessageComposer } from '@nitrots/nitro-renderer/src';
|
||||
import { FC, KeyboardEvent, useCallback, useState } from 'react';
|
||||
import { GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../api';
|
||||
import { Base, Button, ButtonGroup, Column, Flex, Text } from '../../../common';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../hooks';
|
||||
import { NitroLayoutBase } from '../../../layout/base';
|
||||
import { AvatarImageView } from '../../../views/shared/avatar-image/AvatarImageView';
|
||||
import { GuideToolMessageGroup } from '../common/GuideToolMessageGroup';
|
||||
|
||||
interface GuideToolOngoingViewProps
|
||||
{
|
||||
isGuide: boolean;
|
||||
userId: number;
|
||||
userName: string;
|
||||
userFigure: string;
|
||||
isTyping: boolean;
|
||||
messageGroups: GuideToolMessageGroup[];
|
||||
}
|
||||
|
||||
export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
{
|
||||
const { isGuide = false, userId = 0, userName = null, userFigure = null, isTyping = false, messageGroups = [] } = props;
|
||||
|
||||
const [ messageText, setMessageText ] = useState<string>('');
|
||||
|
||||
const visit = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new GuideSessionGetRequesterRoomMessageComposer());
|
||||
}, []);
|
||||
|
||||
const invite = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new GuideSessionInviteRequesterMessageComposer());
|
||||
}, []);
|
||||
|
||||
const resolve = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new GuideSessionResolvedMessageComposer());
|
||||
}, []);
|
||||
|
||||
const onGuideSessionRequesterRoomMessageEvent = useCallback((event: GuideSessionRequesterRoomMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
TryVisitRoom(parser.requesterRoomId);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(GuideSessionRequesterRoomMessageEvent, onGuideSessionRequesterRoomMessageEvent);
|
||||
|
||||
const sendMessage = useCallback(() =>
|
||||
{
|
||||
if(!messageText || !messageText.length) return;
|
||||
|
||||
SendMessageHook(new GuideSessionMessageMessageComposer(messageText));
|
||||
setMessageText('');
|
||||
}, [ messageText ]);
|
||||
|
||||
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if(event.key !== 'Enter') return;
|
||||
|
||||
sendMessage();
|
||||
}, [ sendMessage ]);
|
||||
|
||||
const isOwnChat = useCallback((userId: number) =>
|
||||
{
|
||||
return userId === GetSessionDataManager().userId;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Column fullHeight>
|
||||
<Flex alignItems="center" justifyContent="between" gap={ 1 } className="bg-muted p-2 rounded">
|
||||
{ isGuide &&
|
||||
<ButtonGroup>
|
||||
<Button onClick={ visit }>{ LocalizeText('guide.help.request.guide.ongoing.visit.button') }</Button>
|
||||
<Button disabled onClick={ invite }>{ LocalizeText('guide.help.request.guide.ongoing.invite.button') }</Button>
|
||||
</ButtonGroup> }
|
||||
{ !isGuide &&
|
||||
<Column gap={ 0 }>
|
||||
<Text bold>{ userName }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.ongoing.guide.desc') }</Text>
|
||||
</Column> }
|
||||
<Button variant="danger" disabled>{ LocalizeText('guide.help.common.report.link') }</Button>
|
||||
</Flex>
|
||||
<Column fullHeight overflow="hidden" gap={ 1 } className="bg-muted rounded chat-messages p-2">
|
||||
<Column overflow="auto">
|
||||
{ messageGroups.map((group, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex fullWidth justifyContent={ isOwnChat(group.userId) ? 'end' : 'start' } gap={ 2 }>
|
||||
<Base shrink className="message-avatar">
|
||||
{ (!isOwnChat(group.userId)) &&
|
||||
<AvatarImageView figure={ userFigure } direction={ 2 } /> }
|
||||
</Base>
|
||||
<Base className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (isOwnChat(group.userId) ? 'right' : 'left') }>
|
||||
<Text bold>
|
||||
{ (isOwnChat(group.userId)) && GetSessionDataManager().userName }
|
||||
{ (!isOwnChat(group.userId)) && userName }
|
||||
</Text>
|
||||
{ group.messages.map((chat, index) => <Base key={ index } className="text-break">{ chat.message }</Base>) }
|
||||
</Base>
|
||||
{ (isOwnChat(group.userId)) &&
|
||||
<NitroLayoutBase className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ GetSessionDataManager().figure } direction={ 4 } />
|
||||
</NitroLayoutBase> }
|
||||
</Flex>
|
||||
);
|
||||
}) }
|
||||
</Column>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Flex gap={ 1 }>
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('guide.help.request.guide.ongoing.input.empty', [ 'name' ], [ userName ]) } value={ messageText } onChange={ event => setMessageText(event.target.value) } onKeyDown={ onKeyDown } />
|
||||
<Button variant="success" onClick={ sendMessage }>
|
||||
{ LocalizeText('widgets.chatinput.say') }
|
||||
</Button>
|
||||
</Flex>
|
||||
{ isTyping &&
|
||||
<Text variant="muted">{ LocalizeText('guide.help.common.typing') }</Text> }
|
||||
</Column>
|
||||
<Button fullWidth variant="success" onClick={ resolve }>
|
||||
{ LocalizeText('guide.help.request.' + (isGuide ? 'guide' : 'user') + '.ongoing.close.link') }
|
||||
</Button>
|
||||
</Column>
|
||||
);
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
import { GuideSessionCreateMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useState } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Button, Column, Text } from '../../../common';
|
||||
import { SendMessageHook } from '../../../hooks';
|
||||
|
||||
interface GuideToolUserCreateRequestViewProps
|
||||
{
|
||||
userRequest: string;
|
||||
setUserRequest: (value: string) => void;
|
||||
}
|
||||
|
||||
const MIN_REQUEST_LENGTH: number = 15;
|
||||
|
||||
export const GuideToolUserCreateRequestView: FC<GuideToolUserCreateRequestViewProps> = props =>
|
||||
{
|
||||
const { userRequest = '', setUserRequest = null } = props;
|
||||
const [ isPending, setIsPending ] = useState<boolean>(false);
|
||||
|
||||
const sendRequest = () =>
|
||||
{
|
||||
setIsPending(true);
|
||||
SendMessageHook(new GuideSessionCreateMessageComposer(1, userRequest));
|
||||
}
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Text>{ LocalizeText('guide.help.request.user.create.help') }</Text>
|
||||
<textarea className="request-message" maxLength={ 140 } value={ userRequest } onChange={ event => setUserRequest(event.target.value) } placeholder={ LocalizeText('guide.help.request.user.create.input.help') }></textarea>
|
||||
<Button fullWidth variant="success" disabled={ (userRequest.length < MIN_REQUEST_LENGTH) || isPending } onClick={ sendRequest }>{ LocalizeText('guide.help.request.user.create.input.button') }</Button>
|
||||
</Column>
|
||||
);
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
import { GuideSessionFeedbackMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Button, Column, Flex, Text } from '../../../common';
|
||||
import { SendMessageHook } from '../../../hooks';
|
||||
|
||||
interface GuideToolUserFeedbackViewProps
|
||||
{
|
||||
userName: string;
|
||||
}
|
||||
|
||||
export const GuideToolUserFeedbackView: FC<GuideToolUserFeedbackViewProps> = props =>
|
||||
{
|
||||
const { userName = null } = props;
|
||||
|
||||
const giveFeedback = useCallback((recommend: boolean) =>
|
||||
{
|
||||
SendMessageHook(new GuideSessionFeedbackMessageComposer(recommend));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Flex justifyContent="between" gap={ 1 } className="bg-muted p-2 rounded">
|
||||
<Column gap={ 0 }>
|
||||
<Text bold>{ userName }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.feedback.guide.desc') }</Text>
|
||||
</Column>
|
||||
<Button variant="danger" disabled>{ LocalizeText('guide.help.common.report.link') }</Button>
|
||||
</Flex>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{ LocalizeText('guide.help.request.user.feedback.closed.title') }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.feedback.closed.desc') }</Text>
|
||||
</Column>
|
||||
<hr className="bg-dark m-0 mt-auto" />
|
||||
<Column>
|
||||
<Text center bold>{ LocalizeText('guide.help.request.user.feedback.question') }</Text>
|
||||
<Flex gap={ 1 }>
|
||||
<Button fullWidth variant="success" onClick={ event => giveFeedback(true) }>{ LocalizeText('guide.help.request.user.feedback.positive.button') }</Button>
|
||||
<Button fullWidth variant="danger" onClick={ event => giveFeedback(false) }>{ LocalizeText('guide.help.request.user.feedback.negative.button') }</Button>
|
||||
</Flex>
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
};
|
34
src/components/guide-tool/views/GuideToolUserPendingView.tsx
Normal file
34
src/components/guide-tool/views/GuideToolUserPendingView.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { GuideSessionRequesterCancelsMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Button, Column, Text } from '../../../common';
|
||||
import { SendMessageHook } from '../../../hooks';
|
||||
|
||||
interface GuideToolUserPendingViewProps
|
||||
{
|
||||
helpRequestDescription: string;
|
||||
helpRequestAverageTime: number;
|
||||
}
|
||||
|
||||
export const GuideToolUserPendingView: FC<GuideToolUserPendingViewProps> = props =>
|
||||
{
|
||||
const { helpRequestDescription = null, helpRequestAverageTime = 0 } = props;
|
||||
|
||||
const cancelRequest = () => SendMessageHook(new GuideSessionRequesterCancelsMessageComposer());
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Column gap={ 0 } className="bg-muted rounded p-2">
|
||||
<Text bold>{ LocalizeText('guide.help.request.guide.accept.request.title') }</Text>
|
||||
<Text variant="muted">{ LocalizeText('guide.help.request.type.1') }</Text>
|
||||
<Text wrap textBreak>{ helpRequestDescription }</Text>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{ LocalizeText('guide.help.request.user.pending.info.title') }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.pending.info.message') }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.pending.info.waiting', [ 'waitingtime' ], [ helpRequestAverageTime.toString() ]) }</Text>
|
||||
</Column>
|
||||
<Button variant="danger" onClick={ cancelRequest }>{ LocalizeText('guide.help.request.user.pending.cancel.button') }</Button>
|
||||
</Column>
|
||||
);
|
||||
};
|
13
src/components/guide-tool/views/GuideToolUserThanksView.tsx
Normal file
13
src/components/guide-tool/views/GuideToolUserThanksView.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Column, Text } from '../../../common';
|
||||
|
||||
export const GuideToolUserThanksView: FC<{}> = props =>
|
||||
{
|
||||
return (
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{ LocalizeText('guide.help.request.user.thanks.info.title') }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.thanks.info.desc') }</Text>
|
||||
</Column>
|
||||
);
|
||||
};
|
@ -4,7 +4,8 @@ import { LocalizeText } from '../../../api';
|
||||
import { Button } from '../../../common/Button';
|
||||
import { Column } from '../../../common/Column';
|
||||
import { Text } from '../../../common/Text';
|
||||
import { SendMessageHook } from '../../../hooks';
|
||||
import { GuideToolEvent } from '../../../events/guide-tool/GuideToolEvent';
|
||||
import { dispatchUiEvent, SendMessageHook } from '../../../hooks';
|
||||
import { useHelpContext } from '../HelpContext';
|
||||
|
||||
export const HelpIndexView: FC<{}> = props =>
|
||||
@ -23,6 +24,11 @@ export const HelpIndexView: FC<{}> = props =>
|
||||
SendMessageHook(new GetCfhStatusMessageComposer(false));
|
||||
}, []);
|
||||
|
||||
const onNewHelpRequestClick = useCallback(() =>
|
||||
{
|
||||
dispatchUiEvent(new GuideToolEvent(GuideToolEvent.CREATE_HELP_REQUEST));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Column gap={ 1 }>
|
||||
@ -31,7 +37,7 @@ export const HelpIndexView: FC<{}> = props =>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Button onClick={ onReportClick }>{ LocalizeText('help.main.bully.subtitle') }</Button>
|
||||
<Button disabled={ true }>{ LocalizeText('help.main.help.title') }</Button>
|
||||
<Button onClick={ onNewHelpRequestClick }>{ LocalizeText('help.main.help.title') }</Button>
|
||||
<Button disabled={ true }>{ LocalizeText('help.main.self.tips.title') }</Button>
|
||||
<Button variant="link" className="text-black" onClick={ onRequestMySanctionStatusClick }>{ LocalizeText('help.main.my.sanction.status') }</Button>
|
||||
</Column>
|
||||
|
@ -4,6 +4,7 @@
|
||||
@import './catalog/CatalogView';
|
||||
@import './chat-history/ChatHistoryView';
|
||||
@import './groups/GroupView';
|
||||
@import './guide-tool/GuideToolView';
|
||||
@import './help/HelpView';
|
||||
@import './inventory/InventoryView';
|
||||
@import './loading/LoadingView';
|
||||
|
@ -16,6 +16,7 @@ import { CameraWidgetView } from '../camera/CameraWidgetView';
|
||||
import { CatalogView } from '../catalog/CatalogView';
|
||||
import { ChatHistoryView } from '../chat-history/ChatHistoryView';
|
||||
import { GroupsView } from '../groups/GroupsView';
|
||||
import { GuideToolView } from '../guide-tool/GuideToolView';
|
||||
import { HelpView } from '../help/HelpView';
|
||||
import { InventoryView } from '../inventory/InventoryView';
|
||||
import { ModToolsView } from '../mod-tools/ModToolsView';
|
||||
@ -116,6 +117,7 @@ export const MainView: FC<{}> = props =>
|
||||
<HelpView />
|
||||
<FloorplanEditorView />
|
||||
<NitropediaView />
|
||||
<GuideToolView />
|
||||
<HcCenterView />
|
||||
<CampaignView />
|
||||
</Base>
|
||||
|
@ -65,57 +65,59 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<Column overflow="hidden">
|
||||
<Grid fullHeight={ false }>
|
||||
<Column size={ 6 } gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.createroom.roomnameinfo') }</Text>
|
||||
<input type="text" className="form-control form-control-sm" onChange={ event => setName(event.target.value) } />
|
||||
<Grid overflow="hidden">
|
||||
<Column size={ 6 } gap={ 1 } overflow="auto">
|
||||
<Column gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.createroom.roomnameinfo') }</Text>
|
||||
<input type="text" className="form-control form-control-sm" onChange={ event => setName(event.target.value) } />
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.category') }</Text>
|
||||
<select className="form-select form-select-sm" onChange={ event => setCategory(Number(event.target.value)) }>
|
||||
{ categories && categories.map(category =>
|
||||
{
|
||||
return <option key={ category.id } value={ category.id }>{ LocalizeText(category.name) }</option>
|
||||
}) }
|
||||
</select>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.maxvisitors') }</Text>
|
||||
<select className="form-select form-select-sm" onChange={ event => setVisitorsCount(Number(event.target.value)) }>
|
||||
{ maxVisitorsList && maxVisitorsList.map(value =>
|
||||
{
|
||||
return <option key={ value } value={ value }>{ value }</option>
|
||||
}) }
|
||||
</select>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.tradesettings') }</Text>
|
||||
<select className="form-select form-select-sm" onChange={ event => setTradesSetting(Number(event.target.value)) }>
|
||||
<option value="0">{ LocalizeText('navigator.roomsettings.trade_not_allowed') }</option>
|
||||
<option value="1">{ LocalizeText('navigator.roomsettings.trade_not_with_Controller') }</option>
|
||||
<option value="2">{ LocalizeText('navigator.roomsettings.trade_allowed') }</option>
|
||||
</select>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.createroom.roomdescinfo') }</Text>
|
||||
<input type="text" className="form-control form-control-sm" onChange={ event => setDescription(event.target.value) } />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column size={ 6 } gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.category') }</Text>
|
||||
<select className="form-select form-select-sm" onChange={ event => setCategory(Number(event.target.value)) }>
|
||||
{ categories && categories.map(category =>
|
||||
<Column size={ 6 } gap={ 1 } overflow="auto">
|
||||
{
|
||||
RoomModels.map(model =>
|
||||
{
|
||||
return <option key={ category.id } value={ category.id }>{ LocalizeText(category.name) }</option>
|
||||
}) }
|
||||
</select>
|
||||
</Column>
|
||||
<Column size={ 6 } gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.maxvisitors') }</Text>
|
||||
<select className="form-select form-select-sm" onChange={ event => setVisitorsCount(Number(event.target.value)) }>
|
||||
{ maxVisitorsList && maxVisitorsList.map(value =>
|
||||
{
|
||||
return <option key={ value } value={ value }>{ value }</option>
|
||||
}) }
|
||||
</select>
|
||||
</Column>
|
||||
<Column size={ 6 } gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.tradesettings') }</Text>
|
||||
<select className="form-select form-select-sm" onChange={ event => setTradesSetting(Number(event.target.value)) }>
|
||||
<option value="0">{ LocalizeText('navigator.roomsettings.trade_not_allowed') }</option>
|
||||
<option value="1">{ LocalizeText('navigator.roomsettings.trade_not_with_Controller') }</option>
|
||||
<option value="2">{ LocalizeText('navigator.roomsettings.trade_allowed') }</option>
|
||||
</select>
|
||||
return (<LayoutGridItem fullHeight key={ model.name } onClick={ () => selectModel(model) } itemActive={ (selectedModelName === model.name) } overflow="unset" gap={ 0 } className="p-1" disabled={ (GetSessionDataManager().clubLevel < model.clubLevel) }>
|
||||
<Flex fullHeight center overflow="hidden">
|
||||
<img alt="" src={ getRoomModelImage(model.name) } />
|
||||
</Flex>
|
||||
<Text bold>{ model.tileSize } { LocalizeText('navigator.createroom.tilesize') }</Text>
|
||||
{ model.clubLevel > HabboClubLevelEnum.NO_CLUB && <CurrencyIcon position="absolute" className="top-1 end-1" type="hc" /> }
|
||||
</LayoutGridItem>);
|
||||
})
|
||||
}
|
||||
</Column>
|
||||
</Grid>
|
||||
<Column gap={ 1 }>
|
||||
<Text>{ LocalizeText('navigator.createroom.roomdescinfo') }</Text>
|
||||
<input type="text" className="form-control form-control-sm" onChange={ event => setDescription(event.target.value) } />
|
||||
</Column>
|
||||
<Flex overflow="auto" gap={ 1 }>
|
||||
{
|
||||
RoomModels.map(model =>
|
||||
{
|
||||
return (<LayoutGridItem fullHeight key={ model.name } onClick={ () => selectModel(model) } itemActive={ (selectedModelName === model.name) } overflow="unset" gap={ 0 } className="p-1" disabled={ (GetSessionDataManager().clubLevel < model.clubLevel) }>
|
||||
<Flex fullHeight center overflow="hidden">
|
||||
<img alt="" src={ getRoomModelImage(model.name) } />
|
||||
</Flex>
|
||||
<Text bold>{ model.tileSize } { LocalizeText('navigator.createroom.tilesize') }</Text>
|
||||
{ model.clubLevel > HabboClubLevelEnum.NO_CLUB && <CurrencyIcon position="absolute" className="top-1 end-1" type="hc" /> }
|
||||
</LayoutGridItem>);
|
||||
})
|
||||
}
|
||||
</Flex>
|
||||
<Button variant="success" onClick={ createRoom } disabled={ (!name || (name.length < 3)) }>{ LocalizeText('navigator.createroom.create') }</Button>
|
||||
<Button fullWidth variant="success" onClick={ createRoom } disabled={ (!name || (name.length < 3)) }>{ LocalizeText('navigator.createroom.create') }</Button>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@ -7,13 +7,14 @@ import { ToolbarViewItems } from './common/ToolbarViewItems';
|
||||
|
||||
export interface ToolbarMeViewProps
|
||||
{
|
||||
useGuideTool: boolean;
|
||||
unseenAchievementCount: number;
|
||||
handleToolbarItemClick: (item: string) => void;
|
||||
}
|
||||
|
||||
export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
|
||||
{
|
||||
const { unseenAchievementCount = 0, handleToolbarItemClick = null } = props;
|
||||
const { useGuideTool = false, unseenAchievementCount = 0, handleToolbarItemClick = null } = props;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -26,6 +27,8 @@ export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
|
||||
|
||||
return (
|
||||
<Flex alignItems="center" className="nitro-toolbar-me p-2" gap={ 2 }>
|
||||
{ useGuideTool &&
|
||||
<Base pointer className="navigation-item icon icon-me-helper-tool" onClick={ () => handleToolbarItemClick(ToolbarViewItems.GUIDE_TOOL_ITEM) } /> }
|
||||
<Base pointer className="navigation-item icon icon-me-achievements" onClick={ () => handleToolbarItemClick(ToolbarViewItems.ACHIEVEMENTS_ITEM) }>
|
||||
{ (unseenAchievementCount > 0) &&
|
||||
<ItemCountView count={ unseenAchievementCount } /> }
|
||||
|
@ -1,8 +1,8 @@
|
||||
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 { CreateLinkEvent, GetRoomSession, GetRoomSessionManager, GetSessionDataManager, GetUserProfile, GoToDesktop, OpenMessengerChat } from '../../api';
|
||||
import { Base, Flex } from '../../common';
|
||||
import { AvatarEditorEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
||||
import { AvatarEditorEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, GuideToolEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
||||
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events/achievements';
|
||||
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
|
||||
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
|
||||
@ -32,6 +32,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
const [ userInfo, setUserInfo ] = useState<UserInfoDataParser>(null);
|
||||
const [ userFigure, setUserFigure ] = useState<string>(null);
|
||||
const [ isMeExpanded, setMeExpanded ] = useState(false);
|
||||
const [ useGuideTool, setUseGuideTool ] = useState(false);
|
||||
const [ chatIconType, setChatIconType ] = useState(CHAT_ICON_HIDDEN);
|
||||
const [ unseenInventoryCount, setUnseenInventoryCount ] = useState(0);
|
||||
const [ unseenAchievementCount, setUnseenAchievementCount ] = useState(0);
|
||||
@ -60,6 +61,15 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
|
||||
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) =>
|
||||
{
|
||||
setChatIconType(event.iconType);
|
||||
@ -167,6 +177,10 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
dispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS));
|
||||
setMeExpanded(false);
|
||||
return;
|
||||
case ToolbarViewItems.GUIDE_TOOL_ITEM:
|
||||
dispatchUiEvent(new GuideToolEvent(GuideToolEvent.TOGGLE_GUIDE_TOOL));
|
||||
setMeExpanded(false);
|
||||
return;
|
||||
case ToolbarViewItems.FRIEND_CHAT_ITEM:
|
||||
OpenMessengerChat();
|
||||
return;
|
||||
@ -184,7 +198,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
return (
|
||||
<>
|
||||
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isMeExpanded } timeout={ 300 }>
|
||||
<ToolbarMeView unseenAchievementCount={ unseenAchievementCount } handleToolbarItemClick={ handleToolbarItemClick } />
|
||||
<ToolbarMeView useGuideTool={ useGuideTool } unseenAchievementCount={ unseenAchievementCount } handleToolbarItemClick={ handleToolbarItemClick } />
|
||||
</TransitionAnimation>
|
||||
<Flex alignItems="center" justifyContent="between" gap={ 2 } className="nitro-toolbar py-1 px-3">
|
||||
<Flex gap={ 2 } alignItems="center">
|
||||
|
@ -11,4 +11,5 @@ export class ToolbarViewItems
|
||||
public static ACHIEVEMENTS_ITEM: string = 'TVI_ACHIEVEMENTS_ITEM';
|
||||
public static PROFILE_ITEM: string = 'TVI_PROFILE_ITEM';
|
||||
public static SETTINGS_ITEM: string = 'TVI_SETTINGS_ITEM';
|
||||
public static GUIDE_TOOL_ITEM: string = 'TVI_GUIDE_TOOL_ITEM';
|
||||
}
|
||||
|
10
src/events/guide-tool/GuideToolEvent.ts
Normal file
10
src/events/guide-tool/GuideToolEvent.ts
Normal 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';
|
||||
}
|
1
src/events/guide-tool/index.ts
Normal file
1
src/events/guide-tool/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './GuideToolEvent';
|
@ -2,6 +2,7 @@ export * from './avatar-editor';
|
||||
export * from './camera';
|
||||
export * from './catalog';
|
||||
export * from './friends';
|
||||
export * from './guide-tool';
|
||||
export * from './inventory';
|
||||
export * from './navigator';
|
||||
export * from './notification-center';
|
||||
|
@ -5,6 +5,7 @@ import ReactDOM from 'react-dom';
|
||||
import { App } from './App';
|
||||
import './index.scss';
|
||||
|
||||
//@ts-ignore
|
||||
library.add(fas);
|
||||
|
||||
ReactDOM.render(
|
||||
|
@ -5,7 +5,7 @@ import { NitroCardHeaderViewProps } from './NitroCardHeaderView.types';
|
||||
|
||||
export const NitroCardHeaderView: FC<NitroCardHeaderViewProps> = props =>
|
||||
{
|
||||
const { headerText = null, onCloseClick = null } = props;
|
||||
const { headerText = null, noCloseButton = false, onCloseClick = null } = props;
|
||||
const { theme = 'primary', simple = false } = useNitroCardContext();
|
||||
|
||||
const onMouseDown = useCallback((event: MouseEvent<HTMLDivElement>) =>
|
||||
|
@ -4,5 +4,6 @@ export interface NitroCardHeaderViewProps
|
||||
{
|
||||
headerText: string;
|
||||
theme?: string;
|
||||
noCloseButton?: boolean;
|
||||
onCloseClick: (event: MouseEvent) => void;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
|
||||
|
||||
const onNotificationAlertEvent = useCallback((event: NotificationAlertEvent) =>
|
||||
{
|
||||
console.log(event);
|
||||
const alertItem = new NotificationAlertItem(event.messages, event.alertType, event.clickUrl, event.clickUrlText, event.title, event.imageUrl);
|
||||
|
||||
setAlerts(prevValue => [ alertItem, ...prevValue ]);
|
||||
@ -30,7 +29,6 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
|
||||
|
||||
const onNotificationBubbleEvent = useCallback((event: NotificationBubbleEvent) =>
|
||||
{
|
||||
console.log(event);
|
||||
const notificationItem = new NotificationBubbleItem(event.message, event.notificationType, event.imageUrl, event.linkUrl);
|
||||
|
||||
setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]);
|
||||
|
@ -80,6 +80,7 @@
|
||||
}
|
||||
|
||||
.fas,
|
||||
.svg-inline--fa,
|
||||
.nitro-currency-icon {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
|
Loading…
Reference in New Issue
Block a user