Merge pull request #41 from billsonnn/feature/calendar
Feature/calendar
@ -5,7 +5,7 @@
|
|||||||
"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": "https://swf.nitrots.co/gamedata/habbopages/",
|
||||||
"chat.viewer.height.percentage": 0.40,
|
"chat.viewer.height.percentage": 0.40,
|
||||||
"widget.dimmer.colorwheel": false,
|
"widget.dimmer.colorwheel": false,
|
||||||
"hotelview": {
|
"hotelview": {
|
||||||
|
BIN
src/assets/images/campaign/available.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/campaign/campaign_day_generic_bg.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/images/campaign/campaign_opened.png
Normal file
After Width: | Height: | Size: 744 B |
BIN
src/assets/images/campaign/locked.png
Normal file
After Width: | Height: | Size: 220 B |
BIN
src/assets/images/campaign/locked_bg.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
src/assets/images/campaign/next.png
Normal file
After Width: | Height: | Size: 244 B |
BIN
src/assets/images/campaign/prev.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
src/assets/images/campaign/unavailable.png
Normal file
After Width: | Height: | Size: 448 B |
BIN
src/assets/images/campaign/unlocked_bg.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
@ -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 './campaign/CampaignView';
|
||||||
|
1
src/views/campaign/CampaignView.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
@import './views/calendar/CalendarView';
|
113
src/views/campaign/CampaignView.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { CampaignCalendarData, CampaignCalendarDataMessageEvent, CampaignCalendarDoorOpenedMessageEvent, OpenCampaignCalendarDoorAsStaffComposer, OpenCampaignCalendarDoorComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api';
|
||||||
|
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../hooks';
|
||||||
|
import { CalendarView } from './views/calendar/CalendarView';
|
||||||
|
|
||||||
|
export const CampaignView: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const [ calendarData, setCalendarData ] = useState<CampaignCalendarData>(null);
|
||||||
|
const [ lastOpenAttempt, setLastOpenAttempt ] = useState<number>(-1);
|
||||||
|
const [ receivedProducts, setReceivedProducts ] = useState<Map<number, string>>(new Map());
|
||||||
|
const [ isCalendarOpen, setCalendarOpen ] = useState(false);
|
||||||
|
|
||||||
|
const onCampaignCalendarDataMessageEvent = useCallback((event: CampaignCalendarDataMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(!parser) return;
|
||||||
|
setCalendarData(parser.calendarData);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(CampaignCalendarDataMessageEvent, onCampaignCalendarDataMessageEvent);
|
||||||
|
|
||||||
|
const onCampaignCalendarDoorOpenedMessageEvent = useCallback((event: CampaignCalendarDoorOpenedMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(!parser) return;
|
||||||
|
|
||||||
|
const lastAttempt = lastOpenAttempt;
|
||||||
|
|
||||||
|
if(parser.doorOpened)
|
||||||
|
{
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setCalendarData(prev =>
|
||||||
|
{
|
||||||
|
const copy = prev.clone();
|
||||||
|
copy.openedDays.push(lastOpenAttempt);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
});
|
||||||
|
|
||||||
|
setReceivedProducts(prev =>
|
||||||
|
{
|
||||||
|
const copy = new Map(prev);
|
||||||
|
copy.set(lastAttempt, parser.furnitureClassName);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastOpenAttempt(-1);
|
||||||
|
}, [lastOpenAttempt]);
|
||||||
|
|
||||||
|
CreateMessageHook(CampaignCalendarDoorOpenedMessageEvent, onCampaignCalendarDoorOpenedMessageEvent);
|
||||||
|
|
||||||
|
const openPackage = useCallback((id: number, asStaff = false) =>
|
||||||
|
{
|
||||||
|
if(!calendarData) return;
|
||||||
|
|
||||||
|
setLastOpenAttempt(id);
|
||||||
|
|
||||||
|
if(asStaff)
|
||||||
|
{
|
||||||
|
SendMessageHook(new OpenCampaignCalendarDoorAsStaffComposer(calendarData.campaignName, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendMessageHook(new OpenCampaignCalendarDoorComposer(calendarData.campaignName, id));
|
||||||
|
}
|
||||||
|
}, [calendarData]);
|
||||||
|
|
||||||
|
const onCalendarClose = useCallback(() =>
|
||||||
|
{
|
||||||
|
setCalendarOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onLinkReceived = useCallback((link: string) =>
|
||||||
|
{
|
||||||
|
const value = link.split('/');
|
||||||
|
|
||||||
|
if(value.length < 2) return;
|
||||||
|
|
||||||
|
switch(value[1])
|
||||||
|
{
|
||||||
|
case 'calendar':
|
||||||
|
setCalendarOpen(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const linkTracker = { linkReceived: onLinkReceived, eventUrlPrefix: 'openView/' };
|
||||||
|
AddEventLinkTracker(linkTracker);
|
||||||
|
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
RemoveLinkEventTracker(linkTracker);
|
||||||
|
}
|
||||||
|
}, [onLinkReceived]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{(calendarData && isCalendarOpen) &&
|
||||||
|
<CalendarView close={onCalendarClose} campaignName={calendarData.campaignName} currentDay={calendarData.currentDay} numDays={calendarData.campaignDays} openedDays={calendarData.openedDays} missedDays={calendarData.missedDays} openPackage={openPackage} receivedProducts={receivedProducts} />
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
7
src/views/campaign/common/CalendarItemState.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export class CalendarItemState
|
||||||
|
{
|
||||||
|
public static readonly STATE_UNLOCKED = 1;
|
||||||
|
public static readonly STATE_LOCKED_AVAILABLE = 2;
|
||||||
|
public static readonly STATE_LOCKED_EXPIRED = 3;
|
||||||
|
public static readonly STATE_LOCKED_FUTURE = 4;
|
||||||
|
}
|
4
src/views/campaign/common/Utils.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const getNumItemsDisplayed = (): number =>
|
||||||
|
{
|
||||||
|
return Math.min(Math.max(2, Math.floor(window.screen.width / 135) - 3), 7);
|
||||||
|
}
|
50
src/views/campaign/views/calendar-item/CalendarItemView.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { GetRoomEngine, GetSessionDataManager } from '../../../../api';
|
||||||
|
import { NitroLayoutFlexColumn } from '../../../../layout';
|
||||||
|
import { CalendarItemState } from '../../common/CalendarItemState';
|
||||||
|
import { CalendarItemViewProps } from './CalendarItemView.types';
|
||||||
|
|
||||||
|
export const CalendarItemView: FC<CalendarItemViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { state = null, productName = null, active = false, onClick = null, id = null } = props;
|
||||||
|
|
||||||
|
const getFurnitureIcon = useCallback((name: string) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
let furniData = GetSessionDataManager().getFloorItemDataByName(name);
|
||||||
|
let url = null;
|
||||||
|
if(furniData) url = GetRoomEngine().getFurnitureFloorIconUrl(furniData.id);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
furniData = GetSessionDataManager().getWallItemDataByName(name);
|
||||||
|
if(furniData) url = GetRoomEngine().getFurnitureWallIconUrl(furniData.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroLayoutFlexColumn className={`calendar-item h-100 w-100 cursor-pointer align-items-center justify-content-center ${active ? 'active' : ''}`} onClick={() => onClick(id)}>
|
||||||
|
{ (state === CalendarItemState.STATE_UNLOCKED) &&
|
||||||
|
<div className="unlocked-generic-bg d-flex justify-content-center align-items-center">
|
||||||
|
<div className="opened d-flex justify-content-center align-items-center">
|
||||||
|
{ productName &&
|
||||||
|
<img className="furni-icon" src={getFurnitureIcon(productName)} alt='' />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ (state !== CalendarItemState.STATE_UNLOCKED) &&
|
||||||
|
<div className="locked-generic-bg d-flex justify-content-center align-items-center">
|
||||||
|
{ (state === CalendarItemState.STATE_LOCKED_AVAILABLE) &&
|
||||||
|
<div className="available"/>
|
||||||
|
}
|
||||||
|
{ (state === CalendarItemState.STATE_LOCKED_EXPIRED || state === CalendarItemState.STATE_LOCKED_FUTURE) &&
|
||||||
|
<div className="unavailable" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</NitroLayoutFlexColumn>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
export interface CalendarItemViewProps
|
||||||
|
{
|
||||||
|
id: number;
|
||||||
|
productName?: string;
|
||||||
|
state: number;
|
||||||
|
active?: boolean;
|
||||||
|
onClick(itemId: number): void;
|
||||||
|
}
|
68
src/views/campaign/views/calendar/CalendarView.scss
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
.nitro-campaign-calendar {
|
||||||
|
width: 1055px;
|
||||||
|
height: 400px;
|
||||||
|
|
||||||
|
.calendar-item {
|
||||||
|
background: url("../../../../assets/images/campaign/campaign_day_generic_bg.png");
|
||||||
|
max-height: 100%;
|
||||||
|
min-width: 135px;
|
||||||
|
filter: brightness(80%);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
filter: brightness(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unlocked-generic-bg {
|
||||||
|
background: url("../../../../assets/images/campaign/unlocked_bg.png");
|
||||||
|
background-size: 132px 132px;
|
||||||
|
width: 132px;
|
||||||
|
height: 132px;
|
||||||
|
|
||||||
|
//width: 190px;
|
||||||
|
//height: 189px;
|
||||||
|
|
||||||
|
.opened {
|
||||||
|
background: url("../../../../assets/images/campaign/campaign_opened.png");
|
||||||
|
width: 96px;
|
||||||
|
height: 66px;
|
||||||
|
|
||||||
|
.furni-icon
|
||||||
|
{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.locked-generic-bg {
|
||||||
|
background: url("../../../../assets/images/campaign/locked_bg.png");
|
||||||
|
width: 132px;
|
||||||
|
height: 132px;
|
||||||
|
|
||||||
|
.available {
|
||||||
|
background: url("../../../../assets/images/campaign/available.png");
|
||||||
|
width: 69px;
|
||||||
|
height: 78px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unavailable {
|
||||||
|
background: url("../../../../assets/images/campaign/unavailable.png");
|
||||||
|
width: 68px;
|
||||||
|
height: 78px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-prev {
|
||||||
|
background: url("../../../../assets/images/campaign/prev.png");
|
||||||
|
width: 33px;
|
||||||
|
min-width: 33px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-next {
|
||||||
|
background: url("../../../../assets/images/campaign/next.png");
|
||||||
|
width: 33px;
|
||||||
|
min-width: 33px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
}
|
134
src/views/campaign/views/calendar/CalendarView.tsx
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../layout';
|
||||||
|
import { CalendarItemState } from '../../common/CalendarItemState';
|
||||||
|
import { getNumItemsDisplayed } from '../../common/Utils';
|
||||||
|
import { CalendarItemView } from '../calendar-item/CalendarItemView';
|
||||||
|
import { CalendarViewProps } from './CalendarView.types';
|
||||||
|
|
||||||
|
export const CalendarView: FC<CalendarViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { close = null, campaignName = null, currentDay = null, numDays = null, missedDays = null, openedDays = null, openPackage = null, receivedProducts = null } = props;
|
||||||
|
const [selectedDay, setSelectedDay] = useState(currentDay);
|
||||||
|
const [index, setIndex] = useState(Math.max(0, selectedDay - 1));
|
||||||
|
|
||||||
|
const getDayState = useCallback((day: number) =>
|
||||||
|
{
|
||||||
|
if(openedDays.includes(day))
|
||||||
|
{
|
||||||
|
return CalendarItemState.STATE_UNLOCKED;
|
||||||
|
}
|
||||||
|
if(day > currentDay)
|
||||||
|
{
|
||||||
|
return CalendarItemState.STATE_LOCKED_FUTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(missedDays.includes(day))
|
||||||
|
{
|
||||||
|
return CalendarItemState.STATE_LOCKED_EXPIRED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CalendarItemState.STATE_LOCKED_AVAILABLE;
|
||||||
|
}, [currentDay, missedDays, openedDays]);
|
||||||
|
|
||||||
|
const dayMessage = useCallback((day: number) =>
|
||||||
|
{
|
||||||
|
const state = getDayState(day);
|
||||||
|
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case CalendarItemState.STATE_UNLOCKED:
|
||||||
|
return LocalizeText('campaign.calendar.info.unlocked');
|
||||||
|
case CalendarItemState.STATE_LOCKED_FUTURE:
|
||||||
|
return LocalizeText('campaign.calendar.info.future');
|
||||||
|
case CalendarItemState.STATE_LOCKED_EXPIRED:
|
||||||
|
return LocalizeText('campaign.calendar.info.expired');
|
||||||
|
default: return LocalizeText('campaign.calendar.info.available.desktop');
|
||||||
|
}
|
||||||
|
}, [getDayState]);
|
||||||
|
|
||||||
|
const onClickNext = useCallback(() =>
|
||||||
|
{
|
||||||
|
const nextDay = selectedDay + 1;
|
||||||
|
|
||||||
|
if((nextDay) === numDays) return;
|
||||||
|
|
||||||
|
setSelectedDay(nextDay);
|
||||||
|
|
||||||
|
if((index + getNumItemsDisplayed()) < nextDay + 1)
|
||||||
|
{
|
||||||
|
setIndex(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [index, numDays, selectedDay]);
|
||||||
|
|
||||||
|
const onClickPrev = useCallback(() =>
|
||||||
|
{
|
||||||
|
const prevDay = selectedDay - 1;
|
||||||
|
|
||||||
|
if((prevDay < 0)) return;
|
||||||
|
|
||||||
|
setSelectedDay(prevDay);
|
||||||
|
|
||||||
|
if(index > prevDay)
|
||||||
|
{
|
||||||
|
setIndex(index - 1);
|
||||||
|
}
|
||||||
|
}, [index, selectedDay]);
|
||||||
|
|
||||||
|
const onClickItem = useCallback((item: number) =>
|
||||||
|
{
|
||||||
|
if(selectedDay === item)
|
||||||
|
{
|
||||||
|
//handle opening
|
||||||
|
const state = getDayState(item);
|
||||||
|
if(state === CalendarItemState.STATE_LOCKED_AVAILABLE) openPackage(item, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setSelectedDay(item);
|
||||||
|
}
|
||||||
|
}, [getDayState, openPackage, selectedDay]);
|
||||||
|
|
||||||
|
const forceOpen = useCallback(() =>
|
||||||
|
{
|
||||||
|
const id = selectedDay;
|
||||||
|
const state = getDayState(id);
|
||||||
|
if(GetSessionDataManager().isModerator && state !== CalendarItemState.STATE_UNLOCKED)
|
||||||
|
{
|
||||||
|
openPackage(id, true);
|
||||||
|
}
|
||||||
|
}, [getDayState, openPackage, selectedDay]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView className="nitro-campaign-calendar">
|
||||||
|
<NitroCardHeaderView headerText={LocalizeText(`campaign.calendar.${campaignName}.title`)} onCloseClick={close} />
|
||||||
|
<NitroCardContentView>
|
||||||
|
<div className="d-flex justify-content-between mx-5">
|
||||||
|
<div className="text-black">
|
||||||
|
<h3>{LocalizeText('campaign.calendar.heading.day', ['number'], [(selectedDay + 1).toString()])}</h3>
|
||||||
|
<p>{dayMessage(selectedDay)}</p>
|
||||||
|
</div>
|
||||||
|
{GetSessionDataManager().isModerator &&
|
||||||
|
<button className="btn btn-sm btn-danger my-4" onClick={forceOpen}>Force open</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className="button-container">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<NitroLayoutFlex className="h-100 align-items-center" gap={1}>
|
||||||
|
<div className="calendar-prev cursor-pointer" onClick={onClickPrev} />
|
||||||
|
{
|
||||||
|
[...Array(getNumItemsDisplayed())].map((e, i) =>
|
||||||
|
{
|
||||||
|
const day = index + i;
|
||||||
|
return <CalendarItemView key={i} state={getDayState(day)} active={selectedDay === day} onClick={onClickItem} id={day} productName={receivedProducts.has(day) ? receivedProducts.get(day) : null} />
|
||||||
|
})
|
||||||
|
}
|
||||||
|
<div className="calendar-next cursor-pointer" onClick={onClickNext} />
|
||||||
|
</NitroLayoutFlex>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
)
|
||||||
|
}
|
11
src/views/campaign/views/calendar/CalendarView.types.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface CalendarViewProps
|
||||||
|
{
|
||||||
|
close(): void;
|
||||||
|
openPackage(id: number, asStaff: boolean): void;
|
||||||
|
receivedProducts: Map<number, string>;
|
||||||
|
campaignName: string;
|
||||||
|
currentDay: number;
|
||||||
|
numDays: number;
|
||||||
|
openedDays: number[];
|
||||||
|
missedDays: number[];
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import { CallForHelpResultMessageEvent } from '@nitrots/nitro-renderer';
|
import { CallForHelpResultMessageEvent, GetPendingCallsForHelpMessageComposer, IssueCloseNotificationMessageEvent } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
import { LocalizeText } from '../../api';
|
import { LocalizeText } from '../../api';
|
||||||
import { CreateMessageHook } from '../../hooks/messages/message-event';
|
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { NotificationAlertType } from '../notification-center/common/NotificationAlertType';
|
import { NotificationAlertType } from '../notification-center/common/NotificationAlertType';
|
||||||
import { NotificationUtilities } from '../notification-center/common/NotificationUtilities';
|
import { NotificationUtilities } from '../notification-center/common/NotificationUtilities';
|
||||||
|
import { CallForHelpResult } from './common/CallForHelpResult';
|
||||||
import { GetCloseReasonKey } from './common/GetCloseReasonKey';
|
import { GetCloseReasonKey } from './common/GetCloseReasonKey';
|
||||||
|
|
||||||
export const HelpMessageHandler: FC<{}> = props =>
|
export const HelpMessageHandler: FC<{}> = props =>
|
||||||
@ -14,12 +15,36 @@ export const HelpMessageHandler: FC<{}> = props =>
|
|||||||
|
|
||||||
let message = parser.messageText;
|
let message = parser.messageText;
|
||||||
|
|
||||||
if(!message || !message.length) message = LocalizeText('help.cfh.closed.' + GetCloseReasonKey(parser.resultType))
|
switch(parser.resultType)
|
||||||
|
{
|
||||||
NotificationUtilities.simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('mod.alert.title'));
|
case CallForHelpResult.TOO_MANY_PENDING_CALLS_CODE:
|
||||||
|
SendMessageHook(new GetPendingCallsForHelpMessageComposer());
|
||||||
|
NotificationUtilities.simpleAlert(LocalizeText('help.cfh.error.pending'), NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.error.title'));
|
||||||
|
break;
|
||||||
|
case CallForHelpResult.HAS_ABUSIVE_CALL_CODE:
|
||||||
|
NotificationUtilities.simpleAlert(LocalizeText('help.cfh.error.abusive'), NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.error.title'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(message.trim().length === 0)
|
||||||
|
{
|
||||||
|
message = LocalizeText('help.cfh.sent.text');
|
||||||
|
}
|
||||||
|
NotificationUtilities.simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.sent.title'));
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
CreateMessageHook(CallForHelpResultMessageEvent, onCallForHelpResultMessageEvent);
|
CreateMessageHook(CallForHelpResultMessageEvent, onCallForHelpResultMessageEvent);
|
||||||
|
|
||||||
|
const onIssueCloseNotificationMessageEvent = useCallback((event: IssueCloseNotificationMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
const message = LocalizeText('help.cfh.closed.' + GetCloseReasonKey(parser.closeReason))
|
||||||
|
|
||||||
|
NotificationUtilities.simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('mod.alert.title'));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(IssueCloseNotificationMessageEvent, onIssueCloseNotificationMessageEvent);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
5
src/views/help/common/CallForHelpResult.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export class CallForHelpResult
|
||||||
|
{
|
||||||
|
public static readonly TOO_MANY_PENDING_CALLS_CODE = 1;
|
||||||
|
public static readonly HAS_ABUSIVE_CALL_CODE = 2;
|
||||||
|
}
|
@ -6,6 +6,7 @@ import { TransitionAnimation, TransitionAnimationTypes } from '../../layout';
|
|||||||
import { AchievementsView } from '../achievements/AchievementsView';
|
import { AchievementsView } from '../achievements/AchievementsView';
|
||||||
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
|
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
|
||||||
import { CameraWidgetView } from '../camera/CameraWidgetView';
|
import { CameraWidgetView } from '../camera/CameraWidgetView';
|
||||||
|
import { CampaignView } from '../campaign/CampaignView';
|
||||||
import { CatalogView } from '../catalog/CatalogView';
|
import { CatalogView } from '../catalog/CatalogView';
|
||||||
import { ChatHistoryView } from '../chat-history/ChatHistoryView';
|
import { ChatHistoryView } from '../chat-history/ChatHistoryView';
|
||||||
import { FloorplanEditorView } from '../floorplan-editor/FloorplanEditorView';
|
import { FloorplanEditorView } from '../floorplan-editor/FloorplanEditorView';
|
||||||
@ -77,6 +78,7 @@ export const MainView: FC<MainViewProps> = props =>
|
|||||||
<HelpView />
|
<HelpView />
|
||||||
<FloorplanEditorView />
|
<FloorplanEditorView />
|
||||||
<NitropediaView />
|
<NitropediaView />
|
||||||
|
<CampaignView />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -106,9 +106,8 @@ export const WordQuizWidgetView: FC<{}> = props =>
|
|||||||
{
|
{
|
||||||
setUserAnswers(prev =>
|
setUserAnswers(prev =>
|
||||||
{
|
{
|
||||||
const copy = new Map(prev);
|
|
||||||
const keysToRemove: number[] = [];
|
const keysToRemove: number[] = [];
|
||||||
copy.forEach((value, key) =>
|
prev.forEach((value, key) =>
|
||||||
{
|
{
|
||||||
value.secondsLeft--;
|
value.secondsLeft--;
|
||||||
|
|
||||||
@ -118,8 +117,10 @@ export const WordQuizWidgetView: FC<{}> = props =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keysToRemove.forEach(key => copy.delete(key));
|
if(keysToRemove.length === 0) return prev;
|
||||||
|
|
||||||
|
const copy = new Map(prev);
|
||||||
|
keysToRemove.forEach(key => copy.delete(key));
|
||||||
return copy;
|
return copy;
|
||||||
})
|
})
|
||||||
|
|
||||||
|