Merge remote-tracking branch 'origin/dev' into feature/messenger

This commit is contained in:
MyNameIsBatman 2021-09-18 13:51:11 -03:00
commit cc33ba3e36
60 changed files with 573 additions and 571 deletions

View File

@ -126,6 +126,7 @@ export const App: FC<{}> = props =>
return ( return (
<div className="nitro-app"> <div className="nitro-app">
<div id="nitro-alerts-container" />
{ (!isReady || isError) && <LoadingView isError={ isError } message={ message } /> } { (!isReady || isError) && <LoadingView isError={ isError } message={ message } /> }
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ (isReady && !isError) } timeout={ 300 }> <TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ (isReady && !isError) } timeout={ 300 }>
<MainView /> <MainView />

View File

@ -1,29 +1,36 @@
import { NitroEvent } from '@nitrots/nitro-renderer'; import { NitroEvent } from '@nitrots/nitro-renderer';
export class SimpleAlertUIEvent extends NitroEvent export class NotificationAlertEvent extends NitroEvent
{ {
public static ALERT: string = 'SAUE_ALERT'; public static ALERT: string = 'NAE_ALERT';
private _message: string; private _messages: string[];
private _alertType: string;
private _clickUrl: string; private _clickUrl: string;
private _clickUrlText: string; private _clickUrlText: string;
private _title: string; private _title: string;
private _imageUrl: string; private _imageUrl: string;
constructor(message: string, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null) constructor(messages: string[], alertType: string = null, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null)
{ {
super(SimpleAlertUIEvent.ALERT); super(NotificationAlertEvent.ALERT);
this._message = message; this._messages = messages;
this._alertType = alertType;
this._clickUrl = clickUrl; this._clickUrl = clickUrl;
this._clickUrlText = clickUrlText; this._clickUrlText = clickUrlText;
this._title = title; this._title = title;
this._imageUrl = imageUrl; this._imageUrl = imageUrl;
} }
public get message(): string public get messages(): string[]
{ {
return this._message; return this._messages;
}
public get alertType(): string
{
return this._alertType;
} }
public get clickUrl(): string public get clickUrl(): string

View File

@ -2,7 +2,7 @@ import { NitroEvent } from '@nitrots/nitro-renderer';
export class NotificationBubbleEvent extends NitroEvent export class NotificationBubbleEvent extends NitroEvent
{ {
public static NEW_BUBBLE: string = 'NNBE_NEW_BUBBLE'; public static NEW_BUBBLE: string = 'NBE_NEW_BUBBLE';
private _message: string; private _message: string;
private _notificationType: string; private _notificationType: string;

View File

@ -1,19 +0,0 @@
import { NitroNotification } from '../../views/notification-center/common/Notification';
import { NotificationCenterEvent } from './NotificationCenterEvent';
export class NotificationCenterAddNotificationEvent extends NotificationCenterEvent
{
private _notification: NitroNotification;
constructor(notification: NitroNotification)
{
super(NotificationCenterEvent.ADD_NOTIFICATION);
this._notification = notification;
}
public get notification(): NitroNotification
{
return this._notification;
}
}

View File

@ -1,4 +1,4 @@
export * from './NotificationCenterAddNotificationEvent'; export * from './NotificationAlertEvent';
export * from './NotificationBubbleEvent';
export * from './NotificationCenterAlertEvent'; export * from './NotificationCenterAlertEvent';
export * from './NotificationCenterEvent'; export * from './NotificationCenterEvent';
export * from './SimpleAlertUIEvent';

View File

@ -27,6 +27,7 @@
@import './loading-habbos/LoadingHabbosView'; @import './loading-habbos/LoadingHabbosView';
@import './loading-spinner/LoadingSpinnerView'; @import './loading-spinner/LoadingSpinnerView';
@import './mini-camera/NitroLayoutMiniCameraView'; @import './mini-camera/NitroLayoutMiniCameraView';
@import './notification-alert/NotificationAlertView';
@import './notification-bubble/NotificationBubbleView'; @import './notification-bubble/NotificationBubbleView';
@import './trophy/NitroLayoutTrophyView'; @import './trophy/NitroLayoutTrophyView';
@import './gift-card/NitroLayoutGiftCardView'; @import './gift-card/NitroLayoutGiftCardView';

View File

@ -3,6 +3,7 @@ export * from './draggable-window';
export * from './gift-card'; export * from './gift-card';
export * from './loading-spinner'; export * from './loading-spinner';
export * from './mini-camera'; export * from './mini-camera';
export * from './notification-alert';
export * from './notification-bubble'; export * from './notification-bubble';
export * from './transitions'; export * from './transitions';
export * from './trophy'; export * from './trophy';

View File

@ -0,0 +1,8 @@
.nitro-alert {
width: 350px;
.content-area {
min-height: 125px;
max-height: 300px;
}
}

View File

@ -0,0 +1,17 @@
import { FC } from 'react';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../card';
import { NotificationAlertViewProps } from './NotificationAlertView.types';
export const NotificationAlertView: FC<NotificationAlertViewProps> = props =>
{
const { title = '', close = null, className = '', children = null, ...rest } = props;
return (
<NitroCardView className={ 'nitro-alert ' + className } simple={ true } { ...rest }>
<NitroCardHeaderView headerText={ title } onCloseClick={ close } />
<NitroCardContentView className="d-flex flex-column justify-content-between text-black">
{ children }
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,7 @@
import { DetailsHTMLAttributes } from 'react';
export interface NotificationAlertViewProps extends DetailsHTMLAttributes<HTMLDivElement>
{
title: string;
close: () => void;
}

View File

@ -0,0 +1,2 @@
export * from './NotificationAlertView';
export * from './NotificationAlertView.types';

View File

@ -10,7 +10,7 @@ import { CatalogMode, CatalogViewProps } from './CatalogView.types';
import { BuildCatalogPageTree } from './common/CatalogUtilities'; import { BuildCatalogPageTree } from './common/CatalogUtilities';
import { CatalogContextProvider } from './context/CatalogContext'; import { CatalogContextProvider } from './context/CatalogContext';
import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer'; import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
import { CatalogPageGiftView } from './views/gift/CatalogPageGiftView'; import { CatalogGiftView } from './views/gift/CatalogGiftView';
import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView';
import { CatalogPageView } from './views/page/CatalogPageView'; import { CatalogPageView } from './views/page/CatalogPageView';
@ -214,7 +214,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
</div> </div>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> } </NitroCardView> }
<CatalogPageGiftView /> <CatalogGiftView />
</CatalogContextProvider> </CatalogContextProvider>
); );
} }

View File

@ -2,4 +2,4 @@
@import './navigation/CatalogNavigationView'; @import './navigation/CatalogNavigationView';
@import './page/CatalogPageView'; @import './page/CatalogPageView';
@import './search/CatalogSearchView'; @import './search/CatalogSearchView';
@import './gift/CatalogPageGiftView'; @import './gift/CatalogGiftView';

View File

@ -10,7 +10,7 @@ import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { FurniImageView } from '../../../shared/furni-image/FurniImageView'; import { FurniImageView } from '../../../shared/furni-image/FurniImageView';
import { useCatalogContext } from '../../context/CatalogContext'; import { useCatalogContext } from '../../context/CatalogContext';
export const CatalogPageGiftView: FC<{}> = props => export const CatalogGiftView: FC<{}> = props =>
{ {
const { catalogState = null } = useCatalogContext(); const { catalogState = null } = useCatalogContext();
const { giftConfiguration = null } = catalogState; const { giftConfiguration = null } = catalogState;

View File

@ -63,8 +63,6 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProp
const stringDataType = new StringDataType(); const stringDataType = new StringDataType();
stringDataType.setValue(productData); stringDataType.setValue(productData);
console.log(stringDataType);
dispatchUiEvent(new SetRoomPreviewerStuffDataEvent(activeOffer, stringDataType)); dispatchUiEvent(new SetRoomPreviewerStuffDataEvent(activeOffer, stringDataType));
}, [ currentBadge, activeOffer, roomPreviewer ]); }, [ currentBadge, activeOffer, roomPreviewer ]);

