mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-22 22:30:52 +01:00
Messenger updates
This commit is contained in:
parent
e97f18c53c
commit
6a28213896
@ -1,6 +1,7 @@
|
||||
import { CreateLinkEvent } from '..';
|
||||
|
||||
export function OpenMessengerChat(friendId: number): void
|
||||
export function OpenMessengerChat(friendId: number = -1): void
|
||||
{
|
||||
CreateLinkEvent(`friends/messenger/${friendId}`);
|
||||
if(friendId === -1) CreateLinkEvent('friends/messenger/open');
|
||||
else CreateLinkEvent(`friends/messenger/${friendId}`);
|
||||
}
|
||||
|
23
src/events/friends/FriendsMessengerIconEvent.ts
Normal file
23
src/events/friends/FriendsMessengerIconEvent.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { NitroEvent } from '@nitrots/nitro-renderer';
|
||||
|
||||
export class FriendsMessengerIconEvent extends NitroEvent
|
||||
{
|
||||
public static UPDATE_ICON: string = 'FMIE_UPDATE_ICON';
|
||||
public static HIDE_ICON: number = 0;
|
||||
public static SHOW_ICON: number = 1;
|
||||
public static UNREAD_ICON: number = 2;
|
||||
|
||||
private _iconType: number;
|
||||
|
||||
constructor(type: string, subType: number = FriendsMessengerIconEvent.SHOW_ICON)
|
||||
{
|
||||
super(type);
|
||||
|
||||
this._iconType = subType;
|
||||
}
|
||||
|
||||
public get iconType(): number
|
||||
{
|
||||
return this._iconType;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export * from './FriendEnteredRoomEvent';
|
||||
export * from './FriendListContentEvent';
|
||||
export * from './FriendsEvent';
|
||||
export * from './FriendsMessengerIconEvent';
|
||||
export * from './FriendsSendFriendRequestEvent';
|
||||
|
@ -1,4 +1,5 @@
|
||||
.content-area {
|
||||
height: 100%;
|
||||
padding-top: $container-padding-x;
|
||||
padding-bottom: $container-padding-x;
|
||||
overflow: auto;
|
||||
|
30
src/views/catalog/common/AttemptCatalogPlacement.ts
Normal file
30
src/views/catalog/common/AttemptCatalogPlacement.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { CatalogPageMessageOfferData, RoomObjectCategory, RoomObjectPlacementSource } from '@nitrots/nitro-renderer';
|
||||
import { GetRoomEngine } from '../../../api';
|
||||
import { IsCatalogOfferDraggable } from './IsCatalogOfferDraggable';
|
||||
import { ProductTypeEnum } from './ProductTypeEnum';
|
||||
|
||||
export const AttemptCatalogPlacement = (offer: CatalogPageMessageOfferData) =>
|
||||
{
|
||||
if(!IsCatalogOfferDraggable(offer)) return;
|
||||
|
||||
const product = offer.products[0];
|
||||
|
||||
let category: number = -1;
|
||||
|
||||
switch(product.productType)
|
||||
{
|
||||
case ProductTypeEnum.FLOOR:
|
||||
category = RoomObjectCategory.FLOOR;
|
||||
break;
|
||||
case ProductTypeEnum.WALL:
|
||||
category = RoomObjectCategory.WALL;
|
||||
break;
|
||||
}
|
||||
|
||||
if(category === -1) return;
|
||||
|
||||
if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.CATALOG, -(offer.offerId), category, product.furniClassId, (product.extraParam) ? product.extraParam.toString() : null))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
8
src/views/catalog/common/IsCatalogOfferDraggable.ts
Normal file
8
src/views/catalog/common/IsCatalogOfferDraggable.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { CatalogPageMessageOfferData, RoomControllerLevel } from '@nitrots/nitro-renderer';
|
||||
import { GetRoomSession } from '../../../api';
|
||||
import { ProductTypeEnum } from './ProductTypeEnum';
|
||||
|
||||
export const IsCatalogOfferDraggable = (offer: CatalogPageMessageOfferData) =>
|
||||
{
|
||||
return ((GetRoomSession().isRoomOwner || (GetRoomSession().isGuildRoom && (GetRoomSession().controllerLevel >= RoomControllerLevel.GUILD_MEMBER))) && (offer.products.length === 1) && (offer.products[0].productType !== ProductTypeEnum.EFFECT) && (offer.products[0].productType !== ProductTypeEnum.HABBO_CLUB))
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { MouseEventType } from '@nitrots/nitro-renderer';
|
||||
import { FC, MouseEvent, useCallback } from 'react';
|
||||
import { FC, MouseEvent, useCallback, useState } from 'react';
|
||||
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||
import { CatalogActions } from '../../../reducers/CatalogReducer';
|
||||
import { CatalogProductView } from '../product/CatalogProductView';
|
||||
@ -8,6 +8,7 @@ import { CatalogPageOfferViewProps } from './CatalogPageOfferView.types';
|
||||
export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
||||
{
|
||||
const { isActive = false, offer = null } = props;
|
||||
const [ isMouseDown, setMouseDown ] = useState(false);
|
||||
const { dispatchCatalogState = null } = useCatalogContext();
|
||||
|
||||
const onMouseEvent = useCallback((event: MouseEvent) =>
|
||||
@ -24,8 +25,18 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
||||
}
|
||||
});
|
||||
return;
|
||||
case MouseEventType.MOUSE_DOWN:
|
||||
console.log('ye')
|
||||
setMouseDown(true);
|
||||
return;
|
||||
case MouseEventType.MOUSE_UP:
|
||||
setMouseDown(false);
|
||||
return;
|
||||
case MouseEventType.ROLL_OUT:
|
||||
if(!isMouseDown || !isActive) return;
|
||||
return;
|
||||
}
|
||||
}, [ isActive, offer, dispatchCatalogState ]);
|
||||
}, [ isActive, offer, isMouseDown, dispatchCatalogState ]);
|
||||
|
||||
const product = ((offer.products && offer.products[0]) || null);
|
||||
|
||||
|
@ -65,7 +65,7 @@ export const CatalogProductView: FC<CatalogProductViewProps> = props =>
|
||||
|
||||
return (
|
||||
<div className="col pe-1 pb-1 catalog-offer-item-container">
|
||||
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + (isActive ? 'active ' : '') + (product.uniqueLimitedItem ? 'unique-item ' : '') + ((product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft) ? 'sold-out ' : '') } style={ { backgroundImage: imageUrl }} onClick={ onMouseEvent }>
|
||||
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + (isActive ? 'active ' : '') + (product.uniqueLimitedItem ? 'unique-item ' : '') + ((product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft) ? 'sold-out ' : '') } style={ { backgroundImage: imageUrl }} onClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
|
||||
{ !imageUrl && (product.productType === ProductTypeEnum.ROBOT) &&
|
||||
<AvatarImageView figure={ product.extraParam } direction={ 3 } headOnly={ true } /> }
|
||||
{ (product.productCount > 1) && <span className="position-absolute badge border bg-danger px-1 rounded-circle">{ product.productCount }</span> }
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { FriendListFragmentEvent, FriendListUpdateEvent, FriendRequestsEvent, GetFriendRequestsComposer, MessengerInitEvent, NewConsoleMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { FriendListFragmentEvent, FriendListUpdateEvent, FriendRequestsEvent, GetFriendRequestsComposer, MessengerInitEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { GetSessionDataManager } from '../../api';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
||||
import { MessengerChatMessage } from './common/MessengerChatMessage';
|
||||
import { MessengerSettings } from './common/MessengerSettings';
|
||||
import { useFriendsContext } from './context/FriendsContext';
|
||||
import { FriendsActions } from './reducers/FriendsReducer';
|
||||
@ -10,7 +8,6 @@ import { FriendsActions } from './reducers/FriendsReducer';
|
||||
export const FriendsMessageHandler: FC<{}> = props =>
|
||||
{
|
||||
const { friendsState = null, dispatchFriendsState = null } = useFriendsContext();
|
||||
const { activeChats = [] } = friendsState;
|
||||
|
||||
const onMessengerInitEvent = useCallback((event: MessengerInitEvent) =>
|
||||
{
|
||||
@ -66,28 +63,10 @@ export const FriendsMessageHandler: FC<{}> = props =>
|
||||
});
|
||||
}, [ dispatchFriendsState ]);
|
||||
|
||||
const onNewConsoleMessageEvent = useCallback((event: NewConsoleMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
let userId = parser.senderId;
|
||||
|
||||
if(userId === GetSessionDataManager().userId) userId = 0;
|
||||
|
||||
dispatchFriendsState({
|
||||
type: FriendsActions.ADD_CHAT_MESSAGE,
|
||||
payload: {
|
||||
chatMessage: new MessengerChatMessage(MessengerChatMessage.MESSAGE, userId, parser.messageText, parser.secondsSinceSent, parser.extraData),
|
||||
boolValue: true
|
||||
}
|
||||
});
|
||||
}, [ dispatchFriendsState ]);
|
||||
|
||||
CreateMessageHook(MessengerInitEvent, onMessengerInitEvent);
|
||||
CreateMessageHook(FriendListFragmentEvent, onFriendsFragmentEvent);
|
||||
CreateMessageHook(FriendListUpdateEvent, onFriendsUpdateEvent);
|
||||
CreateMessageHook(FriendRequestsEvent, onFriendRequestsEvent);
|
||||
CreateMessageHook(NewConsoleMessageEvent, onNewConsoleMessageEvent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
import { MessengerChatMessage } from './MessengerChatMessage';
|
||||
import { MessengerChatMessageGroup } from './MessengerChatMessageGroup';
|
||||
export class MessengerChat
|
||||
{
|
||||
private _friendId: number;
|
||||
private _isRead: boolean;
|
||||
private _messageGroups: MessengerChatMessageGroup[];
|
||||
|
||||
constructor(friendId: number)
|
||||
{
|
||||
this._friendId = friendId;
|
||||
this._isRead = true;
|
||||
this._messageGroups = [];
|
||||
}
|
||||
|
||||
public addMessage(message: MessengerChatMessage, setAsNotRead: boolean = true, isSystem: boolean = false): void
|
||||
{
|
||||
if(!this.lastMessageGroup || this.lastMessageGroup.userId !== message.senderId || isSystem || this.lastMessageGroup.isSystem) this._messageGroups.push(new MessengerChatMessageGroup(message.senderId, isSystem));
|
||||
|
||||
this.lastMessageGroup.addMessage(message);
|
||||
|
||||
if(setAsNotRead) this._isRead = false;
|
||||
}
|
||||
|
||||
public read(): void
|
||||
{
|
||||
this._isRead = true;
|
||||
}
|
||||
|
||||
public get friendId(): number
|
||||
{
|
||||
return this._friendId;
|
||||
}
|
||||
|
||||
public get isRead(): boolean
|
||||
{
|
||||
return this._isRead;
|
||||
}
|
||||
|
||||
public get messageGroups(): MessengerChatMessageGroup[]
|
||||
{
|
||||
return this._messageGroups;
|
||||
}
|
||||
|
||||
public get lastMessageGroup(): MessengerChatMessageGroup
|
||||
{
|
||||
return this._messageGroups[this._messageGroups.length - 1];
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { MessengerChatMessage } from './MessengerChatMessage';
|
||||
|
||||
export class MessengerChatMessageGroup
|
||||
{
|
||||
private _userId: number;
|
||||
private _messages: MessengerChatMessage[];
|
||||
private _isSystem: boolean;
|
||||
|
||||
constructor(userId: number, isSystem: boolean)
|
||||
{
|
||||
this._userId = userId;
|
||||
this._messages = [];
|
||||
this._isSystem = isSystem;
|
||||
}
|
||||
|
||||
public addMessage(message: MessengerChatMessage): void
|
||||
{
|
||||
this._messages.push(message);
|
||||
}
|
||||
|
||||
public get userId(): number
|
||||
{
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
public get messages(): MessengerChatMessage[]
|
||||
{
|
||||
return this._messages;
|
||||
}
|
||||
|
||||
public get isSystem(): boolean
|
||||
{
|
||||
return this._isSystem;
|
||||
}
|
||||
}
|
83
src/views/friends/common/MessengerThread.ts
Normal file
83
src/views/friends/common/MessengerThread.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { MessengerFriend } from './MessengerFriend';
|
||||
import { MessengerThreadChat } from './MessengerThreadChat';
|
||||
import { MessengerThreadChatGroup } from './MessengerThreadChatGroup';
|
||||
|
||||
export class MessengerThread
|
||||
{
|
||||
public static MESSAGE_RECEIVED: string = 'MT_MESSAGE_RECEIVED';
|
||||
|
||||
private _participant: MessengerFriend;
|
||||
private _groups: MessengerThreadChatGroup[];
|
||||
private _lastUpdated: Date;
|
||||
private _unread: boolean;
|
||||
|
||||
constructor(participant: MessengerFriend, isNew: boolean = true)
|
||||
{
|
||||
this._participant = participant;
|
||||
this._groups = [];
|
||||
this._lastUpdated = new Date();
|
||||
this._unread = false;
|
||||
|
||||
if(isNew)
|
||||
{
|
||||
this.addMessage(-1, LocalizeText('messenger.moderationinfo'), 0, null, MessengerThreadChat.SECURITY_NOTIFICATION);
|
||||
|
||||
this._unread = false;
|
||||
}
|
||||
}
|
||||
|
||||
public addMessage(senderId: number, message: string, secondsSinceSent: number = 0, extraData: string = null, type: number = 0): MessengerThreadChat
|
||||
{
|
||||
const group = this.getLastGroup(senderId);
|
||||
|
||||
if(!group) return;
|
||||
|
||||
const chat = new MessengerThreadChat(senderId, message, secondsSinceSent, extraData, type);
|
||||
|
||||
group.addChat(chat);
|
||||
|
||||
this._lastUpdated = new Date();
|
||||
this._unread = true;
|
||||
|
||||
return chat;
|
||||
}
|
||||
|
||||
private getLastGroup(userId: number): MessengerThreadChatGroup
|
||||
{
|
||||
let group = this._groups[(this._groups.length - 1)];
|
||||
|
||||
if(group && (group.userId === userId)) return group;
|
||||
|
||||
group = new MessengerThreadChatGroup(userId);
|
||||
|
||||
this._groups.push(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public setRead(): void
|
||||
{
|
||||
this._unread = false;
|
||||
}
|
||||
|
||||
public get participant(): MessengerFriend
|
||||
{
|
||||
return this._participant;
|
||||
}
|
||||
|
||||
public get groups(): MessengerThreadChatGroup[]
|
||||
{
|
||||
return this._groups;
|
||||
}
|
||||
|
||||
public get lastUpdated(): Date
|
||||
{
|
||||
return this._lastUpdated;
|
||||
}
|
||||
|
||||
public get unread(): boolean
|
||||
{
|
||||
return this._unread;
|
||||
}
|
||||
}
|
@ -1,23 +1,25 @@
|
||||
export class MessengerChatMessage
|
||||
export class MessengerThreadChat
|
||||
{
|
||||
public static MESSAGE: number = 0;
|
||||
public static CHAT: number = 0;
|
||||
public static ROOM_INVITE: number = 1;
|
||||
public static SECURITY_ALERT: number = 2;
|
||||
public static STATUS_ALERT: number = 3;
|
||||
public static STATUS_NOTIFICATION: number = 2;
|
||||
public static SECURITY_NOTIFICATION: number = 3;
|
||||
|
||||
private _type: number;
|
||||
private _senderId: number;
|
||||
private _message: string;
|
||||
private _sentAt: number;
|
||||
private _secondsSinceSent: number;
|
||||
private _extraData: string;
|
||||
private _date: Date;
|
||||
|
||||
constructor(type: number, senderId: number, message: string, sentAt: number, extraData?: string)
|
||||
constructor(senderId: number, message: string, secondsSinceSent: number = 0, extraData: string = null, type: number = 0)
|
||||
{
|
||||
this._type = type;
|
||||
this._senderId = senderId;
|
||||
this._message = message;
|
||||
this._sentAt = sentAt;
|
||||
this._secondsSinceSent = secondsSinceSent;
|
||||
this._extraData = extraData;
|
||||
this._date = new Date();
|
||||
}
|
||||
|
||||
public get type(): number
|
||||
@ -35,13 +37,18 @@ export class MessengerChatMessage
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public get sentAt(): number
|
||||
public get secondsSinceSent(): number
|
||||
{
|
||||
return this._sentAt;
|
||||
return this._secondsSinceSent;
|
||||
}
|
||||
|
||||
public get extraData(): string
|
||||
{
|
||||
return this._extraData;
|
||||
}
|
||||
|
||||
public get date(): Date
|
||||
{
|
||||
return this._date;
|
||||
}
|
||||
}
|
28
src/views/friends/common/MessengerThreadChatGroup.ts
Normal file
28
src/views/friends/common/MessengerThreadChatGroup.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { MessengerThreadChat } from './MessengerThreadChat';
|
||||
|
||||
export class MessengerThreadChatGroup
|
||||
{
|
||||
private _userId: number;
|
||||
private _chats: MessengerThreadChat[];
|
||||
|
||||
constructor(userId: number)
|
||||
{
|
||||
this._userId = userId;
|
||||
this._chats = [];
|
||||
}
|
||||
|
||||
public addChat(message: MessengerThreadChat): void
|
||||
{
|
||||
this._chats.push(message);
|
||||
}
|
||||
|
||||
public get userId(): number
|
||||
{
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
public get chats(): MessengerThreadChat[]
|
||||
{
|
||||
return this._chats;
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import { FriendListUpdateParser, FriendParser, FriendRequestData } from '@nitrots/nitro-renderer';
|
||||
import { Reducer } from 'react';
|
||||
import { MessengerChat } from '../common/MessengerChat';
|
||||
import { MessengerChatMessage } from '../common/MessengerChatMessage';
|
||||
import { MessengerFriend } from '../common/MessengerFriend';
|
||||
import { MessengerRequest } from '../common/MessengerRequest';
|
||||
import { MessengerSettings } from '../common/MessengerSettings';
|
||||
@ -18,8 +16,6 @@ export interface IFriendsState
|
||||
settings: MessengerSettings;
|
||||
friends: MessengerFriend[];
|
||||
requests: MessengerRequest[];
|
||||
activeChats: MessengerChat[];
|
||||
firstChatEverOpen: boolean;
|
||||
}
|
||||
|
||||
export interface IFriendsAction
|
||||
@ -30,8 +26,6 @@ export interface IFriendsAction
|
||||
fragment?: FriendParser[];
|
||||
update?: FriendListUpdateParser;
|
||||
requests?: FriendRequestData[];
|
||||
chats?: MessengerChat[];
|
||||
chatMessage?: MessengerChatMessage;
|
||||
numberValue?: number;
|
||||
boolValue?: boolean;
|
||||
}
|
||||
@ -44,17 +38,12 @@ export class FriendsActions
|
||||
public static PROCESS_FRAGMENT: string = 'FA_PROCESS_FRAGMENT';
|
||||
public static PROCESS_UPDATE: string = 'FA_PROCESS_UPDATE';
|
||||
public static PROCESS_REQUESTS: string = 'FA_PROCESS_REQUESTS';
|
||||
public static SET_ACTIVE_CHATS: string = 'FA_SET_ACTIVE_CHATS';
|
||||
public static SET_CHAT_READ: string = 'FA_SET_CHAT_READ';
|
||||
public static ADD_CHAT_MESSAGE: string = 'FA_ADD_CHAT_MESSAGE';
|
||||
}
|
||||
|
||||
export const initialFriends: IFriendsState = {
|
||||
settings: null,
|
||||
friends: [],
|
||||
requests: [],
|
||||
activeChats: [],
|
||||
firstChatEverOpen: false
|
||||
requests: []
|
||||
}
|
||||
|
||||
export const FriendsReducer: Reducer<IFriendsState, IFriendsAction> = (state, action) =>
|
||||
@ -108,11 +97,17 @@ export const FriendsReducer: Reducer<IFriendsState, IFriendsAction> = (state, ac
|
||||
{
|
||||
const index = friends.findIndex(existingFriend => (existingFriend.id === friend.id));
|
||||
|
||||
const newFriend = new MessengerFriend();
|
||||
newFriend.populate(friend);
|
||||
if(index === -1)
|
||||
{
|
||||
const newFriend = new MessengerFriend();
|
||||
newFriend.populate(friend);
|
||||
|
||||
if(index > -1) friends[index] = newFriend;
|
||||
else friends.unshift(newFriend);
|
||||
friends.unshift(newFriend);
|
||||
}
|
||||
else
|
||||
{
|
||||
friends[index].populate(friend);
|
||||
}
|
||||
}
|
||||
|
||||
for(const friend of update.addedFriends) processUpdate(friend);
|
||||
@ -146,43 +141,6 @@ export const FriendsReducer: Reducer<IFriendsState, IFriendsAction> = (state, ac
|
||||
|
||||
return { ...state, requests };
|
||||
}
|
||||
case FriendsActions.SET_ACTIVE_CHATS: {
|
||||
const activeChats = (action.payload.chats || []);
|
||||
|
||||
if(!state.firstChatEverOpen && activeChats.length > 0) activeChats[0].addMessage(new MessengerChatMessage(MessengerChatMessage.SECURITY_ALERT, 0, null, 0), false, true);
|
||||
|
||||
return { ...state, activeChats, firstChatEverOpen: true };
|
||||
}
|
||||
case FriendsActions.SET_CHAT_READ: {
|
||||
const friendId = action.payload.numberValue;
|
||||
|
||||
const activeChats = Array.from(state.activeChats);
|
||||
|
||||
let activeChatIndex = activeChats.findIndex(c => c.friendId === friendId);
|
||||
|
||||
if(activeChatIndex > -1) activeChats[activeChatIndex].read();
|
||||
|
||||
return { ...state, activeChats };
|
||||
}
|
||||
case FriendsActions.ADD_CHAT_MESSAGE: {
|
||||
const message = action.payload.chatMessage;
|
||||
const toFriendId = action.payload.numberValue;
|
||||
const setAsNotRead = action.payload.boolValue;
|
||||
|
||||
const activeChats = Array.from(state.activeChats);
|
||||
|
||||
let activeChatIndex = activeChats.findIndex(c => c.friendId === toFriendId ? toFriendId : message.senderId);
|
||||
|
||||
if(activeChatIndex === -1)
|
||||
{
|
||||
activeChats.push(new MessengerChat(message.senderId));
|
||||
activeChatIndex = activeChats.length - 1;
|
||||
}
|
||||
|
||||
activeChats[activeChatIndex].addMessage(message, setAsNotRead);
|
||||
|
||||
return { ...state, activeChats };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
import { FC } from 'react';
|
||||
import { GetSessionDataManager } from '../../../../api';
|
||||
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||
import { MessengerThreadChat } from '../../common/MessengerThreadChat';
|
||||
import { FriendsMessengerThreadGroupProps } from './FriendsMessengerThreadGroup.types';
|
||||
|
||||
export const FriendsMessengerThreadGroup: FC<FriendsMessengerThreadGroupProps> = props =>
|
||||
{
|
||||
const { thread = null, group = null } = props;
|
||||
|
||||
if(!thread || !group) return null;
|
||||
|
||||
if(group.userId === -1)
|
||||
{
|
||||
return (
|
||||
<div className="d-flex gap-2 w-100 justify-content-start">
|
||||
{ group.chats.map((chat, index) =>
|
||||
{
|
||||
return (
|
||||
<div key={ index } className="text-break">
|
||||
{ chat.type === MessengerThreadChat.SECURITY_NOTIFICATION &&
|
||||
<div className="bg-light rounded mb-2 d-flex gap-2 px-2 py-1 small text-muted align-items-center">
|
||||
<i className="icon icon-friendlist-warning flex-shrink-0" />
|
||||
<div>{ chat.message }</div>
|
||||
</div> }
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ 'd-flex gap-2 w-100 justify-content-' + (group.userId === 0 ? 'end' : 'start') }>
|
||||
{ (group.userId > 0) &&
|
||||
<div className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ thread.participant.figure } direction={ 2 } />
|
||||
</div> }
|
||||
<div className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (group.userId === 0 ? 'right' : 'left') }>
|
||||
{ group.chats.map((chat, index) => <div key={ index } className="text-break">{ chat.message }</div>) }
|
||||
</div>
|
||||
{ (group.userId === 0) &&
|
||||
<div className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ GetSessionDataManager().figure } direction={ 4 } />
|
||||
</div> }
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { MessengerThread } from '../../common/MessengerThread';
|
||||
import { MessengerThreadChatGroup } from '../../common/MessengerThreadChatGroup';
|
||||
|
||||
export interface FriendsMessengerThreadGroupProps
|
||||
{
|
||||
thread: MessengerThread;
|
||||
group: MessengerThreadChatGroup;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { FC } from 'react';
|
||||
import { FriendsMessengerThreadGroup } from '../messenger-thread-group/FriendsMessengerThreadGroup';
|
||||
import { FriendsMessengerThreadViewProps } from './FriendsMessengerThreadView.types';
|
||||
|
||||
export const FriendsMessengerThreadView: FC<FriendsMessengerThreadViewProps> = props =>
|
||||
{
|
||||
const { thread = null } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (thread.groups.length > 0) && thread.groups.map((group, index) =>
|
||||
{
|
||||
return <FriendsMessengerThreadGroup key={ index } thread={ thread } group={ group } />;
|
||||
}) }
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { MessengerThread } from '../../common/MessengerThread';
|
||||
|
||||
export interface FriendsMessengerThreadViewProps
|
||||
{
|
||||
thread: MessengerThread;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
.nitro-friends-messenger {
|
||||
width: 280px;
|
||||
resize: both;
|
||||
|
||||
.friend-head {
|
||||
position: relative;
|
||||
|
@ -1,41 +1,134 @@
|
||||
import { FollowFriendMessageComposer, ILinkEventTracker, NitroEvent, SendMessageComposer, UserProfileComposer } from '@nitrots/nitro-renderer';
|
||||
import { FollowFriendMessageComposer, ILinkEventTracker, NewConsoleMessageEvent, SendMessageComposer, UserProfileComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AddEventLinkTracker, GetSessionDataManager, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
|
||||
import { FriendsEvent } from '../../../../events/friends/FriendsEvent';
|
||||
import { SendMessageHook, useUiEvent } from '../../../../hooks';
|
||||
import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
|
||||
import { FriendsMessengerIconEvent } from '../../../../events';
|
||||
import { BatchUpdates, CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||
import { MessengerChat } from '../../common/MessengerChat';
|
||||
import { MessengerChatMessage } from '../../common/MessengerChatMessage';
|
||||
import { MessengerThread } from '../../common/MessengerThread';
|
||||
import { MessengerThreadChat } from '../../common/MessengerThreadChat';
|
||||
import { useFriendsContext } from '../../context/FriendsContext';
|
||||
import { FriendsActions } from '../../reducers/FriendsReducer';
|
||||
import { FriendsMessengerThreadView } from '../messenger-thread/FriendsMessengerThreadView';
|
||||
|
||||
export const FriendsMessengerView: FC<{}> = props =>
|
||||
{
|
||||
const { friendsState = null, dispatchFriendsState = null } = useFriendsContext();
|
||||
const { activeChats = [], friends = [] } = friendsState;
|
||||
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ selectedChatIndex, setSelectedChatIndex ] = useState(0);
|
||||
const [ message, setMessage ] = useState('');
|
||||
|
||||
const [ messageThreads, setMessageThreads ] = useState<MessengerThread[]>([]);
|
||||
const [ activeThreadIndex, setActiveThreadIndex ] = useState(-1);
|
||||
const [ hiddenThreadIndexes, setHiddenThreadIndexes ] = useState<number[]>([]);
|
||||
const [ messageText, setMessageText ] = useState('');
|
||||
const { friendsState = null } = useFriendsContext();
|
||||
const { friends = [] } = friendsState;
|
||||
const messagesBox = useRef<HTMLDivElement>();
|
||||
|
||||
const onNitroEvent = useCallback((event: NitroEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case FriendsEvent.SHOW_FRIEND_MESSENGER:
|
||||
setIsVisible(true);
|
||||
return;
|
||||
case FriendsEvent.TOGGLE_FRIEND_MESSENGER:
|
||||
setIsVisible(value => !value);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
const [ updateValue, setUpdateValue ] = useState({});
|
||||
|
||||
useUiEvent(FriendsEvent.SHOW_FRIEND_MESSENGER, onNitroEvent);
|
||||
useUiEvent(FriendsEvent.TOGGLE_FRIEND_MESSENGER, onNitroEvent);
|
||||
const followFriend = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new FollowFriendMessageComposer(messageThreads[activeThreadIndex].participant.id));
|
||||
}, [ messageThreads, activeThreadIndex ]);
|
||||
|
||||
const openProfile = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new UserProfileComposer(messageThreads[activeThreadIndex].participant.id));
|
||||
}, [ messageThreads, activeThreadIndex ]);
|
||||
|
||||
const getFriend = useCallback((userId: number) =>
|
||||
{
|
||||
return ((friends.find(friend => (friend.id === userId))) || null);
|
||||
}, [ friends ]);
|
||||
|
||||
const visibleThreads = useMemo(() =>
|
||||
{
|
||||
return messageThreads.filter((thread, index) =>
|
||||
{
|
||||
if(hiddenThreadIndexes.indexOf(index) >= 0) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [ messageThreads, hiddenThreadIndexes ]);
|
||||
|
||||
const getMessageThreadWithIndex = useCallback<(userId: number) => [ number, MessengerThread ]>((userId: number) =>
|
||||
{
|
||||
if(messageThreads.length > 0)
|
||||
{
|
||||
for(let i = 0; i < messageThreads.length; i++)
|
||||
{
|
||||
const thread = messageThreads[i];
|
||||
|
||||
if(thread.participant && (thread.participant.id === userId))
|
||||
{
|
||||
const hiddenIndex = hiddenThreadIndexes.indexOf(i);
|
||||
|
||||
if(hiddenIndex >= 0)
|
||||
{
|
||||
setHiddenThreadIndexes(prevValue =>
|
||||
{
|
||||
const newIndexes = [ ...prevValue ];
|
||||
|
||||
newIndexes.splice(hiddenIndex, 1);
|
||||
|
||||
return newIndexes;
|
||||
});
|
||||
}
|
||||
|
||||
return [ i, thread ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const friend = getFriend(userId);
|
||||
|
||||
if(!friend) return [ -1, null ];
|
||||
|
||||
const thread = new MessengerThread(friend);
|
||||
const newThreads = [ ...messageThreads, thread ];
|
||||
|
||||
setMessageThreads(newThreads);
|
||||
|
||||
return [ (newThreads.length - 1), thread ];
|
||||
}, [ messageThreads, hiddenThreadIndexes, getFriend ]);
|
||||
|
||||
const onNewConsoleMessageEvent = useCallback((event: NewConsoleMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const [ threadIndex, thread ] = getMessageThreadWithIndex(parser.senderId);
|
||||
|
||||
if((threadIndex === -1) || !thread) return;
|
||||
|
||||
thread.addMessage(parser.senderId, parser.messageText, parser.secondsSinceSent, parser.extraData);
|
||||
|
||||
setMessageThreads(prevValue => [ ...prevValue ]);
|
||||
}, [ getMessageThreadWithIndex ]);
|
||||
|
||||
CreateMessageHook(NewConsoleMessageEvent, onNewConsoleMessageEvent);
|
||||
|
||||
const sendMessage = useCallback(() =>
|
||||
{
|
||||
if(!messageText || !messageText.length) return;
|
||||
|
||||
if(activeThreadIndex === -1) return;
|
||||
|
||||
const thread = messageThreads[activeThreadIndex];
|
||||
|
||||
if(!thread) return;
|
||||
|
||||
SendMessageHook(new SendMessageComposer(thread.participant.id, messageText));
|
||||
|
||||
thread.addMessage(0, messageText, 0, null, MessengerThreadChat.CHAT);
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setMessageThreads(prevValue => [ ...prevValue ]);
|
||||
setMessageText('');
|
||||
});
|
||||
}, [ messageThreads, activeThreadIndex, messageText ]);
|
||||
|
||||
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if(event.key !== 'Enter') return;
|
||||
|
||||
sendMessage();
|
||||
}, [ sendMessage ]);
|
||||
|
||||
const linkReceived = useCallback((url: string) =>
|
||||
{
|
||||
@ -43,69 +136,35 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
|
||||
if(parts.length < 3) return;
|
||||
|
||||
const friendId = parseInt(parts[2]);
|
||||
|
||||
let existingChatIndex = activeChats.findIndex(c => c.friendId === friendId);
|
||||
|
||||
if(existingChatIndex === -1)
|
||||
if(parts[2] === 'open')
|
||||
{
|
||||
const clonedActiveChats = Array.from(activeChats);
|
||||
clonedActiveChats.push(new MessengerChat(friendId));
|
||||
setIsVisible(true);
|
||||
|
||||
dispatchFriendsState({
|
||||
type: FriendsActions.SET_ACTIVE_CHATS,
|
||||
payload: {
|
||||
chats: clonedActiveChats
|
||||
}
|
||||
});
|
||||
|
||||
existingChatIndex = clonedActiveChats.length - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedChatIndex(existingChatIndex);
|
||||
setIsVisible(true);
|
||||
}, [ activeChats, dispatchFriendsState ]);
|
||||
const [ threadIndex ] = getMessageThreadWithIndex(parseInt(parts[2]));
|
||||
|
||||
const getFriendFigure = useCallback((id: number) =>
|
||||
{
|
||||
const friend = friends.find(f => f.id === id);
|
||||
if(threadIndex === -1) return;
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return friend.figure;
|
||||
}, [ friends ]);
|
||||
|
||||
const selectChat = useCallback((index: number) =>
|
||||
{
|
||||
const chat = activeChats[index];
|
||||
|
||||
if(!chat) return;
|
||||
|
||||
dispatchFriendsState({
|
||||
type: FriendsActions.SET_CHAT_READ,
|
||||
payload: {
|
||||
numberValue: chat.friendId
|
||||
}
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setActiveThreadIndex(threadIndex);
|
||||
setIsVisible(true);
|
||||
});
|
||||
|
||||
setSelectedChatIndex(index);
|
||||
}, [ activeChats, dispatchFriendsState ]);
|
||||
}, [ getMessageThreadWithIndex ]);
|
||||
|
||||
const selectedChat = useMemo(() =>
|
||||
{
|
||||
return activeChats[selectedChatIndex];
|
||||
}, [ activeChats, selectedChatIndex ]);
|
||||
|
||||
const selectedChatFriend = useMemo(() =>
|
||||
const closeThread = useCallback((threadIndex: number) =>
|
||||
{
|
||||
if(!selectedChat) return null;
|
||||
setHiddenThreadIndexes(prevValue =>
|
||||
{
|
||||
const values = [ ...prevValue ];
|
||||
|
||||
const friend = friends.find(f => f.id === selectedChat.friendId);
|
||||
if(values.indexOf(threadIndex) === -1) values.push(threadIndex);
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return friend;
|
||||
}, [ friends, selectedChat ]);
|
||||
return values;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -121,112 +180,102 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!messagesBox || !messagesBox.current) return;
|
||||
|
||||
messagesBox.current.scrollTop = messagesBox.current.scrollHeight;
|
||||
if(!isVisible) return;
|
||||
|
||||
}, [ selectedChat ]);
|
||||
if(activeThreadIndex === -1) setActiveThreadIndex(0);
|
||||
}, [ isVisible, activeThreadIndex ]);
|
||||
|
||||
const followFriend = useCallback(() =>
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new FollowFriendMessageComposer(selectedChatFriend.id));
|
||||
}, [ selectedChatFriend ]);
|
||||
if(hiddenThreadIndexes.indexOf(activeThreadIndex) >= 0) setActiveThreadIndex(0);
|
||||
}, [ activeThreadIndex, hiddenThreadIndexes ]);
|
||||
|
||||
const openProfile = useCallback(() =>
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new UserProfileComposer(selectedChatFriend.id));
|
||||
}, [ selectedChatFriend ]);
|
||||
if(!isVisible || (activeThreadIndex === -1)) return;
|
||||
|
||||
const sendMessage = useCallback(() =>
|
||||
const activeThread = messageThreads[activeThreadIndex];
|
||||
|
||||
if(activeThread.unread)
|
||||
{
|
||||
messagesBox.current.scrollTop = messagesBox.current.scrollHeight;
|
||||
activeThread.setRead();
|
||||
setUpdateValue({});
|
||||
}
|
||||
}, [ isVisible, messageThreads, activeThreadIndex ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(message.length === 0) return;
|
||||
if(!visibleThreads.length)
|
||||
{
|
||||
setIsVisible(false);
|
||||
|
||||
SendMessageHook(new SendMessageComposer(selectedChat.friendId, message));
|
||||
dispatchUiEvent(new FriendsMessengerIconEvent(FriendsMessengerIconEvent.UPDATE_ICON, FriendsMessengerIconEvent.HIDE_ICON));
|
||||
|
||||
dispatchFriendsState({
|
||||
type: FriendsActions.ADD_CHAT_MESSAGE,
|
||||
payload: {
|
||||
chatMessage: new MessengerChatMessage(MessengerChatMessage.MESSAGE, 0, message, (new Date().getMilliseconds())),
|
||||
numberValue: selectedChat.friendId,
|
||||
boolValue: false
|
||||
return;
|
||||
}
|
||||
|
||||
let isUnread = false;
|
||||
|
||||
for(const thread of visibleThreads)
|
||||
{
|
||||
if(thread.unread)
|
||||
{
|
||||
isUnread = true;
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
setMessage('');
|
||||
}, [ selectedChat, message, dispatchFriendsState ]);
|
||||
}
|
||||
|
||||
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if(event.key !== 'Enter') return;
|
||||
|
||||
sendMessage();
|
||||
}, [ sendMessage ]);
|
||||
dispatchUiEvent(new FriendsMessengerIconEvent(FriendsMessengerIconEvent.UPDATE_ICON, isUnread ? FriendsMessengerIconEvent.UNREAD_ICON : FriendsMessengerIconEvent.SHOW_ICON));
|
||||
}, [ visibleThreads, updateValue ]);
|
||||
|
||||
if(!isVisible) return null;
|
||||
|
||||
return (<NitroCardView className="nitro-friends-messenger" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('messenger.window.title', ['OPEN_CHAT_COUNT'], [activeChats.length.toString()]) } onCloseClick={ () => setIsVisible(false) } />
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex gap-2 overflow-auto pb-1">
|
||||
{ activeChats && activeChats.map((chat, index) =>
|
||||
return (
|
||||
<NitroCardView className="nitro-friends-messenger" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('messenger.window.title', [ 'OPEN_CHAT_COUNT' ], [ visibleThreads.length.toString() ]) } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex gap-2 overflow-auto pb-1">
|
||||
{ visibleThreads && (visibleThreads.length > 0) && visibleThreads.map((thread, index) =>
|
||||
{
|
||||
return <div key={ index } className="friend-head rounded flex-shrink-0 cursor-pointer bg-muted" onClick={ () => selectChat(index) }>
|
||||
{ !chat.isRead && <i className="icon icon-friendlist-new-message" /> }
|
||||
<AvatarImageView figure={ getFriendFigure(chat.friendId) } headOnly={true} direction={3} />
|
||||
</div>;
|
||||
const messageThreadIndex = messageThreads.indexOf(thread);
|
||||
|
||||
return (
|
||||
<div key={ index } className="friend-head rounded flex-shrink-0 cursor-pointer bg-muted" onClick={ event => setActiveThreadIndex(messageThreadIndex) }>
|
||||
{ thread.unread && <i className="icon icon-friendlist-new-message" /> }
|
||||
<AvatarImageView figure={ thread.participant.figure } headOnly={ true } direction={ 3 } />
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
</div>
|
||||
<hr className="bg-dark mt-3 mb-2" />
|
||||
{ selectedChat && selectedChatFriend && <>
|
||||
<div className="text-black chat-title bg-light pe-2 position-absolute">{ LocalizeText('messenger.window.separator', ['FRIEND_NAME'], [ selectedChatFriend.name ]) }</div>
|
||||
</div>
|
||||
<hr className="bg-dark mt-3 mb-2" />
|
||||
{ (activeThreadIndex >= 0) &&
|
||||
<>
|
||||
<div className="text-black chat-title bg-light pe-2 position-absolute">
|
||||
{ LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) }
|
||||
</div>
|
||||
<div className="d-flex gap-2 mb-2">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-sm btn-primary" onClick={ followFriend }>
|
||||
<button className="d-flex justify-content-center align-items-center btn btn-sm btn-primary" onClick={ followFriend }>
|
||||
<i className="icon icon-friendlist-follow" />
|
||||
</button>
|
||||
<button className="btn btn-sm btn-primary" onClick={ openProfile }>
|
||||
<button className="d-flex justify-content-center align-items-center btn btn-sm btn-primary" onClick={ openProfile }>
|
||||
<i className="icon icon-user-profile" />
|
||||
</button>
|
||||
</div>
|
||||
<button className="btn btn-sm btn-danger">{ LocalizeText('messenger.window.button.report') }</button>
|
||||
<button className="btn btn-sm btn-primary ms-auto"><i className="fas fa-times" /></button>
|
||||
<button className="btn btn-sm btn-primary ms-auto" onClick={ event => closeThread(activeThreadIndex) }><i className="fas fa-times" /></button>
|
||||
</div>
|
||||
<div ref={ messagesBox } className="bg-muted p-2 rounded chat-messages mb-2 d-flex flex-column">
|
||||
{ selectedChat.messageGroups.map((group, groupIndex) =>
|
||||
{
|
||||
return <div key={ groupIndex } className={ 'd-flex gap-2 w-100 justify-content-' + (group.userId === 0 ? 'end' : 'start') }>
|
||||
{ group.isSystem && <>
|
||||
{ group.messages.map((message, messageIndex) =>
|
||||
{
|
||||
return <div key={ messageIndex } className="text-break">
|
||||
{ message.type === MessengerChatMessage.SECURITY_ALERT && <div className="bg-light rounded mb-2 d-flex gap-2 px-2 py-1 small text-muted align-items-center">
|
||||
<i className="icon icon-friendlist-warning flex-shrink-0" />
|
||||
<div>{ LocalizeText('messenger.moderationinfo') }</div>
|
||||
</div> }
|
||||
</div>
|
||||
}) }
|
||||
</> }
|
||||
{ !group.isSystem && <>
|
||||
{ group.userId !== 0 && <div className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ selectedChatFriend.figure } direction={ 2 } />
|
||||
</div> }
|
||||
<div className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (group.userId === 0 ? 'right' : 'left') }>
|
||||
{ group.messages.map((message, messageIndex) =>
|
||||
{
|
||||
return <div key={ messageIndex } className="text-break">{ message.message }</div>
|
||||
}) }
|
||||
</div>
|
||||
{ group.userId === 0 && <div className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ GetSessionDataManager().figure } direction={ 4 } />
|
||||
</div> }
|
||||
</> }
|
||||
</div>;
|
||||
}) }
|
||||
<FriendsMessengerThreadView thread={ messageThreads[activeThreadIndex] } />
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('messenger.window.input.default', ['FRIEND_NAME'], [ selectedChatFriend.name ]) } value={ message } onChange={ (e) => setMessage(e.target.value) } onKeyDown={ onKeyDown } />
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) } value={ messageText } onChange={ event => setMessageText(event.target.value) } onKeyDown={ onKeyDown } />
|
||||
<button className="btn btn-sm btn-success" onClick={ sendMessage }>{ LocalizeText('widgets.chatinput.say') }</button>
|
||||
</div>
|
||||
</> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>);
|
||||
};
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
||||
|
@ -696,7 +696,6 @@
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
max-height: 24px;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
overflow: hidden;
|
||||
|
||||
.user-image {
|
||||
@ -707,8 +706,9 @@
|
||||
height: 130px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
transform: scale(0.5) translateZ(0);
|
||||
transform: scale(0.5);
|
||||
overflow: hidden;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,4 +6,10 @@
|
||||
background-position-x: center;
|
||||
background-position-y: -8px !important;
|
||||
pointer-events: none;
|
||||
image-rendering: pixelated;
|
||||
|
||||
&.scale-0-5,
|
||||
&.scale-0-75 {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { AvatarScaleType, AvatarSetType } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
import { AvatarImageViewProps } from './AvatarImageView.types';
|
||||
|
||||
@ -10,6 +10,13 @@ export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
||||
const [ randomValue, setRandomValue ] = useState(-1);
|
||||
const isDisposed = useRef(false);
|
||||
|
||||
const getScaleStyle = useMemo(() =>
|
||||
{
|
||||
if(scale === .5) return '0-5';
|
||||
|
||||
return scale.toString();
|
||||
}, [ scale ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, {
|
||||
@ -50,5 +57,5 @@ export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
||||
|
||||
const url = `url('${ avatarUrl }')`;
|
||||
|
||||
return <div className={ 'avatar-image scale-' + scale } style={ (avatarUrl && url.length) ? { backgroundImage: url } : {} }></div>;
|
||||
return <div className={ 'avatar-image scale-' + getScaleStyle } style={ (avatarUrl && url.length) ? { backgroundImage: url } : {} }></div>;
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
background-color: $light;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
image-rendering: auto;
|
||||
|
||||
&.border-0 {
|
||||
&::after {
|
||||
|
@ -29,31 +29,28 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
width: 50px;
|
||||
margin: 0 1px;
|
||||
//margin: 0 1px;
|
||||
position: relative;
|
||||
|
||||
.toolbar-avatar {
|
||||
height: 50px;
|
||||
&.item-avatar {
|
||||
width: 50px;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
|
||||
.avatar-image {
|
||||
margin-left: -5px;
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
&:hover, &.active {
|
||||
height: 53px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon,
|
||||
.toolbar-avatar {
|
||||
&.item-avatar {
|
||||
position: relative;
|
||||
transition: transform .2s ease-out;
|
||||
//transition: transform .2s ease-out;
|
||||
|
||||
&:hover, &.active {
|
||||
-webkit-transform: translate(0, -3px);
|
||||
transform: translate(0, -3px);
|
||||
-webkit-transform: translate(-1px, -1px);
|
||||
transform: translate(-1px, -1px);
|
||||
filter: drop-shadow(2px 2px 0 rgba($black, 0.8));
|
||||
}
|
||||
}
|
||||
@ -68,7 +65,7 @@
|
||||
|
||||
.count {
|
||||
top: 0rem;
|
||||
right: 5px;
|
||||
right: 2px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Dispose, DropBounce, EaseOut, FigureUpdateEvent, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserInfoDataParser, UserInfoEvent, UserProfileComposer, Wait } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomSession, GetRoomSessionManager, GetSessionDataManager, GoToDesktop } from '../../api';
|
||||
import { AvatarEditorEvent, CatalogEvent, FriendsEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
||||
import { GetRoomSession, GetRoomSessionManager, GetSessionDataManager, GoToDesktop, OpenMessengerChat } from '../../api';
|
||||
import { AvatarEditorEvent, CatalogEvent, FriendsEvent, FriendsMessengerIconEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
||||
import { AchievementsUIEvent } from '../../events/achievements';
|
||||
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
|
||||
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
|
||||
@ -14,6 +14,10 @@ import { AvatarImageView } from '../shared/avatar-image/AvatarImageView';
|
||||
import { ToolbarMeView } from './me/ToolbarMeView';
|
||||
import { ToolbarViewItems, ToolbarViewProps } from './ToolbarView.types';
|
||||
|
||||
const CHAT_ICON_HIDDEN: number = 0;
|
||||
const CHAT_ICON_SHOWING: number = 1;
|
||||
const CHAT_ICON_UNREAD: number = 2;
|
||||
|
||||
export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
{
|
||||
const { isInRoom } = props;
|
||||
@ -21,6 +25,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
const [ userInfo, setUserInfo ] = useState<UserInfoDataParser>(null);
|
||||
const [ userFigure, setUserFigure ] = useState<string>(null);
|
||||
const [ isMeExpanded, setMeExpanded ] = useState(false);
|
||||
const [ chatIconType, setChatIconType ] = useState(CHAT_ICON_HIDDEN);
|
||||
const [ unseenInventoryCount, setUnseenInventoryCount ] = useState(0);
|
||||
|
||||
const unseenFriendListCount = 0;
|
||||
@ -45,6 +50,13 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
|
||||
CreateMessageHook(FigureUpdateEvent, onUserFigureEvent);
|
||||
|
||||
const onFriendsMessengerIconEvent = useCallback((event: FriendsMessengerIconEvent) =>
|
||||
{
|
||||
setChatIconType(event.iconType);
|
||||
}, []);
|
||||
|
||||
useUiEvent(FriendsMessengerIconEvent.UPDATE_ICON, onFriendsMessengerIconEvent);
|
||||
|
||||
const onUnseenItemTrackerUpdateEvent = useCallback((event: UnseenItemTrackerUpdateEvent) =>
|
||||
{
|
||||
setUnseenInventoryCount(event.count);
|
||||
@ -131,6 +143,9 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
dispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS));
|
||||
setMeExpanded(false);
|
||||
return;
|
||||
case ToolbarViewItems.FRIEND_CHAT_ITEM:
|
||||
OpenMessengerChat();
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
@ -148,20 +163,16 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
<ToolbarMeView handleToolbarItemClick={ handleToolbarItemClick } />
|
||||
</TransitionAnimation>
|
||||
<div className="d-flex justify-content-between align-items-center nitro-toolbar py-1 px-3">
|
||||
<div className="d-flex align-items-center toolbar-left-side">
|
||||
<div className="navigation-items navigation-avatar pe-1 me-2">
|
||||
<div className="navigation-item">
|
||||
<div className={ 'toolbar-avatar ' + (isMeExpanded ? 'active ' : '') } onClick={ event => setMeExpanded(!isMeExpanded) }>
|
||||
<AvatarImageView figure={ userFigure } direction={ 2 } />
|
||||
</div>
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="navigation-items gap-2">
|
||||
<div className={ 'navigation-item item-avatar ' + (isMeExpanded ? 'active ' : '') } onClick={ event => setMeExpanded(!isMeExpanded) }>
|
||||
<AvatarImageView figure={ userFigure } direction={ 2 } />
|
||||
{ (unseenAchievementsCount > 0) &&
|
||||
<div className="position-absolute bg-danger px-1 py-0 rounded shadow count">{ unseenAchievementsCount }</div> }
|
||||
</div>
|
||||
{ (unseenAchievementsCount > 0) && (
|
||||
<div className="position-absolute bg-danger px-1 py-0 rounded shadow count">{ unseenAchievementsCount }</div>) }
|
||||
</div>
|
||||
<div className="navigation-items">
|
||||
{ isInRoom && (
|
||||
<div className="navigation-item" onClick={ visitDesktop }>
|
||||
<i className="icon icon-hotelview icon-nitro-light"></i>
|
||||
<i className="icon icon-habbo"></i>
|
||||
</div>) }
|
||||
{ !isInRoom && (
|
||||
<div className="navigation-item">
|
||||
@ -188,13 +199,20 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
</div>
|
||||
<div id="toolbar-chat-input-container" className="d-flex align-items-center" />
|
||||
</div>
|
||||
<div className="d-flex toolbar-right-side">
|
||||
<div className="navigation-items">
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<div className="navigation-items gap-2">
|
||||
<div className="navigation-item" onClick={ event => handleToolbarItemClick(ToolbarViewItems.FRIEND_LIST_ITEM) }>
|
||||
<i className="icon icon-friendall"></i>
|
||||
{ (unseenFriendListCount > 0) && (
|
||||
<div className="position-absolute bg-danger px-1 py-0 rounded shadow count">{ unseenFriendListCount }</div>) }
|
||||
</div>
|
||||
{ ((chatIconType === CHAT_ICON_SHOWING) || (chatIconType === CHAT_ICON_UNREAD)) &&
|
||||
<div className="navigation-item" onClick={ event => handleToolbarItemClick(ToolbarViewItems.FRIEND_CHAT_ITEM) }>
|
||||
{ (chatIconType === CHAT_ICON_SHOWING) && <i className="icon icon-message" /> }
|
||||
{ (chatIconType === CHAT_ICON_UNREAD) && <i className="icon icon-message is-unseen" /> }
|
||||
{ (unseenFriendListCount > 0) &&
|
||||
<div className="position-absolute bg-danger px-1 py-0 rounded shadow count">{ unseenFriendListCount }</div> }
|
||||
</div> }
|
||||
</div>
|
||||
<div id="toolbar-friend-bar-container" />
|
||||
</div>
|
||||
|
@ -9,6 +9,7 @@ export class ToolbarViewItems
|
||||
public static INVENTORY_ITEM: string = 'TVI_INVENTORY_ITEM';
|
||||
public static CATALOG_ITEM: string = 'TVI_CATALOG_ITEM';
|
||||
public static FRIEND_LIST_ITEM: string = 'TVI_FRIEND_LIST_ITEM';
|
||||
public static FRIEND_CHAT_ITEM: string = 'TVI_FRIEND_CHAT_ITEM';
|
||||
public static CLOTHING_ITEM: string = 'TVI_CLOTHING_ITEM';
|
||||
public static CAMERA_ITEM: string = 'TVI_CAMERA_ITEM';
|
||||
public static MOD_TOOLS_ITEM: string = 'TVI_MOD_TOOLS_ITEM';
|
||||
|
@ -24,6 +24,6 @@
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"node_modules/@nitrots/nitro-renderer/**/*.ts",
|
||||
"node_modules/@nitrots/nitro-renderer/src/**/*.ts",
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user