Notification center updates

This commit is contained in:
Bill 2021-08-22 01:00:05 -04:00
parent 6ffeb3d340
commit 27d7083440
12 changed files with 295 additions and 20 deletions

View File

@ -0,0 +1,41 @@
import { NitroEvent } from '@nitrots/nitro-renderer';
export class NotificationBubbleEvent extends NitroEvent
{
public static NEW_BUBBLE: string = 'NNBE_NEW_BUBBLE';
private _message: string;
private _notificationType: string;
private _imageUrl: string;
private _linkUrl: string;
constructor(message: string, notificationType: string, imageUrl: string, linkUrl: string)
{
super(NotificationBubbleEvent.NEW_BUBBLE);
this._message = message;
this._notificationType = notificationType;
this._imageUrl = imageUrl;
this._linkUrl = linkUrl;
}
public get message(): string
{
return this._message;
}
public get notificationType(): string
{
return this._notificationType;
}
public get imageUrl(): string
{
return this._imageUrl;
}
public get linkUrl(): string
{
return this._linkUrl;
}
}

View File

@ -10,7 +10,6 @@ import { HotelView } from '../hotel-view/HotelView';
import { InventoryView } from '../inventory/InventoryView';
import { ModToolsView } from '../mod-tools/ModToolsView';
import { NavigatorView } from '../navigator/NavigatorView';
import { NotificationCenterView } from '../notification-center/NotificationCenterView';
import { RightSideView } from '../right-side/RightSideView';
import { RoomHostView } from '../room-host/RoomHostView';
import { ToolbarView } from '../toolbar/ToolbarView';
@ -61,7 +60,6 @@ export const MainView: FC<MainViewProps> = props =>
<CatalogView />
<FriendListView />
<RightSideView />
<NotificationCenterView />
<UserSettingsView />
<UserProfileView />
</div>

View File

@ -1,10 +1,12 @@
import { HabboBroadcastMessageEvent, HotelWillShutdownEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent } from '@nitrots/nitro-renderer';
import { AchievementNotificationMessageEvent, HabboBroadcastMessageEvent, HotelWillShutdownEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent, RespectReceivedEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { GetSessionDataManager, LocalizeBadgeName, LocalizeText } from '../../api';
import { NotificationCenterAlertEvent } from '../../events';
import { dispatchUiEvent } from '../../hooks/events';
import { CreateMessageHook } from '../../hooks/messages';
import { DialogMessageNotification } from './common/DialogMessageNotification';
import { HotelWillShutdownNotification } from './common/HotelWillShutdownNotification';
import { NotificationType } from './common/NotificationType';
import { NotificationUtilities } from './common/NotificationUtilities';
import { useNotificationCenterContext } from './context/NotificationCenterContext';
import { INotificationCenterMessageHandlerProps } from './NotificationCenterMessageHandler.types';
import { NotificationCenterActions } from './reducers/NotificationCenterReducer';
@ -50,21 +52,41 @@ export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHand
{
const parser = event.getParser();
console.log(parser);
NotificationUtilities.showNotification(parser.type, parser.parameters);
}, []);
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new DialogMessageNotification(parser.type, parser.parameters)
}
});
}, [ dispatchNotificationCenterState ]);
const onRespectReceivedEvent = useCallback((event: RespectReceivedEvent) =>
{
const parser = event.getParser();
if(parser.userId !== GetSessionDataManager().userId) return;
const text1 = LocalizeText('notifications.text.respect.1');
const text2 = LocalizeText('notifications.text.respect.2', [ 'count' ], [ parser.respectsReceived.toString() ]);
NotificationUtilities.showSingleBubble(text1, NotificationType.RESPECT);
NotificationUtilities.showSingleBubble(text2, NotificationType.RESPECT);
}, []);
const onAchievementNotificationMessageEvent = useCallback((event: AchievementNotificationMessageEvent) =>
{
const parser = event.getParser();
const text1 = LocalizeText('achievements.levelup.desc');
const badgeName = LocalizeBadgeName(parser.data.badgeCode);
const badgeImage = GetSessionDataManager().getBadgeUrl(parser.data.badgeCode);
const internalLink = 'questengine/achievements/' + parser.data.category;
NotificationUtilities.showSingleBubble((text1 + ' ' + badgeName), NotificationType.ACHIEVEMENT, badgeImage, internalLink);
}, []);
CreateMessageHook(HabboBroadcastMessageEvent, onHabboBroadcastMessageEvent);
CreateMessageHook(ModeratorMessageEvent, onModeratorMessageEvent);
CreateMessageHook(MOTDNotificationEvent, onMOTDNotificationEvent);
CreateMessageHook(HotelWillShutdownEvent, onHotelWillShutdownEvent);
CreateMessageHook(NotificationDialogMessageEvent, onNotificationDialogMessageEvent);
CreateMessageHook(RespectReceivedEvent, onRespectReceivedEvent);
CreateMessageHook(AchievementNotificationMessageEvent, onAchievementNotificationMessageEvent);
return null;
}