View File

@ -0,0 +1,27 @@
import { CallForHelpResultMessageEvent, FurnitureListItemParser, PetData } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../api';
import { CreateMessageHook } from '../../hooks/messages/message-event';
import { NotificationAlertType } from '../notification-center/common/NotificationAlertType';
import { NotificationUtilities } from '../notification-center/common/NotificationUtilities';
import { GetCloseReasonKey } from './common/GetCloseReasonKey';
let furniMsgFragments: Map<number, FurnitureListItemParser>[] = null;
let petMsgFragments: Map<number, PetData>[] = null;
export const HelpMessageHandler: FC<{}> = props =>
{
const onCallForHelpResultMessageEvent = useCallback((event: CallForHelpResultMessageEvent) =>
{
const parser = event.getParser();
let message = parser.messageText;
if(!message || !message.length) message = LocalizeText('help.cfh.closed.' + GetCloseReasonKey(parser.resultType))
NotificationUtilities.simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('mod.alert.title'));
}, []);
CreateMessageHook(CallForHelpResultMessageEvent, onCallForHelpResultMessageEvent);
return null;
}

View File

@ -0,0 +1,11 @@
import { FC } from 'react';
import { HelpMessageHandler } from './HelpMessageHandler';
export const HelpView: FC<{}> = props =>
{
return (
<>
<HelpMessageHandler />
</>
);
}

View File

@ -0,0 +1,8 @@
export const GetCloseReasonKey = (code: number) =>
{
if(code === 1) return 'useless';
if(code === 2) return 'abusive';
return 'resolved';
}

View File

@ -9,6 +9,7 @@ import { CameraWidgetView } from '../camera/CameraWidgetView';
import { CatalogView } from '../catalog/CatalogView'; import { CatalogView } from '../catalog/CatalogView';
import { FriendsView } from '../friends/FriendsView'; import { FriendsView } from '../friends/FriendsView';
import { GroupsView } from '../groups/GroupsView'; import { GroupsView } from '../groups/GroupsView';
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';
import { ModToolsView } from '../mod-tools/ModToolsView'; import { ModToolsView } from '../mod-tools/ModToolsView';
@ -69,6 +70,7 @@ export const MainView: FC<MainViewProps> = props =>
<UserProfileView /> <UserProfileView />
<GroupsView /> <GroupsView />
<CameraWidgetView /> <CameraWidgetView />
<HelpView />
</div> </div>
); );
} }

View File

@ -1,20 +1,14 @@
import { AchievementNotificationMessageEvent, ActivityPointNotificationMessageEvent, ClubGiftNotificationEvent, HabboBroadcastMessageEvent, HotelClosesAndWillOpenAtEvent, HotelWillShutdownEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent, PetAddedToInventoryEvent, RespectReceivedEvent, RoomEnterEvent, Vector3d } from '@nitrots/nitro-renderer'; import { AchievementNotificationMessageEvent, ActivityPointNotificationMessageEvent, ClubGiftNotificationEvent, ClubGiftSelectedEvent, HabboBroadcastMessageEvent, HotelClosedAndOpensEvent, HotelClosesAndWillOpenAtEvent, HotelWillCloseInMinutesEvent, InfoFeedEnableMessageEvent, MaintenanceStatusMessageEvent, ModeratorCautionEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent, PetLevelNotificationEvent, PetReceivedMessageEvent, RespectReceivedEvent, RoomEnterEvent, UserBannedMessageEvent, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { GetRoomEngine, GetSessionDataManager, LocalizeBadgeName, LocalizeText } from '../../api'; import { GetConfiguration, GetRoomEngine, GetSessionDataManager, LocalizeBadgeName, LocalizeText } from '../../api';
import { NotificationCenterAlertEvent } from '../../events';
import { dispatchUiEvent } from '../../hooks/events';
import { CreateMessageHook } from '../../hooks/messages'; import { CreateMessageHook } from '../../hooks/messages';
import { HotelWillShutdownNotification } from './common/HotelWillShutdownNotification'; import { NotificationBubbleType } from './common/NotificationBubbleType';
import { NotificationType } from './common/NotificationType';
import { NotificationUtilities } from './common/NotificationUtilities'; import { NotificationUtilities } from './common/NotificationUtilities';
import { useNotificationCenterContext } from './context/NotificationCenterContext'; import { ProductImageUtility } from './common/ProductImageUtility';
import { INotificationCenterMessageHandlerProps } from './NotificationCenterMessageHandler.types'; import { INotificationCenterMessageHandlerProps } from './NotificationCenterMessageHandler.types';
import { NotificationCenterActions } from './reducers/NotificationCenterReducer';
export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHandlerProps> = props => export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHandlerProps> = props =>
{ {
const { dispatchNotificationCenterState = null } = useNotificationCenterContext();
const onRespectReceivedEvent = useCallback((event: RespectReceivedEvent) => const onRespectReceivedEvent = useCallback((event: RespectReceivedEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
@ -24,8 +18,8 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
const text1 = LocalizeText('notifications.text.respect.1'); const text1 = LocalizeText('notifications.text.respect.1');
const text2 = LocalizeText('notifications.text.respect.2', [ 'count' ], [ parser.respectsReceived.toString() ]); const text2 = LocalizeText('notifications.text.respect.2', [ 'count' ], [ parser.respectsReceived.toString() ]);
NotificationUtilities.showSingleBubble(text1, NotificationType.RESPECT); NotificationUtilities.showSingleBubble(text1, NotificationBubbleType.RESPECT);
NotificationUtilities.showSingleBubble(text2, NotificationType.RESPECT); NotificationUtilities.showSingleBubble(text2, NotificationBubbleType.RESPECT);
}, []); }, []);
CreateMessageHook(RespectReceivedEvent, onRespectReceivedEvent); CreateMessageHook(RespectReceivedEvent, onRespectReceivedEvent);
@ -34,7 +28,7 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
{ {
const parser = event.getParser(); const parser = event.getParser();
NotificationUtilities.simpleAlert(parser.message.replace(/\\r/g, '\r')); NotificationUtilities.simpleAlert(parser.message.replace(/\\r/g, '\r'), null, null, LocalizeText('notifications.broadcast.title'));
}, []); }, []);
CreateMessageHook(HabboBroadcastMessageEvent, onHabboBroadcastMessageEvent); CreateMessageHook(HabboBroadcastMessageEvent, onHabboBroadcastMessageEvent);
@ -48,7 +42,7 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
const badgeImage = GetSessionDataManager().getBadgeUrl(parser.data.badgeCode); const badgeImage = GetSessionDataManager().getBadgeUrl(parser.data.badgeCode);
const internalLink = 'questengine/achievements/' + parser.data.category; const internalLink = 'questengine/achievements/' + parser.data.category;
NotificationUtilities.showSingleBubble((text1 + ' ' + badgeName), NotificationType.ACHIEVEMENT, badgeImage, internalLink); NotificationUtilities.showSingleBubble((text1 + ' ' + badgeName), NotificationBubbleType.ACHIEVEMENT, badgeImage, internalLink);
}, []); }, []);
CreateMessageHook(AchievementNotificationMessageEvent, onAchievementNotificationMessageEvent); CreateMessageHook(AchievementNotificationMessageEvent, onAchievementNotificationMessageEvent);
@ -66,7 +60,7 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
{ {
const parser = event.getParser(); const parser = event.getParser();
dispatchUiEvent(new NotificationCenterAlertEvent(NotificationCenterAlertEvent.HOTEL_ALERT, [ parser.message ], parser.link)); NotificationUtilities.handleModeratorMessage(parser.message, parser.link);
}, []); }, []);
CreateMessageHook(ModeratorMessageEvent, onModeratorMessageEvent); CreateMessageHook(ModeratorMessageEvent, onModeratorMessageEvent);
@ -75,11 +69,31 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
{ {
const parser = event.getParser(); const parser = event.getParser();
// bubble for loyalty if(parser.amountChanged <= 0) return;
let imageUrl: string = null;
switch(parser.type)
{
case 5:
imageUrl = GetConfiguration<string>('currency.asset.icon.url', '').replace('%type%', parser.type.toString());
break;
}
NotificationUtilities.showSingleBubble(LocalizeText('notifications.text.loyalty.received', [ 'amount' ], [ parser.amountChanged.toString() ]), NotificationBubbleType.INFO, imageUrl);
}, []); }, []);
CreateMessageHook(ActivityPointNotificationMessageEvent, onActivityPointNotificationMessageEvent); CreateMessageHook(ActivityPointNotificationMessageEvent, onActivityPointNotificationMessageEvent);
const onUserBannedMessageEvent = useCallback((event: UserBannedMessageEvent) =>
{
const parser = event.getParser();
NotificationUtilities.handleUserBannedMessage(parser.message);
}, []);
CreateMessageHook(UserBannedMessageEvent, onUserBannedMessageEvent);
const onHotelClosesAndWillOpenAtEvent = useCallback((event: HotelClosesAndWillOpenAtEvent) => const onHotelClosesAndWillOpenAtEvent = useCallback((event: HotelClosesAndWillOpenAtEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
@ -89,7 +103,7 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
CreateMessageHook(HotelClosesAndWillOpenAtEvent, onHotelClosesAndWillOpenAtEvent); CreateMessageHook(HotelClosesAndWillOpenAtEvent, onHotelClosesAndWillOpenAtEvent);
const onPetAddedToInventoryEvent = useCallback((event: PetAddedToInventoryEvent) => const onPetReceivedMessageEvent = useCallback((event: PetReceivedMessageEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
@ -101,10 +115,10 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
if(imageResult) imageUrl = imageResult.getImage().src; if(imageResult) imageUrl = imageResult.getImage().src;
NotificationUtilities.showSingleBubble(text, NotificationType.PETLEVEL, imageUrl); NotificationUtilities.showSingleBubble(text, NotificationBubbleType.PETLEVEL, imageUrl);
}, []); }, []);
CreateMessageHook(PetAddedToInventoryEvent, onPetAddedToInventoryEvent); CreateMessageHook(PetReceivedMessageEvent, onPetReceivedMessageEvent);
const onRoomEnterEvent = useCallback((event: RoomEnterEvent) => const onRoomEnterEvent = useCallback((event: RoomEnterEvent) =>
{ {
@ -119,24 +133,67 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
{ {
const parser = event.getParser(); const parser = event.getParser();
dispatchUiEvent(new NotificationCenterAlertEvent(NotificationCenterAlertEvent.HOTEL_ALERT, parser.messages)); NotificationUtilities.handleMOTD(parser.messages);
}, []); }, []);
CreateMessageHook(MOTDNotificationEvent, onMOTDNotificationEvent); CreateMessageHook(MOTDNotificationEvent, onMOTDNotificationEvent);
const onHotelWillShutdownEvent = useCallback((event: HotelWillShutdownEvent) => const onPetLevelNotificationEvent = useCallback((event: PetLevelNotificationEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
dispatchNotificationCenterState({ let imageUrl: string = null;
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new HotelWillShutdownNotification(parser.minutes)
}
});
}, [ dispatchNotificationCenterState ]);
CreateMessageHook(HotelWillShutdownEvent, onHotelWillShutdownEvent); const imageResult = GetRoomEngine().getRoomObjectPetImage(parser.figureData.typeId, parser.figureData.paletteId, parseInt(parser.figureData.color, 16), new Vector3d(45 * 3), 64, null, true);
if(imageResult) imageUrl = imageResult.getImage().src;
NotificationUtilities.showSingleBubble(LocalizeText('notifications.text.petlevel', [ 'pet_name', 'level' ], [ parser.petName, parser.level.toString() ]), NotificationBubbleType.PETLEVEL, imageUrl);
}, []);
CreateMessageHook(PetLevelNotificationEvent, onPetLevelNotificationEvent);
const onInfoFeedEnableMessageEvent = useCallback((event: InfoFeedEnableMessageEvent) =>
{
const parser = event.getParser();
NotificationUtilities.BUBBLES_DISABLED = !(parser.enabled);
}, []);
CreateMessageHook(InfoFeedEnableMessageEvent, onInfoFeedEnableMessageEvent);
const onClubGiftSelectedEvent = useCallback((event: ClubGiftSelectedEvent) =>
{
const parser = event.getParser();
if(!parser.products || !parser.products.length) return;
const productData = parser.products[0];
if(!productData) return;
NotificationUtilities.showSingleBubble(LocalizeText('notifications.text.club_gift.selected'), NotificationBubbleType.INFO, ProductImageUtility.getProductImageUrl(productData.productType, productData.furniClassId, productData.extraParam))
}, []);
CreateMessageHook(ClubGiftSelectedEvent, onClubGiftSelectedEvent);
const onMaintenanceStatusMessageEvent = useCallback((event: MaintenanceStatusMessageEvent) =>
{
const parser = event.getParser();
NotificationUtilities.handleHotelMaintenanceMessage(parser.minutesUntilMaintenance, parser.duration);
}, []);
CreateMessageHook(MaintenanceStatusMessageEvent, onMaintenanceStatusMessageEvent);
const onModeratorCautionEvent = useCallback((event: ModeratorCautionEvent) =>
{
const parser = event.getParser();
NotificationUtilities.handleModeratorCaution(parser.message, parser.url);
}, []);
CreateMessageHook(ModeratorCautionEvent, onModeratorCautionEvent);
const onNotificationDialogMessageEvent = useCallback((event: NotificationDialogMessageEvent) => const onNotificationDialogMessageEvent = useCallback((event: NotificationDialogMessageEvent) =>
{ {
@ -147,5 +204,23 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
CreateMessageHook(NotificationDialogMessageEvent, onNotificationDialogMessageEvent); CreateMessageHook(NotificationDialogMessageEvent, onNotificationDialogMessageEvent);
const onHotelWillCloseInMinutesEvent = useCallback((event: HotelWillCloseInMinutesEvent) =>
{
const parser = event.getParser();
NotificationUtilities.handleHotelClosingMessage(parser.openMinute);
}, []);
CreateMessageHook(HotelWillCloseInMinutesEvent, onHotelWillCloseInMinutesEvent);
const onHotelClosedAndOpensEvent = useCallback((event: HotelClosedAndOpensEvent) =>
{
const parser = event.getParser();
NotificationUtilities.handleLoginFailedHotelClosedMessage(parser.openHour, parser.openMinute);
}, []);
CreateMessageHook(HotelClosedAndOpensEvent, onHotelClosedAndOpensEvent);
return null; return null;
} }

View File

@ -1,12 +1,3 @@
.nitro-alert {
width: 350px;
.content-area {
min-height: 125px;
max-height: 300px;
}
}
.nitro-notification-center-container { .nitro-notification-center-container {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -1,40 +1,42 @@
import { FC, ReactNode, useCallback, useMemo, useState } from 'react'; import { FC, ReactNode, useCallback, useMemo, useState } from 'react';
import { NotificationCenterAlertEvent } from '../../events'; import { createPortal } from 'react-dom';
import { NotificationAlertEvent } from '../../events';
import { NotificationBubbleEvent } from '../../events/notification-center/NotificationBubbleEvent'; import { NotificationBubbleEvent } from '../../events/notification-center/NotificationBubbleEvent';
import { useUiEvent } from '../../hooks/events'; import { useUiEvent } from '../../hooks/events';
import { NotificationItem } from './common/NotificationItem'; import { NotificationAlertItem } from './common/NotificationAlertItem';
import { NotificationType } from './common/NotificationType'; import { NotificationBubbleItem } from './common/NotificationBubbleItem';
import { NotificationBubbleType } from './common/NotificationBubbleType';
import { NotificationCenterMessageHandler } from './NotificationCenterMessageHandler'; import { NotificationCenterMessageHandler } from './NotificationCenterMessageHandler';
import { NotificationCenterViewProps } from './NotificationCenterView.types'; import { NotificationCenterViewProps } from './NotificationCenterView.types';
import { NotificationCenterBroadcastMessageView } from './views/broadcast-message/NotificationCenterBroadcastMessageView'; import { GetAlertLayout } from './views/alert-layouts/GetAlertLayout';
import { GetBubbleLayout } from './views/bubble-layouts/GetBubbleLayout'; import { GetBubbleLayout } from './views/bubble-layouts/GetBubbleLayout';
export const NotificationCenterView: FC<NotificationCenterViewProps> = props => export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
{ {
const [ alerts, setAlerts ] = useState<NotificationCenterAlertEvent[]>([]); const [ alerts, setAlerts ] = useState<NotificationAlertItem[]>([]);
const [ bubbleAlerts, setBubbleAlerts ] = useState<NotificationItem[]>([]); const [ bubbleAlerts, setBubbleAlerts ] = useState<NotificationBubbleItem[]>([]);
const onNotificationCenterAlertEvent = useCallback((event: NotificationCenterAlertEvent) => const onNotificationAlertEvent = useCallback((event: NotificationAlertEvent) =>
{ {
setAlerts(prevValue => console.log(event);
{ const alertItem = new NotificationAlertItem(event.messages, event.alertType, event.clickUrl, event.clickUrlText, event.title, event.imageUrl);
return [ ...prevValue, event ];
}); setAlerts(prevValue => [ alertItem, ...prevValue ]);
}, []); }, []);
useUiEvent(NotificationCenterAlertEvent.HOTEL_ALERT, onNotificationCenterAlertEvent); useUiEvent(NotificationAlertEvent.ALERT, onNotificationAlertEvent);
const onNotificationBubbleEvent = useCallback((event: NotificationBubbleEvent) => const onNotificationBubbleEvent = useCallback((event: NotificationBubbleEvent) =>
{ {
console.log(event); console.log(event);
const notificationItem = new NotificationItem(event.message, event.notificationType, event.imageUrl, event.linkUrl); const notificationItem = new NotificationBubbleItem(event.message, event.notificationType, event.imageUrl, event.linkUrl);
setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]); setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]);
}, []); }, []);
useUiEvent(NotificationBubbleEvent.NEW_BUBBLE, onNotificationBubbleEvent); useUiEvent(NotificationBubbleEvent.NEW_BUBBLE, onNotificationBubbleEvent);
const closeAlert = useCallback((alert: NotificationCenterAlertEvent) => const closeAlert = useCallback((alert: NotificationAlertItem) =>
{ {
setAlerts(prevValue => setAlerts(prevValue =>
{ {
@ -47,7 +49,7 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
}); });
}, []); }, []);
const closeBubbleAlert = useCallback((item: NotificationItem) => const closeBubbleAlert = useCallback((item: NotificationBubbleItem) =>
{ {
setBubbleAlerts(prevValue => setBubbleAlerts(prevValue =>
{ {
@ -60,6 +62,22 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
}) })
}, []); }, []);
const getAlerts = useMemo(() =>
{
if(!alerts || !alerts.length) return null;
const elements: ReactNode[] = [];
for(const alert of alerts)
{
const element = GetAlertLayout(alert, () => closeAlert(alert));
elements.push(element);
}
return elements;
}, [ alerts, closeAlert ]);
const getBubbleAlerts = useMemo(() => const getBubbleAlerts = useMemo(() =>
{ {
if(!bubbleAlerts || !bubbleAlerts.length) return null; if(!bubbleAlerts || !bubbleAlerts.length) return null;
@ -70,7 +88,7 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
{ {
const element = GetBubbleLayout(alert, () => closeBubbleAlert(alert)); const element = GetBubbleLayout(alert, () => closeBubbleAlert(alert));
if(alert.notificationType === NotificationType.CLUBGIFT) if(alert.notificationType === NotificationBubbleType.CLUBGIFT)
{ {
elements.unshift(element); elements.unshift(element);
@ -89,15 +107,7 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
<div className="nitro-notification-center"> <div className="nitro-notification-center">
{ getBubbleAlerts } { getBubbleAlerts }
</div> </div>
{ (alerts.length > 0) && alerts.map((alert, index) => { createPortal(getAlerts, document.getElementById('nitro-alerts-container')) }
{
switch(alert.type)
{
case NotificationCenterAlertEvent.HOTEL_ALERT:
default:
return <NotificationCenterBroadcastMessageView key={ index } notification={ alert } onClose={ () => closeAlert(alert) } />;
}
})}
</> </>
); );
} }

View File

@ -1,4 +0,0 @@
import { NitroNotification } from './Notification';
export class BroadcastMessageNotification extends NitroNotification
{}

View File

@ -1,24 +0,0 @@
import { NitroNotification } from './Notification';
export class DialogMessageNotification extends NitroNotification
{
private _type: string;
private _parameters: Map<string, string>;
constructor(type: string, parameters: Map<string, string>)
{
super();
this._type = type;
this._parameters = parameters;
}
public get type(): string
{
return this._type;
}
public get parameters(): Map<string, string>
{
return this._parameters;
}
}

View File

@ -1,17 +0,0 @@
import { NitroNotification } from './Notification';
export class HotelWillShutdownNotification extends NitroNotification
{
private _minutes: number;
constructor(minutes: number)
{
super();
this._minutes = minutes;
}
public get minutes(): number
{
return this._minutes;
}
}

View File

@ -1,19 +0,0 @@
import { NitroNotification } from './Notification';
export class MOTDNotification extends NitroNotification
{
private _messages: string[];
constructor(messages: string[])
{
super();
this._messages = [];
for(const message of messages) this._messages.push(message.replace(/\r\n|\r|\n/g, '<br />'));
}
public get messages(): string[]
{
return this._messages;
}
}

View File

@ -1,17 +0,0 @@
import { NitroNotification } from './Notification';
export class ModeratorMessageNotification extends NitroNotification
{
private _link: string;
constructor(message: string, link: string)
{
super(message);
this._link = link;
}
public get link(): string
{
return this._link;
}
}

View File

@ -1,49 +0,0 @@
export class NitroNotification
{
public static CURRENT_ID: number = 0;
private _id: number;
private _title: string;
private _message: string;
private _dismissed: boolean = false;
private _timestamp: number;
constructor(message: string = null, title: string = null)
{
this._id = ++NitroNotification.CURRENT_ID;
this._title = title;
this._timestamp = Date.now();
if(message) this._message = message.replace(/\r\n|\r|\n/g, '<br />');
}
public dismiss(): void
{
this._dismissed = true;
}
public get id(): number
{
return this._id;
}
public get title(): string
{
return this._title;
}
public get message(): string
{
return this._message;
}
public get dismissed(): boolean
{
return this._dismissed;
}
public get timestamp(): number
{
return this._timestamp;
}
}

View File

@ -0,0 +1,62 @@
import { NotificationBubbleType } from './NotificationBubbleType';
export class NotificationAlertItem
{
private static ITEM_ID: number = -1;
private _id: number;
private _messages: string[];
private _alertType: string;
private _clickUrl: string;
private _clickUrlText: string;
private _title: string;
private _imageUrl: string;
constructor(messages: string[], alertType: string = NotificationBubbleType.INFO, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null)
{
NotificationAlertItem.ITEM_ID += 1;
this._id = NotificationAlertItem.ITEM_ID;
this._messages = messages;
this._alertType = alertType;
this._clickUrl = clickUrl;
this._clickUrlText = clickUrlText;
this._title = title;
this._imageUrl = imageUrl;
}
public get id(): number
{
return this._id;
}
public get messages(): string[]
{
return this._messages;
}
public get alertType(): string
{
return this._alertType;
}
public get clickUrl(): string
{
return this._clickUrl;
}
public get clickUrlText(): string
{
return this._clickUrlText;
}
public get title(): string
{
return this._title;
}
public get imageUrl(): string
{
return this._imageUrl;
}
}

View File

@ -0,0 +1,7 @@
export class NotificationAlertType
{
public static DEFAULT: string = 'default';
public static MOTD: string = 'motd';
public static MODERATION: string = 'moderation';
public static EVENT: string = 'event';
}

View File

@ -1,6 +1,6 @@
import { NotificationType } from './NotificationType'; import { NotificationBubbleType } from './NotificationBubbleType';
export class NotificationItem export class NotificationBubbleItem
{ {
private static ITEM_ID: number = -1; private static ITEM_ID: number = -1;
@ -10,11 +10,11 @@ export class NotificationItem
private _iconUrl: string; private _iconUrl: string;
private _linkUrl: string; private _linkUrl: string;
constructor(message: string, notificationType: string = NotificationType.INFO, iconUrl: string = null, linkUrl: string = null) constructor(message: string, notificationType: string = NotificationBubbleType.INFO, iconUrl: string = null, linkUrl: string = null)
{ {
NotificationItem.ITEM_ID += 1; NotificationBubbleItem.ITEM_ID += 1;
this._id = NotificationItem.ITEM_ID; this._id = NotificationBubbleItem.ITEM_ID;
this._message = message; this._message = message;
this._notificationType = notificationType; this._notificationType = notificationType;
this._iconUrl = iconUrl; this._iconUrl = iconUrl;

View File

@ -1,4 +1,4 @@
export class NotificationType export class NotificationBubbleType
{ {
public static FRIENDOFFLINE: string = 'friendoffline'; public static FRIENDOFFLINE: string = 'friendoffline';
public static FRIENDONLINE: string = 'friendonline'; public static FRIENDONLINE: string = 'friendonline';

View File

@ -1,10 +1,11 @@
import { HabboWebTools, RoomEnterEffect } from '@nitrots/nitro-renderer'; import { HabboWebTools, RoomEnterEffect } from '@nitrots/nitro-renderer';
import { CreateLinkEvent, GetConfiguration, GetNitroInstance, LocalizeText } from '../../../api'; import { CreateLinkEvent, GetConfiguration, GetNitroInstance, LocalizeText } from '../../../api';
import { SimpleAlertUIEvent } from '../../../events'; import { NotificationAlertEvent } from '../../../events';
import { NotificationBubbleEvent } from '../../../events/notification-center/NotificationBubbleEvent'; import { NotificationBubbleEvent } from '../../../events/notification-center/NotificationBubbleEvent';
import { dispatchUiEvent } from '../../../hooks'; import { dispatchUiEvent } from '../../../hooks';
import { CatalogPageName } from '../../catalog/common/CatalogPageName'; import { CatalogPageName } from '../../catalog/common/CatalogPageName';
import { NotificationType } from './NotificationType'; import { NotificationAlertType } from './NotificationAlertType';
import { NotificationBubbleType } from './NotificationBubbleType';
export class NotificationUtilities export class NotificationUtilities
{ {
@ -12,6 +13,8 @@ export class NotificationUtilities
private static MODERATION_DISCLAIMER_DELAY_MS: number = 5000; private static MODERATION_DISCLAIMER_DELAY_MS: number = 5000;
private static MODERATION_DISCLAIMER_TIMEOUT: ReturnType<typeof setTimeout> = null; private static MODERATION_DISCLAIMER_TIMEOUT: ReturnType<typeof setTimeout> = null;
public static BUBBLES_DISABLED: boolean = false;
private static cleanText(text: string): string private static cleanText(text: string): string
{ {
return text.replace(/\\r/g, '\r') return text.replace(/\\r/g, '\r')
@ -68,62 +71,92 @@ export class NotificationUtilities
const configuration = this.getNotificationConfig(('notification.' + type)); const configuration = this.getNotificationConfig(('notification.' + type));
if(configuration) if(configuration) for(const key in configuration) options.set(key, configuration[key]);
{
for(const key in configuration) options.set(key, configuration[key]);
}
console.log(options); console.log(options);
if(options.get('display') === 'BUBBLE') const title = this.getNotificationPart(options, type, 'title', true);
{ const message = this.getNotificationPart(options, type, 'message', true).replace(/\\r/g, '\r');
const message = this.getNotificationPart(options, type, 'message', true); const linkTitle = this.getNotificationPart(options, type, 'linkTitle', false);
const linkUrl = this.getNotificationPart(options, type, 'linkUrl', false); const linkUrl = this.getNotificationPart(options, type, 'linkUrl', false);
const isEventLink = (linkUrl && linkUrl.substr(0, 6) === 'event');
const image = this.getNotificationImageUrl(options, type); const image = this.getNotificationImageUrl(options, type);
dispatchUiEvent(new NotificationBubbleEvent(LocalizeText(message), NotificationType.INFO, LocalizeText(image), (isEventLink ? linkUrl.substr(6) : linkUrl))); if(options.get('display') === 'BUBBLE')
{
this.showSingleBubble(LocalizeText(message), NotificationBubbleType.INFO, LocalizeText(image), linkUrl);
} }
else else
{ {
this.simpleAlert(message, NotificationAlertType.EVENT, linkUrl, linkTitle, title, image);
} }
} }
public static showSingleBubble(message: string, type: string, imageUrl: string = null, internalLink: string = null): void public static showSingleBubble(message: string, type: string, imageUrl: string = null, internalLink: string = null): void
{ {
if(this.BUBBLES_DISABLED) return;
dispatchUiEvent(new NotificationBubbleEvent(message, type, imageUrl, internalLink)); dispatchUiEvent(new NotificationBubbleEvent(message, type, imageUrl, internalLink));
} }
public static simpleAlert(message: string, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null): void
{
if(!title || !title.length) title = LocalizeText('notifications.broadcast.title');
dispatchUiEvent(new SimpleAlertUIEvent(message, clickUrl, clickUrlText, title, imageUrl));
}
public static alert(title: string, message: string): void
{
dispatchUiEvent(new SimpleAlertUIEvent(message, null, null, title, null));
}
public static showClubGiftNotification(numGifts: number): void public static showClubGiftNotification(numGifts: number): void
{ {
if(numGifts <= 0) return; if(numGifts <= 0) return;
dispatchUiEvent(new NotificationBubbleEvent(numGifts.toString(), NotificationType.CLUBGIFT, null, 'catalog/open/' + CatalogPageName.CLUB_GIFTS)); this.showSingleBubble(numGifts.toString(), NotificationBubbleType.CLUBGIFT, null, ('catalog/open/' + CatalogPageName.CLUB_GIFTS));
} }
public static showModeratorMessage(message: string, url: string = null): void public static handleMOTD(messages: string[]): void
{ {
this.simpleAlert(this.cleanText(message), url, LocalizeText('mod.alert.link'), LocalizeText('mod.alert.title')); messages = messages.map(message => this.cleanText(message));
dispatchUiEvent(new NotificationAlertEvent(messages, NotificationAlertType.MOTD, null, null, LocalizeText('notifications.motd.title')));
}
public static simpleAlert(message: string, type: string, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null): void
{
if(!title || !title.length) title = LocalizeText('notifications.broadcast.title');
dispatchUiEvent(new NotificationAlertEvent([ this.cleanText(message) ], type, clickUrl, clickUrlText, title, imageUrl));
}
public static showModeratorMessage(message: string, url: string = null, showHabboWay: boolean = true): void
{
this.simpleAlert(message, NotificationAlertType.MODERATION, url, LocalizeText('mod.alert.link'), LocalizeText('mod.alert.title'));
}
public static handleModeratorCaution(message: string, url: string = null): void
{
this.showModeratorMessage(message, url);
}
public static handleModeratorMessage(message: string, url: string = null): void
{
this.showModeratorMessage(message, url, false);
}
public static handleUserBannedMessage(message: string): void
{
this.showModeratorMessage(message);
} }
public static handleHotelClosedMessage(open: number, minute: number, thrownOut: boolean): void public static handleHotelClosedMessage(open: number, minute: number, thrownOut: boolean): void
{ {
const text: string = LocalizeText(('opening.hours.' + (thrownOut ? 'disconnected' : 'closed')), [ 'h', 'm'], [ this.getTimeZeroPadded(open), this.getTimeZeroPadded(minute) ]);; this.simpleAlert( LocalizeText(('opening.hours.' + (thrownOut ? 'disconnected' : 'closed')), [ 'h', 'm'], [ this.getTimeZeroPadded(open), this.getTimeZeroPadded(minute) ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
}
this.alert(LocalizeText('opening.hours.title'), text); public static handleHotelMaintenanceMessage(minutesUntilMaintenance: number, duration: number): void
{
this.simpleAlert(LocalizeText('maintenance.shutdown', [ 'm', 'd' ], [ minutesUntilMaintenance.toString(), duration.toString() ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
}
public static handleHotelClosingMessage(minutes: number): void
{
this.simpleAlert(LocalizeText('opening.hours.shutdown', [ 'm' ], [ minutes.toString() ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
}
public static handleLoginFailedHotelClosedMessage(openHour: number, openMinutes: number): void
{
this.simpleAlert(LocalizeText('opening.hours.disconnected', [ 'h', 'm' ], [ openHour.toString(), openMinutes.toString() ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
} }
public static openUrl(url: string): void public static openUrl(url: string): void
@ -134,7 +167,7 @@ export class NotificationUtilities
} }
else else
{ {
CreateLinkEvent(url); CreateLinkEvent(url.substring(6));
} }
} }
@ -153,7 +186,7 @@ export class NotificationUtilities
{ {
if(this.MODERATION_DISCLAIMER_SHOWN) return; if(this.MODERATION_DISCLAIMER_SHOWN) return;
this.showSingleBubble(LocalizeText('mod.chatdisclaimer'), NotificationType.INFO); this.showSingleBubble(LocalizeText('mod.chatdisclaimer'), NotificationBubbleType.INFO);
this.MODERATION_DISCLAIMER_SHOWN = true; this.MODERATION_DISCLAIMER_SHOWN = true;
} }

View File

@ -0,0 +1,59 @@
import { CatalogPageMessageProductData } from '@nitrots/nitro-renderer';
import { GetRoomEngine } from '../../../api';
import { FurniCategory } from '../../catalog/common/FurniCategory';
export class ProductImageUtility
{
public static getProductImageUrl(productType: string, furniClassId: number, extraParam: string): string
{
let imageUrl: string = null;
switch(productType)
{
case CatalogPageMessageProductData.S:
imageUrl = GetRoomEngine().getFurnitureFloorIconUrl(furniClassId);
break;
case CatalogPageMessageProductData.I:
const productCategory = this.getProductCategory(CatalogPageMessageProductData.I, furniClassId);
if(productCategory === 1)
{
imageUrl = GetRoomEngine().getFurnitureWallIconUrl(furniClassId, extraParam);
}
else
{
switch(productCategory)
{
case FurniCategory.WALL_PAPER:
break;
case FurniCategory.LANDSCAPE:
break;
case FurniCategory.FLOOR:
break;
}
}
break;
case CatalogPageMessageProductData.E:
// fx_icon_furniClassId_png
break;
}
return imageUrl;
}
private static getProductCategory(productType: string, furniClassId: number): number
{
if(productType === CatalogPageMessageProductData.S) return 1;
if(productType === CatalogPageMessageProductData.I)
{
if(furniClassId === 3001) return FurniCategory.WALL_PAPER;
if(furniClassId === 3002) return FurniCategory.FLOOR;
if(furniClassId === 4057) return FurniCategory.LANDSCAPE;
}
return 1;
}
}

View File

@ -1,14 +0,0 @@
import { createContext, FC, useContext } from 'react';
import { INotificationCenterContext, NotificationCenterContextProps } from './NotificationCenterContext.types';
const NotificationCenterContext = createContext<INotificationCenterContext>({
notificationCenterState: null,
dispatchNotificationCenterState: null
});
export const NotificationCenterContextProvider: FC<NotificationCenterContextProps> = props =>
{
return <NotificationCenterContext.Provider value={ props.value }>{ props.children }</NotificationCenterContext.Provider>
}
export const useNotificationCenterContext = () => useContext(NotificationCenterContext);

View File

@ -1,13 +0,0 @@
import { Dispatch, ProviderProps } from 'react';
import { INotificationCenterAction, INotificationCenterState } from '../reducers/NotificationCenterReducer';
export interface INotificationCenterContext
{
notificationCenterState: INotificationCenterState;
dispatchNotificationCenterState: Dispatch<INotificationCenterAction>;
}
export interface NotificationCenterContextProps extends ProviderProps<INotificationCenterContext>
{
}

View File

@ -1,77 +0,0 @@
import { Reducer } from 'react';
import { NitroNotification } from '../common/Notification';
export interface INotificationCenterState
{
notifications: NitroNotification[];
}
export interface INotificationCenterAction
{
type: string;
payload: {
id?: number;
notification?: NitroNotification;
};
}
export class NotificationCenterActions
{
public static ADD_NOTIFICATION: string = 'NCA_ADD_NOTIFICATION';
public static REMOVE_NOTIFICATION: string = 'NCA_REMOVE_NOTIFICATION';
public static DISMISS_NOTIFICATION: string = 'NCA_DISMISS_NOTIFICATION';
}
export const initialNotificationCenter: INotificationCenterState = {
notifications: []
}
export const NotificationCenterReducer: Reducer<INotificationCenterState, INotificationCenterAction> = (state, action) =>
{
switch(action.type)
{
case NotificationCenterActions.ADD_NOTIFICATION: {
const notification = (action.payload.notification || null);
if(!notification) return state;
const notifications = [ ...state.notifications, notification ];
return { ...state, notifications };
}
case NotificationCenterActions.REMOVE_NOTIFICATION: {
const id = (action.payload.id || null);
if(!id) return state;
if(!state.notifications) return state;
const notifications = Array.from(state.notifications);
const notificationIndex = notifications.findIndex(notification => notification.id === id);
if(notificationIndex === -1) return state;
notifications.splice(notificationIndex, 1);
return { ...state, notifications };
}
case NotificationCenterActions.DISMISS_NOTIFICATION: {
const id = (action.payload.id || null);
if(!id) return state;
if(!state.notifications) return state;
const notifications = Array.from(state.notifications);
const notificationIndex = notifications.findIndex(notification => notification.id === id);
if(notificationIndex === -1) return state;
notifications[notificationIndex].dismiss();
return { ...state, notifications };
}
default:
return state;
}
}

View File

@ -1,19 +0,0 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { NotificationCenterAlertBaseProps } from './NotificationCenterAlertBase.types';
export const NotificationCenterAlertBase: FC<NotificationCenterAlertBaseProps> = props =>
{
const { headerText = LocalizeText('mod.alert.title'), onClose = null, children = null } = props;
return (
<NitroCardView className="nitro-alert" simple={ true }>
<NitroCardHeaderView headerText={ headerText } onCloseClick={ onClose } />
<NitroCardContentView className="d-flex flex-column justify-content-between text-black">
{ children }
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,5 +0,0 @@
export interface NotificationCenterAlertBaseProps
{
headerText?: string;
onClose: () => void;
}

View File

@ -0,0 +1,19 @@
import { NotificationAlertItem } from '../../common/NotificationAlertItem';
import { NotificationAlertType } from '../../common/NotificationAlertType';
import { NotificationDefaultAlertView } from './default/NotificationDefaultAlertView';
import { NotificationEventAlertView } from './event/NotificationEventAlertView';
export const GetAlertLayout = (item: NotificationAlertItem, close: () => void) =>
{
if(!item) return null;
const props = { key: item.id, item, close };
switch(item.alertType)
{
case NotificationAlertType.EVENT:
return <NotificationEventAlertView { ...props } />
default:
return <NotificationDefaultAlertView { ...props } />
}
}

View File

@ -0,0 +1,7 @@
import { NotificationAlertItem } from '../../common/NotificationAlertItem';
export interface NotificationAlertLayoutViewProps
{
item: NotificationAlertItem;
close: () => void;
}

View File

@ -0,0 +1,35 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../api';
import { NotificationAlertView } from '../../../../../layout';
import { NotificationUtilities } from '../../../common/NotificationUtilities';
import { NotificationDefaultAlertViewProps } from './NotificationDefaultAlertView.types';
export const NotificationDefaultAlertView: FC<NotificationDefaultAlertViewProps> = props =>
{
const { item = null, close = null, ...rest } = props;
const visitUrl = useCallback(() =>
{
NotificationUtilities.openUrl(item.clickUrl);
close();
}, [ item, close ]);
return (
<NotificationAlertView title={ item.title } close={ close } { ...rest }>
{ (item.messages.length > 0) && item.messages.map((message, index) =>
{
const htmlText = message.replace(/\r\n|\r|\n/g, '<br />');
return (
<div key={ index } dangerouslySetInnerHTML={ { __html: htmlText } } />
);
}) }
<div className="d-flex justify-content-center align-items-center mt-1">
<button type="button" className="btn btn-primary" onClick={ close }>{ LocalizeText('generic.close') }</button>
</div>
{ item.clickUrl && item.clickUrl.length &&
<button type="button" className="btn btn-link" onClick={ visitUrl }>{ LocalizeText(item.clickUrlText) }</button> }
</NotificationAlertView>
);
}

View File

@ -0,0 +1,7 @@
import { DetailsHTMLAttributes } from 'react';
import { NotificationAlertLayoutViewProps } from '../NotificationAlertLayoutView.types';
export interface NotificationDefaultAlertViewProps extends NotificationAlertLayoutViewProps, DetailsHTMLAttributes<HTMLDivElement>
{
}

View File

@ -0,0 +1,33 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../api';
import { NotificationAlertView } from '../../../../../layout';
import { NotificationUtilities } from '../../../common/NotificationUtilities';
import { NotificationEventAlertViewProps } from './NotificationEventAlertView.types';
export const NotificationEventAlertView: FC<NotificationEventAlertViewProps> = props =>
{
const { item = null, close = null, ...rest } = props;
const visitUrl = useCallback(() =>
{
NotificationUtilities.openUrl(item.clickUrl);
close();
}, [ item, close ]);
return (
<NotificationAlertView title={ item.title } close={ close } { ...rest }>
{ (item.messages.length > 0) && item.messages.map((message, index) =>
{
const htmlText = message.replace(/\r\n|\r|\n/g, '<br />');
return (
<div key={ index } dangerouslySetInnerHTML={ { __html: htmlText } } />
);
}) }
<div className="d-flex justify-content-center align-items-center mt-1">
<button type="button" className="btn btn-primary" onClick={ visitUrl }>{ LocalizeText(item.clickUrlText) }</button>
</div>
</NotificationAlertView>
);
}

View File

@ -0,0 +1,7 @@
import { DetailsHTMLAttributes } from 'react';
import { NotificationAlertLayoutViewProps } from '../NotificationAlertLayoutView.types';
export interface NotificationEventAlertViewProps extends NotificationAlertLayoutViewProps, DetailsHTMLAttributes<HTMLDivElement>
{
}

View File

@ -1,30 +0,0 @@
import { FC, useMemo } from 'react';
import { LocalizeText } from '../../../../api';
import { NotificationCenterAlertBase } from '../alert-base/NotificationCenterAlertBase';
import { NotificationCenterBroadcastMessageViewProps } from './NotificationCenterBroadcastMessageView.types';
export const NotificationCenterBroadcastMessageView: FC<NotificationCenterBroadcastMessageViewProps> = props =>
{
const { notification = null, onClose = null } = props;
const message = useMemo(() =>
{
let finalMessage = '';
notification.message.forEach(message =>
{
finalMessage += message.replace(/\r\n|\r|\n/g, '<br />');
});
return finalMessage;
}, [ notification ]);
return (
<NotificationCenterAlertBase onClose={ onClose }>
<div dangerouslySetInnerHTML={ { __html: message } } />
<div className="d-flex justify-content-center align-items-center">
<button type="button" className="btn btn-primary" onClick={ onClose }>{ LocalizeText('generic.close') }</button>
</div>
</NotificationCenterAlertBase>
);
};

View File

@ -1,7 +0,0 @@
import { NotificationCenterAlertEvent } from '../../../../events';
export class NotificationCenterBroadcastMessageViewProps
{
notification: NotificationCenterAlertEvent;
onClose: () => void;
}

View File

@ -1,9 +1,9 @@
import { NotificationItem } from '../../common/NotificationItem'; import { NotificationBubbleItem } from '../../common/NotificationBubbleItem';
import { NotificationType } from '../../common/NotificationType'; import { NotificationBubbleType } from '../../common/NotificationBubbleType';
import { NotificationClubGiftBubbleView } from './club-gift/NotificationClubGiftBubbleView'; import { NotificationClubGiftBubbleView } from './club-gift/NotificationClubGiftBubbleView';
import { NotificationDefaultBubbleView } from './default/NotificationDefaultBubbleView'; import { NotificationDefaultBubbleView } from './default/NotificationDefaultBubbleView';
export const GetBubbleLayout = (item: NotificationItem, close: () => void) => export const GetBubbleLayout = (item: NotificationBubbleItem, close: () => void) =>
{ {
if(!item) return null; if(!item) return null;
@ -11,7 +11,7 @@ export const GetBubbleLayout = (item: NotificationItem, close: () => void) =>
switch(item.notificationType) switch(item.notificationType)
{ {
case NotificationType.CLUBGIFT: case NotificationBubbleType.CLUBGIFT:
return <NotificationClubGiftBubbleView { ...props } /> return <NotificationClubGiftBubbleView { ...props } />
default: default:
return <NotificationDefaultBubbleView { ...props } /> return <NotificationDefaultBubbleView { ...props } />

View File

@ -1,7 +1,7 @@
import { NotificationItem } from '../../common/NotificationItem'; import { NotificationBubbleItem } from '../../common/NotificationBubbleItem';
export interface NotificationBubbleLayoutViewProps export interface NotificationBubbleLayoutViewProps
{ {
item: NotificationItem; item: NotificationBubbleItem;
close: () => void; close: () => void;
} }

View File

@ -1,28 +0,0 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { NotificationTrayItemView } from '../tray-item/NotificationTrayItemView';
import { HotelWillShutdownViewProps } from './HotelWillShutdownView.types';
export const HotelWillShutdownView: FC<HotelWillShutdownViewProps> = props =>
{
const { notification = null, inTray = null, onButtonClick = null } = props;
if(!notification) return null;
const content = <>{ LocalizeText('opening.hours.shutdown', ['m'], [notification.minutes.toString()]) }</>;
if(inTray)
return (
<NotificationTrayItemView title={ LocalizeText('mod.alert.title') } content={ content } timestamp={ notification.timestamp } onCloseClick={ () => onButtonClick('remove_notification') } />
);
return (
<NitroCardView className="nitro-notification" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('mod.alert.title') } onCloseClick={ () => onButtonClick('dismiss_notification') } />
<NitroCardContentView className="text-black">
{ content }
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -1,7 +0,0 @@
import { HotelWillShutdownNotification } from '../../common/HotelWillShutdownNotification';
import { NotificationViewProps } from '../../NotificationCenterView.types';
export class HotelWillShutdownViewProps extends NotificationViewProps
{
notification: HotelWillShutdownNotification;
}

View File

@ -1,33 +0,0 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { NotificationTrayItemView } from '../tray-item/NotificationTrayItemView';
import { ModeratorMessageViewProps } from './ModeratorMessageView.types';
export const ModeratorMessageView: FC<ModeratorMessageViewProps> = props =>
{
const { notification = null, inTray = null, onButtonClick = null } = props;
if(!notification) return null;
const content = <>
<div dangerouslySetInnerHTML={ { __html: notification.message } } />
<div className="fw-bold text-center">
<a href={ notification.link } rel="noreferrer" target="_blank" onClick={ () => onButtonClick('dismiss_notification') }>{ LocalizeText('mod.alert.link') }</a>
</div>
</>;
if(inTray)
return (
<NotificationTrayItemView title={ LocalizeText('mod.alert.title') } content={ content } timestamp={ notification.timestamp } onCloseClick={ () => onButtonClick('remove_notification') } />
);
return (
<NitroCardView className="nitro-notification" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('mod.alert.title') } onCloseClick={ () => onButtonClick('dismiss_notification') } />
<NitroCardContentView className="text-black">
{ content }
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -1,7 +0,0 @@
import { ModeratorMessageNotification } from '../../common/ModeratorMessageNotification';
import { NotificationViewProps } from '../../NotificationCenterView.types';
export class ModeratorMessageViewProps extends NotificationViewProps
{
notification: ModeratorMessageNotification;
}

View File

@ -1,10 +0,0 @@
import { FC } from 'react';
import { NotificationCenterBroadcastMessageView } from '../broadcast-message/NotificationCenterBroadcastMessageView';
import { NotificationCenterMotdViewProps } from './NotificationCenterMotdView.types';
export const NotificationCenterMotdView: FC<NotificationCenterMotdViewProps> = props =>
{
const { notification = null, onClose = null } = props;
return <NotificationCenterBroadcastMessageView notification={ notification } onClose={ onClose } />;
}

View File

@ -1,7 +0,0 @@
import { NotificationCenterAlertEvent } from '../../../../events';
export interface NotificationCenterMotdViewProps
{
notification: NotificationCenterAlertEvent;
onClose: () => void;
}

View File

@ -1,22 +0,0 @@
import { FC } from 'react';
import { NotificationTrayItemViewProps } from './NotificationTrayItemView.types';
export const NotificationTrayItemView: FC<NotificationTrayItemViewProps> = props =>
{
const { title = null, content = null, timestamp = null, onCloseClick = null } = props;
return (
<div className="rounded bg-muted mb-2 text-black">
<div className="py-1 px-2 fw-bold d-flex justify-content-between align-items-center">
<div className="me-2">{ title }</div>
<i className="fas fa-times cursor-pointer" onClick={ onCloseClick }></i>
</div>
<div className="py-1 px-2">
{ content }
</div>
<div className="py-1 px-2">
<i className="far fa-clock"></i> { timestamp }
</div>
</div>
);
};

View File

@ -1,7 +0,0 @@
export class NotificationTrayItemViewProps
{
title: string;
content: any;
timestamp: number;
onCloseClick: () => void;
}

View File

@ -260,11 +260,18 @@ export const ChatInputView: FC<{}> = props =>
} }
}, [ onKeyDownEvent ]); }, [ onKeyDownEvent ]);
useEffect(() =>
{
if(!inputRef.current) return;
inputRef.current.parentElement.dataset.value = chatValue;
}, [ chatValue ]);
return ( return (
createPortal( createPortal(
<div className="nitro-chat-input-container"> <div className="nitro-chat-input-container">
<div className="input-sizer"> <div className="input-sizer">
<input ref={ inputRef } type="text" className="chat-input" placeholder={ LocalizeText('widgets.chatinput.default') } value={ chatValue } maxLength={ maxChatLength } onChange={ event => { event.target.parentElement.dataset.value = event.target.value; updateChatInput(event.target.value) } } onMouseDown={ event => setInputFocus() } /> <input ref={ inputRef } type="text" className="chat-input" placeholder={ LocalizeText('widgets.chatinput.default') } value={ chatValue } maxLength={ maxChatLength } onChange={ event => updateChatInput(event.target.value) } onMouseDown={ event => setInputFocus() } />
</div> </div>
<ChatInputStyleSelectorView onStyleSelected={ onStyleSelected } /> <ChatInputStyleSelectorView onStyleSelected={ onStyleSelected } />
</div>, document.getElementById('toolbar-chat-input-container')) </div>, document.getElementById('toolbar-chat-input-container'))

View File

@ -55,16 +55,8 @@ export const FurnitureDimmerView: FC<{}> = props =>
BatchUpdates(() => BatchUpdates(() =>
{ {
let prevDimmerState = 0; setLastDimmerState(dimmerState);
setDimmerState(widgetEvent.state);
setDimmerState(prevValue =>
{
setLastDimmerState(prevValue);
return widgetEvent.state;
});
setLastDimmerState(prevDimmerState);
setSelectedPresetId(widgetEvent.presetId); setSelectedPresetId(widgetEvent.presetId);
setEffectId(widgetEvent.effectId); setEffectId(widgetEvent.effectId);
setSelectedEffectId(widgetEvent.effectId); setSelectedEffectId(widgetEvent.effectId);
@ -73,10 +65,11 @@ export const FurnitureDimmerView: FC<{}> = props =>
setBrightness(widgetEvent.brightness); setBrightness(widgetEvent.brightness);
setSelectedBrightness(widgetEvent.brightness); setSelectedBrightness(widgetEvent.brightness);
}); });
return; return;
} }
} }
}, []); }, [ dimmerState ]);
CreateEventDispatcherHook(RoomWidgetUpdateDimmerEvent.PRESETS, eventDispatcher, onNitroEvent); CreateEventDispatcherHook(RoomWidgetUpdateDimmerEvent.PRESETS, eventDispatcher, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetUpdateDimmerEvent.HIDE, eventDispatcher, onNitroEvent); CreateEventDispatcherHook(RoomWidgetUpdateDimmerEvent.HIDE, eventDispatcher, onNitroEvent);
@ -144,6 +137,8 @@ export const FurnitureDimmerView: FC<{}> = props =>
{ {
if((dimmerState === 0) && (lastDimmerState === 0)) return; if((dimmerState === 0) && (lastDimmerState === 0)) return;
console.log('ye')
widgetHandler.processWidgetMessage(new RoomWidgetDimmerPreviewMessage(selectedColor, selectedBrightness, (selectedEffectId === 2))); widgetHandler.processWidgetMessage(new RoomWidgetDimmerPreviewMessage(selectedColor, selectedBrightness, (selectedEffectId === 2)));
}, [ widgetHandler, dimmerState, lastDimmerState, selectedColor, selectedBrightness, selectedEffectId ]); }, [ widgetHandler, dimmerState, lastDimmerState, selectedColor, selectedBrightness, selectedEffectId ]);