View File

@ -7,6 +7,18 @@
}
}
.nitro-notification-center {
.notification-bubble {
pointer-events: all;
padding: 2px;
background-color: $gable-green;
border: 2px solid rgba($white, 0.5);
font-size: $font-size-sm;
margin-bottom: 5px;
}
}
.nitro-notification-center-container {
position: absolute;
top: 0;

View File

@ -1,15 +1,17 @@
import { FC, useCallback, useState } from 'react';
import { FC, ReactNode, useCallback, useMemo, useState } from 'react';
import { NotificationCenterAlertEvent } from '../../events';
import { NotificationBubbleEvent } from '../../events/notification-center/NotificationBubbleEvent';
import { useUiEvent } from '../../hooks/events';
import { NotificationItem } from './common/NotificationItem';
import { NotificationCenterMessageHandler } from './NotificationCenterMessageHandler';
import { NotificationCenterViewProps } from './NotificationCenterView.types';
import { NotificationCenterBroadcastMessageView } from './views/broadcast-message/NotificationCenterBroadcastMessageView';
import { NotificationBubbleView } from './views/notification-bubble/NotificationBubbleView';
export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
{
const [ alerts, setAlerts ] = useState<NotificationCenterAlertEvent[]>([]);
const [ bubbleAlerts, setBubbleAlerts ] = useState<NotificationItem[]>([]);
const onNotificationCenterAlertEvent = useCallback((event: NotificationCenterAlertEvent) =>
{
@ -21,6 +23,16 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
useUiEvent(NotificationCenterAlertEvent.HOTEL_ALERT, onNotificationCenterAlertEvent);
const onNotificationBubbleEvent = useCallback((event: NotificationBubbleEvent) =>
{
console.log(event);
const notificationItem = new NotificationItem(event.message, event.notificationType, null, event.linkUrl);
setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]);
}, []);
useUiEvent(NotificationBubbleEvent.NEW_BUBBLE, onNotificationBubbleEvent);
const closeAlert = useCallback((alert: NotificationCenterAlertEvent) =>
{
setAlerts(prevValue =>
@ -34,9 +46,36 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
});
}, []);
const closeBubbleAlert = useCallback((item: NotificationItem) =>
{
setBubbleAlerts(prevValue =>
{
const newAlerts = [ ...prevValue ];
const index = newAlerts.findIndex(value => (item === value));
if(index >= 0) newAlerts.splice(index, 1);
return newAlerts;
})
}, []);
const getBubbleAlerts = useMemo(() =>
{
if(!bubbleAlerts || !bubbleAlerts.length) return null;
const elements: ReactNode[] = [];
for(const alert of bubbleAlerts) elements.push(<NotificationBubbleView key={ alert.id } notificationItem={ alert } close={ () => closeBubbleAlert(alert) } />);
return elements;
}, [ bubbleAlerts, closeBubbleAlert ]);
return (
<>
<NotificationCenterMessageHandler />
<div className="nitro-notification-center">
{ getBubbleAlerts }
</div>
{ (alerts.length > 0) && alerts.map((alert, index) =>
{
switch(alert.type)

View File

@ -0,0 +1,48 @@
import { NotificationType } from './NotificationType';
export class NotificationItem
{
private static ITEM_ID: number = -1;
private _id: number;
private _message: string;
private _notificationType: string;
private _iconUrl: string;
private _linkUrl: string;
constructor(message: string, notificationType: string = NotificationType.INFO, iconUrl: string = null, linkUrl: string = null)
{
NotificationItem.ITEM_ID += 1;
this._id = NotificationItem.ITEM_ID;
this._message = message;
this._notificationType = notificationType;
this._iconUrl = iconUrl;
this._linkUrl = linkUrl;
}
public get id(): number
{
return this._id;
}
public get message(): string
{
return this._message;
}
public get notificationType(): string
{
return this._notificationType;
}
public get iconUrl(): string
{
return this._iconUrl;
}
public get linkUrl(): string
{
return this._linkUrl;
}
}

View File

@ -0,0 +1,19 @@
export class NotificationType
{
public static FRIENDOFFLINE: string = 'friendoffline';
public static FRIENDONLINE: string = 'friendonline';
public static THIRDPARTYFRIENDOFFLINE: string = 'thirdpartyfriendoffline';
public static THIRDPARTYFRIENDONLINE: string = 'thirdpartyfriendonline';
public static ACHIEVEMENT: string = 'achievement';
public static BADGE_RECEIVED: string = 'badge_received';
public static INFO: string = 'info';
public static RECYCLEROK: string = 'recyclerok';
public static RESPECT: string = 'respect';
public static CLUB: string = 'club';
public static SOUNDMACHINE: string = 'soundmachine';
public static PETLEVEL: string = 'petlevel';
public static CLUBGIFT: string = 'clubgift';
public static BUYFURNI: string = 'buyfurni';
public static VIP: string = 'vip';
public static ROOMMESSAGESPOSTED: string = 'roommessagesposted';
}

View File

@ -0,0 +1,78 @@
import { GetConfiguration, GetNitroInstance, LocalizeText } from '../../../api';
import { NotificationBubbleEvent } from '../../../events/notification-center/NotificationBubbleEvent';
import { dispatchUiEvent } from '../../../hooks';
import { NotificationType } from './NotificationType';
export class NotificationUtilities
{
private static getMainNotificationConfig(): { [key: string]: { delivery?: string, display?: string; title?: string; image?: string }}
{
return GetConfiguration<{ [key: string]: { delivery?: string, display?: string; title?: string; image?: string }}>('notification', {});
}
private static getNotificationConfig(key: string): { delivery?: string, display?: string; title?: string; image?: string }
{
const mainConfig = this.getMainNotificationConfig();
if(!mainConfig) return null;
return mainConfig[key];
}
public static getNotificationPart(options: Map<string, string>, type: string, key: string, localize: boolean): string
{
if(options.has(key)) return options.get(key);
const localizeKey = [ 'notification', type, key ].join('.');
if(GetNitroInstance().localization.hasValue(localizeKey) || localize)
{
return LocalizeText(localizeKey, Array.from(options.keys()), Array.from(options.values()));
}
return null;
}
public static getNotificationImageUrl(options: Map<string, string>, type: string): string
{
let imageUrl = options.get('image');
// eslint-disable-next-line no-template-curly-in-string
if(!imageUrl) imageUrl = ('${image.library.url}notifications/' + type.replace(/\./g, '_') + '.png');
return imageUrl;
}
public static showNotification(type: string, options: Map<string, string> = null): void
{
if(!options) options = new Map();
const configuration = this.getNotificationConfig(('notification.' + type));
if(configuration)
{
for(const key in configuration) options.set(key, configuration[key]);
}
console.log(options);
if(options.get('display') === 'BUBBLE')
{
const message = this.getNotificationPart(options, type, 'message', true);
const linkUrl = this.getNotificationPart(options, type, 'linkUrl', false);
const isEventLink = (linkUrl && linkUrl.substr(0, 6) === 'event');
const image = this.getNotificationImageUrl(options, type);
dispatchUiEvent(new NotificationBubbleEvent(message, NotificationType.INFO, image, (isEventLink ? linkUrl.substr(6) : linkUrl)));
}
else
{
}
}
public static showSingleBubble(message: string, type: string, imageUrl: string = null, internalLink: string = null): void
{
dispatchUiEvent(new NotificationBubbleEvent(message, type, imageUrl, internalLink));
}
}

View File

@ -1,7 +1,19 @@
import { FC } from 'react';
import { FC, useMemo } from 'react';
import { LocalizeText } from '../../../../api';
import { NotificationBubbleViewProps } from './NotificationBubbleView.types';
export const NotificationBubbleView: FC<NotificationBubbleViewProps> = props =>
{
return null;
const { notificationItem = null, close = null } = props;
const message = useMemo(() =>
{
return LocalizeText(notificationItem.message);
}, [ notificationItem ]);
return (
<div className="notification-bubble">
{ message }
</div>
);
}

View File

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

View File

@ -3,7 +3,8 @@
top: 0px;
right: 10px;
min-width: 200px;
max-width: 400px;
max-width: 200px;
height: calc(100% - #{$toolbar-height});
z-index: $rightside-zindex;
pointer-events: none;
}

View File

@ -1,4 +1,5 @@
import { FC } from 'react';
import { NotificationCenterView } from '../notification-center/NotificationCenterView';
import { PurseView } from '../purse/PurseView';
import { RightSideProps } from './RightSideView.types';
@ -6,8 +7,9 @@ export const RightSideView: FC<RightSideProps> = props =>
{
return (
<div className="nitro-right-side">
<div className="d-flex flex-column">
<div className="position-relative d-flex flex-column">
<PurseView />
<NotificationCenterView />
</div>
</div>
);