From 712d8242cec8e1dd0db9ec02571e776755be7ab9 Mon Sep 17 00:00:00 2001 From: Bill Date: Thu, 31 Mar 2022 12:16:58 -0400 Subject: [PATCH] Recode friends/messenger state --- src/api/friends/GetGroupChatData.ts | 11 + .../common => api/friends}/GroupType.ts | 0 src/api/friends/IGroupChatData.ts | 6 + .../common => api/friends}/MessengerFriend.ts | 2 +- src/api/friends/MessengerIconState.ts | 6 + .../friends}/MessengerRequest.ts | 8 +- .../friends}/MessengerSettings.ts | 0 .../common => api/friends}/MessengerThread.ts | 17 +- .../friends}/MessengerThreadChat.ts | 0 .../friends}/MessengerThreadChatGroup.ts | 0 src/api/friends/OpenMessengerChat.ts | 4 +- src/api/friends/index.ts | 10 + .../RoomWidgetUpdateFriendRequestEvent.ts | 35 -- .../RoomWidgetUpdateInfostandUserEvent.ts | 3 - .../events/RoomWidgetUpdateRoomObjectEvent.ts | 18 +- src/api/nitro/room/widgets/events/index.ts | 1 - .../widgets/handlers/FriendRequestHandler.ts | 65 --- .../handlers/RoomWidgetAvatarInfoHandler.ts | 2 +- .../handlers/RoomWidgetInfostandHandler.ts | 26 +- src/api/nitro/room/widgets/handlers/index.ts | 1 - .../RoomWidgetFriendRequestMessage.ts | 21 - .../messages/RoomWidgetUserActionMessage.ts | 1 - src/api/nitro/room/widgets/messages/index.ts | 1 - src/components/friends/FriendsContext.tsx | 32 -- src/components/friends/FriendsView.tsx | 371 +----------------- .../friends/common/FriendsHelper.ts | 14 - src/components/friends/common/Utils.ts | 18 - .../friends/views/FriendBarView.tsx | 93 +++++ .../views/friend-bar/FriendBarItemView.tsx | 78 ---- .../views/friend-bar/FriendBarView.tsx | 48 --- .../friends-list/FriendsListGroupView.tsx | 105 +++++ ... => FriendsListRemoveConfirmationView.tsx} | 0 .../friends-list/FriendsListRequestView.tsx | 48 +++ ...View.tsx => FriendsListRoomInviteView.tsx} | 1 - ...archView.tsx => FriendsListSearchView.tsx} | 5 +- .../views/friends-list/FriendsListView.tsx | 72 ++-- .../FriendsListGroupItemView.tsx | 93 ----- .../FriendsListGroupView.tsx | 24 -- .../FriendsListRequestItemView.tsx | 31 -- .../FriendsListRequestView.tsx | 35 -- .../messenger/FriendsMessengerThreadGroup.tsx | 84 ---- .../messenger/FriendsMessengerThreadView.tsx | 88 ++++- .../views/messenger/FriendsMessengerView.tsx | 308 ++++----------- src/components/room/RoomView.tsx | 3 +- .../room/widgets/RoomWidgetsView.tsx | 8 +- .../AvatarInfoWidgetAvatarView.tsx | 12 +- .../FriendRequestDialogView.tsx | 44 --- .../FriendRequestWidgetView.tsx | 140 +++++-- src/components/toolbar/ToolbarMeView.tsx | 38 +- src/components/toolbar/ToolbarView.tsx | 106 +---- src/events/friends/FriendListContentEvent.ts | 26 -- src/events/friends/FriendRequestEvent.ts | 21 - .../FriendsAcceptFriendRequestEvent.ts | 20 - .../FriendsDeclineFriendRequestEvent.ts | 20 - src/events/friends/FriendsEvent.ts | 10 - .../friends/FriendsMessengerIconEvent.ts | 23 -- .../friends/FriendsRequestCountEvent.ts | 20 - .../friends/FriendsSendFriendRequestEvent.ts | 27 -- src/events/friends/index.ts | 8 - src/hooks/friends/index.ts | 2 + src/hooks/friends/useFriends.ts | 265 +++++++++++++ src/hooks/friends/useMessenger.ts | 222 +++++++++++ src/hooks/index.ts | 1 + 63 files changed, 1172 insertions(+), 1630 deletions(-) create mode 100644 src/api/friends/GetGroupChatData.ts rename src/{components/friends/common => api/friends}/GroupType.ts (100%) create mode 100644 src/api/friends/IGroupChatData.ts rename src/{components/friends/common => api/friends}/MessengerFriend.ts (96%) create mode 100644 src/api/friends/MessengerIconState.ts rename src/{components/friends/common => api/friends}/MessengerRequest.ts (75%) rename src/{components/friends/common => api/friends}/MessengerSettings.ts (100%) rename src/{components/friends/common => api/friends}/MessengerThread.ts (84%) rename src/{components/friends/common => api/friends}/MessengerThreadChat.ts (100%) rename src/{components/friends/common => api/friends}/MessengerThreadChatGroup.ts (100%) delete mode 100644 src/api/nitro/room/widgets/events/RoomWidgetUpdateFriendRequestEvent.ts delete mode 100644 src/api/nitro/room/widgets/handlers/FriendRequestHandler.ts delete mode 100644 src/api/nitro/room/widgets/messages/RoomWidgetFriendRequestMessage.ts delete mode 100644 src/components/friends/FriendsContext.tsx delete mode 100644 src/components/friends/common/FriendsHelper.ts delete mode 100644 src/components/friends/common/Utils.ts create mode 100644 src/components/friends/views/FriendBarView.tsx delete mode 100644 src/components/friends/views/friend-bar/FriendBarItemView.tsx delete mode 100644 src/components/friends/views/friend-bar/FriendBarView.tsx create mode 100644 src/components/friends/views/friends-list/FriendsListGroupView.tsx rename src/components/friends/views/friends-list/{FriendsRemoveConfirmationView.tsx => FriendsListRemoveConfirmationView.tsx} (100%) create mode 100644 src/components/friends/views/friends-list/FriendsListRequestView.tsx rename src/components/friends/views/friends-list/{FriendsRoomInviteView.tsx => FriendsListRoomInviteView.tsx} (99%) rename src/components/friends/views/friends-list/{FriendsSearchView.tsx => FriendsListSearchView.tsx} (97%) delete mode 100644 src/components/friends/views/friends-list/friends-list-group/FriendsListGroupItemView.tsx delete mode 100644 src/components/friends/views/friends-list/friends-list-group/FriendsListGroupView.tsx delete mode 100644 src/components/friends/views/friends-list/friends-list-request/FriendsListRequestItemView.tsx delete mode 100644 src/components/friends/views/friends-list/friends-list-request/FriendsListRequestView.tsx delete mode 100644 src/components/friends/views/messenger/FriendsMessengerThreadGroup.tsx delete mode 100644 src/components/room/widgets/friend-request/FriendRequestDialogView.tsx delete mode 100644 src/events/friends/FriendListContentEvent.ts delete mode 100644 src/events/friends/FriendRequestEvent.ts delete mode 100644 src/events/friends/FriendsAcceptFriendRequestEvent.ts delete mode 100644 src/events/friends/FriendsDeclineFriendRequestEvent.ts delete mode 100644 src/events/friends/FriendsEvent.ts delete mode 100644 src/events/friends/FriendsMessengerIconEvent.ts delete mode 100644 src/events/friends/FriendsRequestCountEvent.ts delete mode 100644 src/events/friends/FriendsSendFriendRequestEvent.ts create mode 100644 src/hooks/friends/index.ts create mode 100644 src/hooks/friends/useFriends.ts create mode 100644 src/hooks/friends/useMessenger.ts diff --git a/src/api/friends/GetGroupChatData.ts b/src/api/friends/GetGroupChatData.ts new file mode 100644 index 00000000..4e8a0243 --- /dev/null +++ b/src/api/friends/GetGroupChatData.ts @@ -0,0 +1,11 @@ +import { IGroupChatData } from './IGroupChatData'; + +export const GetGroupChatData = (extraData: string) => +{ + const splitData = extraData.split('/'); + const username = splitData[0]; + const figure = splitData[1]; + const userId = parseInt(splitData[2]); + + return ({ username: username, figure: figure, userId: userId } as IGroupChatData); +} diff --git a/src/components/friends/common/GroupType.ts b/src/api/friends/GroupType.ts similarity index 100% rename from src/components/friends/common/GroupType.ts rename to src/api/friends/GroupType.ts diff --git a/src/api/friends/IGroupChatData.ts b/src/api/friends/IGroupChatData.ts new file mode 100644 index 00000000..24a3f9cf --- /dev/null +++ b/src/api/friends/IGroupChatData.ts @@ -0,0 +1,6 @@ +export interface IGroupChatData +{ + username: string; + figure: string; + userId: number; +} diff --git a/src/components/friends/common/MessengerFriend.ts b/src/api/friends/MessengerFriend.ts similarity index 96% rename from src/components/friends/common/MessengerFriend.ts rename to src/api/friends/MessengerFriend.ts index 2ed3c655..b5cfc887 100644 --- a/src/components/friends/common/MessengerFriend.ts +++ b/src/api/friends/MessengerFriend.ts @@ -2,7 +2,7 @@ import { FriendParser } from '@nitrots/nitro-renderer'; export class MessengerFriend { - public static RELATIONSHIP_NONE: number = 0; + public static RELATIONSHIP_NONE: number = 0; public static RELATIONSHIP_HEART: number = 1; public static RELATIONSHIP_SMILE: number = 2; public static RELATIONSHIP_BOBBA: number = 3; diff --git a/src/api/friends/MessengerIconState.ts b/src/api/friends/MessengerIconState.ts new file mode 100644 index 00000000..63f8c133 --- /dev/null +++ b/src/api/friends/MessengerIconState.ts @@ -0,0 +1,6 @@ +export class MessengerIconState +{ + public static HIDDEN: number = 0; + public static SHOW: number = 1; + public static UNREAD: number = 2; +} diff --git a/src/components/friends/common/MessengerRequest.ts b/src/api/friends/MessengerRequest.ts similarity index 75% rename from src/components/friends/common/MessengerRequest.ts rename to src/api/friends/MessengerRequest.ts index 2e7dbfa2..89ceec5b 100644 --- a/src/components/friends/common/MessengerRequest.ts +++ b/src/api/friends/MessengerRequest.ts @@ -11,10 +11,10 @@ export class MessengerRequest { if(!data) return false; - this._id = data.requestId; - this._name = data.requesterName; - this._figureString = data.figureString; - this._requesterUserId = data.requesterUserId; + this._id = data.requestId; + this._name = data.requesterName; + this._figureString = data.figureString; + this._requesterUserId = data.requesterUserId; return true; } diff --git a/src/components/friends/common/MessengerSettings.ts b/src/api/friends/MessengerSettings.ts similarity index 100% rename from src/components/friends/common/MessengerSettings.ts rename to src/api/friends/MessengerSettings.ts diff --git a/src/components/friends/common/MessengerThread.ts b/src/api/friends/MessengerThread.ts similarity index 84% rename from src/components/friends/common/MessengerThread.ts rename to src/api/friends/MessengerThread.ts index a1bfe0e7..e0b0ca85 100644 --- a/src/components/friends/common/MessengerThread.ts +++ b/src/api/friends/MessengerThread.ts @@ -1,14 +1,16 @@ -import { LocalizeText } from '../../../api'; +import { LocalizeText } from '../utils'; +import { GetGroupChatData } from './GetGroupChatData'; import { GroupType } from './GroupType'; import { MessengerFriend } from './MessengerFriend'; import { MessengerThreadChat } from './MessengerThreadChat'; import { MessengerThreadChatGroup } from './MessengerThreadChatGroup'; -import { getGroupChatData } from './Utils'; export class MessengerThread { public static MESSAGE_RECEIVED: string = 'MT_MESSAGE_RECEIVED'; + public static THREAD_ID: number = 0; + private _threadId: number; private _participant: MessengerFriend; private _groups: MessengerThreadChatGroup[]; private _lastUpdated: Date; @@ -16,6 +18,7 @@ export class MessengerThread constructor(participant: MessengerFriend, isNew: boolean = true) { + this._threadId = ++MessengerThread.THREAD_ID; this._participant = participant; this._groups = []; this._lastUpdated = new Date(); @@ -32,7 +35,7 @@ export class MessengerThread public addMessage(senderId: number, message: string, secondsSinceSent: number = 0, extraData: string = null, type: number = 0): MessengerThreadChat { const isGroupChat = (senderId < 0 && extraData); - const userId = isGroupChat ? getGroupChatData(extraData).userId : senderId; + const userId = isGroupChat ? GetGroupChatData(extraData).userId : senderId; const group = this.getLastGroup(userId); @@ -45,6 +48,7 @@ export class MessengerThread group.addChat(chat); this._lastUpdated = new Date(); + this._unreadCount++; return chat; @@ -68,6 +72,11 @@ export class MessengerThread this._unreadCount = 0; } + public get threadId(): number + { + return this._threadId; + } + public get participant(): MessengerFriend { return this._participant; @@ -90,6 +99,6 @@ export class MessengerThread public get unread(): boolean { - return this._unreadCount > 0; + return (this._unreadCount > 0); } } diff --git a/src/components/friends/common/MessengerThreadChat.ts b/src/api/friends/MessengerThreadChat.ts similarity index 100% rename from src/components/friends/common/MessengerThreadChat.ts rename to src/api/friends/MessengerThreadChat.ts diff --git a/src/components/friends/common/MessengerThreadChatGroup.ts b/src/api/friends/MessengerThreadChatGroup.ts similarity index 100% rename from src/components/friends/common/MessengerThreadChatGroup.ts rename to src/api/friends/MessengerThreadChatGroup.ts diff --git a/src/api/friends/OpenMessengerChat.ts b/src/api/friends/OpenMessengerChat.ts index 37e0bc3c..10add8c3 100644 --- a/src/api/friends/OpenMessengerChat.ts +++ b/src/api/friends/OpenMessengerChat.ts @@ -2,6 +2,6 @@ import { CreateLinkEvent } from '..'; export function OpenMessengerChat(friendId: number = 0): void { - if(friendId === 0) CreateLinkEvent('friends/messenger/open'); - else CreateLinkEvent(`friends/messenger/${friendId}`); + if(friendId === 0) CreateLinkEvent('friends-messenger/open'); + else CreateLinkEvent(`friends-messenger/${friendId}`); } diff --git a/src/api/friends/index.ts b/src/api/friends/index.ts index ffb458ee..da3d2c64 100644 --- a/src/api/friends/index.ts +++ b/src/api/friends/index.ts @@ -1 +1,11 @@ +export * from './GetGroupChatData'; +export * from './GroupType'; +export * from './IGroupChatData'; +export * from './MessengerFriend'; +export * from './MessengerIconState'; +export * from './MessengerRequest'; +export * from './MessengerSettings'; +export * from './MessengerThread'; +export * from './MessengerThreadChat'; +export * from './MessengerThreadChatGroup'; export * from './OpenMessengerChat'; diff --git a/src/api/nitro/room/widgets/events/RoomWidgetUpdateFriendRequestEvent.ts b/src/api/nitro/room/widgets/events/RoomWidgetUpdateFriendRequestEvent.ts deleted file mode 100644 index b60ac2d4..00000000 --- a/src/api/nitro/room/widgets/events/RoomWidgetUpdateFriendRequestEvent.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; - -export class RoomWidgetUpdateFriendRequestEvent extends RoomWidgetUpdateEvent -{ - public static SHOW_FRIEND_REQUEST: string = 'RWFRUE_SHOW_FRIEND_REQUEST'; - public static HIDE_FRIEND_REQUEST: string = 'RWFRUE_HIDE_FRIEND_REQUEST'; - - private _requestId: number; - private _userId: number; - private _userName: string; - - constructor(type: string, requestId: number = -1, userId: number = -1, userName: string = null) - { - super(type); - - this._requestId = requestId; - this._userId = userId; - this._userName = userName; - } - - public get requestId(): number - { - return this._requestId; - } - - public get userId(): number - { - return this._userId; - } - - public get userName(): string - { - return this._userName; - } -} diff --git a/src/api/nitro/room/widgets/events/RoomWidgetUpdateInfostandUserEvent.ts b/src/api/nitro/room/widgets/events/RoomWidgetUpdateInfostandUserEvent.ts index f54ecc9a..32640c75 100644 --- a/src/api/nitro/room/widgets/events/RoomWidgetUpdateInfostandUserEvent.ts +++ b/src/api/nitro/room/widgets/events/RoomWidgetUpdateInfostandUserEvent.ts @@ -24,12 +24,10 @@ export class RoomWidgetUpdateInfostandUserEvent extends RoomWidgetUpdateInfostan public carryItem: number = 0; public roomIndex: number = 0; public isSpectatorMode: boolean = false; - public realName: string = ''; public allowNameChange: boolean = false; public amIOwner: boolean = false; public amIAnyRoomController: boolean = false; public roomControllerLevel: number = 0; - public canBeAskedAsFriend: boolean = false; public canBeKicked: boolean = false; public canBeBanned: boolean = false; public canBeMuted: boolean = false; @@ -39,7 +37,6 @@ export class RoomWidgetUpdateInfostandUserEvent extends RoomWidgetUpdateInfostan public canTrade: boolean = false; public canTradeReason: number = 0; public targetRoomControllerLevel: number = 0; - public isFriend: boolean = false; public isAmbassador: boolean = false; public get isOwnUser(): boolean diff --git a/src/api/nitro/room/widgets/events/RoomWidgetUpdateRoomObjectEvent.ts b/src/api/nitro/room/widgets/events/RoomWidgetUpdateRoomObjectEvent.ts index cbc1d9c9..6ec8bf86 100644 --- a/src/api/nitro/room/widgets/events/RoomWidgetUpdateRoomObjectEvent.ts +++ b/src/api/nitro/room/widgets/events/RoomWidgetUpdateRoomObjectEvent.ts @@ -2,15 +2,15 @@ import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; export class RoomWidgetUpdateRoomObjectEvent extends RoomWidgetUpdateEvent { - public static OBJECT_SELECTED: string = 'RWUROE_OBJECT_SELECTED'; - public static OBJECT_DESELECTED: string = 'RWUROE_OBJECT_DESELECTED'; - public static USER_REMOVED: string = 'RWUROE_USER_REMOVED'; - public static FURNI_REMOVED: string = 'RWUROE_FURNI_REMOVED'; - public static FURNI_ADDED: string = 'RWUROE_FURNI_ADDED'; - public static USER_ADDED: string = 'RWUROE_USER_ADDED'; - public static OBJECT_ROLL_OVER: string = 'RWUROE_OBJECT_ROLL_OVER'; - public static OBJECT_ROLL_OUT: string = 'RWUROE_OBJECT_ROLL_OUT'; - public static OBJECT_REQUEST_MANIPULATION: string = 'RWUROE_OBJECT_REQUEST_MANIPULATION'; + public static OBJECT_SELECTED: string = 'RWUROE_OBJECT_SELECTED'; + public static OBJECT_DESELECTED: string = 'RWUROE_OBJECT_DESELECTED'; + public static USER_REMOVED: string = 'RWUROE_USER_REMOVED'; + public static FURNI_REMOVED: string = 'RWUROE_FURNI_REMOVED'; + public static FURNI_ADDED: string = 'RWUROE_FURNI_ADDED'; + public static USER_ADDED: string = 'RWUROE_USER_ADDED'; + public static OBJECT_ROLL_OVER: string = 'RWUROE_OBJECT_ROLL_OVER'; + public static OBJECT_ROLL_OUT: string = 'RWUROE_OBJECT_ROLL_OUT'; + public static OBJECT_REQUEST_MANIPULATION: string = 'RWUROE_OBJECT_REQUEST_MANIPULATION'; private _id: number; private _category: number; diff --git a/src/api/nitro/room/widgets/events/index.ts b/src/api/nitro/room/widgets/events/index.ts index be6a6ae5..cd7d85f6 100644 --- a/src/api/nitro/room/widgets/events/index.ts +++ b/src/api/nitro/room/widgets/events/index.ts @@ -18,7 +18,6 @@ export * from './RoomWidgetUpdateDimmerEvent'; export * from './RoomWidgetUpdateDimmerStateEvent'; export * from './RoomWidgetUpdateEvent'; export * from './RoomWidgetUpdateExternalImageEvent'; -export * from './RoomWidgetUpdateFriendRequestEvent'; export * from './RoomWidgetUpdateInfostandEvent'; export * from './RoomWidgetUpdateInfostandFurniEvent'; export * from './RoomWidgetUpdateInfostandPetEvent'; diff --git a/src/api/nitro/room/widgets/handlers/FriendRequestHandler.ts b/src/api/nitro/room/widgets/handlers/FriendRequestHandler.ts deleted file mode 100644 index 5269341c..00000000 --- a/src/api/nitro/room/widgets/handlers/FriendRequestHandler.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { NitroEvent, RoomSessionFriendRequestEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer'; -import { FriendRequestEvent, FriendsAcceptFriendRequestEvent, FriendsDeclineFriendRequestEvent } from '../../../../../events'; -import { DispatchUiEvent } from '../../../../../hooks'; -import { RoomWidgetUpdateEvent, RoomWidgetUpdateFriendRequestEvent } from '../events'; -import { RoomWidgetFriendRequestMessage, RoomWidgetMessage } from '../messages'; -import { RoomWidgetHandler } from './RoomWidgetHandler'; - -export class FriendRequestHandler extends RoomWidgetHandler -{ - public processEvent(event: NitroEvent): void - { - const friendRequestEvent = (event as RoomSessionFriendRequestEvent); - - switch(event.type) - { - case RoomSessionFriendRequestEvent.RSFRE_FRIEND_REQUEST: - this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateFriendRequestEvent(RoomWidgetUpdateFriendRequestEvent.SHOW_FRIEND_REQUEST, friendRequestEvent.requestId, friendRequestEvent.userId, friendRequestEvent.userName)); - return; - case FriendRequestEvent.ACCEPTED: - case FriendRequestEvent.DECLINED: - this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateFriendRequestEvent(RoomWidgetUpdateFriendRequestEvent.HIDE_FRIEND_REQUEST, friendRequestEvent.requestId)); - return; - } - } - - public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent - { - const friendMessage = (message as RoomWidgetFriendRequestMessage); - - switch(message.type) - { - case RoomWidgetFriendRequestMessage.ACCEPT: - DispatchUiEvent(new FriendsAcceptFriendRequestEvent(friendMessage.requestId)); - break; - case RoomWidgetFriendRequestMessage.DECLINE: - DispatchUiEvent(new FriendsDeclineFriendRequestEvent(friendMessage.requestId)); - break; - - } - - return null; - } - - public get type(): string - { - return RoomWidgetEnum.FRIEND_REQUEST; - } - - public get eventTypes(): string[] - { - return [ - RoomSessionFriendRequestEvent.RSFRE_FRIEND_REQUEST, - FriendRequestEvent.ACCEPTED, - FriendRequestEvent.DECLINED - ]; - } - - public get messageTypes(): string[] - { - return [ - RoomWidgetFriendRequestMessage.ACCEPT, - RoomWidgetFriendRequestMessage.DECLINE - ]; - } -} diff --git a/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts b/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts index 5731ad1d..19b06411 100644 --- a/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts +++ b/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts @@ -1,7 +1,7 @@ import { NitroEvent, RoomEngineUseProductEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionDanceEvent, RoomSessionPetStatusUpdateEvent, RoomSessionUserDataUpdateEvent, RoomWidgetEnum, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer'; import { SendMessageComposer } from '../../..'; import { GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..'; -import { MessengerFriend } from '../../../../../components/friends/common/MessengerFriend'; +import { MessengerFriend } from '../../../../friends/MessengerFriend'; import { FurniCategory } from '../../../../inventory/FurniCategory'; import { RoomWidgetAvatarInfoEvent, RoomWidgetUpdateDanceStatusEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateUserDataEvent, RoomWidgetUseProductBubbleEvent, UseProductItem } from '../events'; import { RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetRoomObjectMessage, RoomWidgetUseProductMessage, RoomWidgetUserActionMessage } from '../messages'; diff --git a/src/api/nitro/room/widgets/handlers/RoomWidgetInfostandHandler.ts b/src/api/nitro/room/widgets/handlers/RoomWidgetInfostandHandler.ts index 3928ed64..5ebf0902 100644 --- a/src/api/nitro/room/widgets/handlers/RoomWidgetInfostandHandler.ts +++ b/src/api/nitro/room/widgets/handlers/RoomWidgetInfostandHandler.ts @@ -1,9 +1,8 @@ import { IFurnitureData, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionFavoriteGroupUpdateEvent, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, Vector3d } from '@nitrots/nitro-renderer'; import { SendMessageComposer } from '../../..'; import { GetNitroInstance, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..'; -import { FriendsHelper } from '../../../../../components/friends/common/FriendsHelper'; import { PetSupplementEnum } from '../../../../../components/room/widgets/avatar-info/common/PetSupplementEnum'; -import { FriendsSendFriendRequestEvent, HelpReportUserEvent, InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../../../events'; +import { HelpReportUserEvent, InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../../../events'; import { DispatchUiEvent } from '../../../../../hooks'; import { LocalizeText } from '../../../../utils/LocalizeText'; import { RoomWidgetObjectNameEvent, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../events'; @@ -81,9 +80,6 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler return this.processObjectNameMessage((message as RoomWidgetRoomObjectMessage)); case RoomWidgetRoomObjectMessage.GET_OBJECT_INFO: return this.processObjectInfoMessage((message as RoomWidgetRoomObjectMessage)); - case RoomWidgetUserActionMessage.SEND_FRIEND_REQUEST: - DispatchUiEvent(new FriendsSendFriendRequestEvent(userData.webID, userData.name)); - break; case RoomWidgetUserActionMessage.RESPECT_USER: GetSessionDataManager().giveRespect(userId); break; @@ -452,11 +448,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler if(roomObject) event.carryItem = (roomObject.model.getValue(RoomObjectVariable.FIGURE_CARRY_OBJECT) || 0); - if(eventType === RoomWidgetUpdateInfostandUserEvent.OWN_USER) - { - event.realName = GetSessionDataManager().realName; - event.allowNameChange = GetSessionDataManager().canChangeName; - } + if(eventType === RoomWidgetUpdateInfostandUserEvent.OWN_USER) event.allowNameChange = GetSessionDataManager().canChangeName; event.amIOwner = this.container.roomSession.isRoomOwner; event.isGuildRoom = this.container.roomSession.isGuildRoom; @@ -466,19 +458,6 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler if(eventType === RoomWidgetUpdateInfostandUserEvent.PEER) { - event.canBeAskedAsFriend = FriendsHelper.canRequestFriend(userData.webID); - - if(!event.canBeAskedAsFriend) - { - const friend = FriendsHelper.getFriend(userData.webID); - - if(friend) - { - event.realName = friend.realName; - event.isFriend = true; - } - } - if(roomObject) { const flatControl = roomObject.model.getValue(RoomObjectVariable.FIGURE_FLAT_CONTROL); @@ -755,7 +734,6 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler return [ RoomWidgetRoomObjectMessage.GET_OBJECT_INFO, RoomWidgetRoomObjectMessage.GET_OBJECT_NAME, - RoomWidgetUserActionMessage.SEND_FRIEND_REQUEST, RoomWidgetUserActionMessage.RESPECT_USER, RoomWidgetUserActionMessage.WHISPER_USER, RoomWidgetUserActionMessage.IGNORE_USER, diff --git a/src/api/nitro/room/widgets/handlers/index.ts b/src/api/nitro/room/widgets/handlers/index.ts index 40fadc00..61153aed 100644 --- a/src/api/nitro/room/widgets/handlers/index.ts +++ b/src/api/nitro/room/widgets/handlers/index.ts @@ -1,5 +1,4 @@ export * from './DoorbellWidgetHandler'; -export * from './FriendRequestHandler'; export * from './FurniChooserWidgetHandler'; export * from './FurnitureContextMenuWidgetHandler'; export * from './FurnitureCreditWidgetHandler'; diff --git a/src/api/nitro/room/widgets/messages/RoomWidgetFriendRequestMessage.ts b/src/api/nitro/room/widgets/messages/RoomWidgetFriendRequestMessage.ts deleted file mode 100644 index 898a042d..00000000 --- a/src/api/nitro/room/widgets/messages/RoomWidgetFriendRequestMessage.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { RoomWidgetMessage } from './RoomWidgetMessage'; - -export class RoomWidgetFriendRequestMessage extends RoomWidgetMessage -{ - public static ACCEPT: string = 'RWFRM_ACCEPT'; - public static DECLINE: string = 'RMFRM_DECLINE'; - - private _requestId: number; - - constructor(type: string, requestId: number) - { - super(type); - - this._requestId = requestId; - } - - public get requestId(): number - { - return this._requestId; - } -} diff --git a/src/api/nitro/room/widgets/messages/RoomWidgetUserActionMessage.ts b/src/api/nitro/room/widgets/messages/RoomWidgetUserActionMessage.ts index 56fd89c7..964572f3 100644 --- a/src/api/nitro/room/widgets/messages/RoomWidgetUserActionMessage.ts +++ b/src/api/nitro/room/widgets/messages/RoomWidgetUserActionMessage.ts @@ -13,7 +13,6 @@ export class RoomWidgetUserActionMessage extends RoomWidgetMessage public static MUTE_USER_2MIN: string = 'RWUAM_MUTE_USER_2MIN'; public static MUTE_USER_5MIN: string = 'RWUAM_MUTE_USER_5MIN'; public static MUTE_USER_10MIN: string = 'RWUAM_MUTE_USER_10MIN'; - public static SEND_FRIEND_REQUEST: string = 'RWUAM_SEND_FRIEND_REQUEST'; public static RESPECT_USER: string = 'RWUAM_RESPECT_USER'; public static GIVE_RIGHTS: string = 'RWUAM_GIVE_RIGHTS'; public static TAKE_RIGHTS: string = 'RWUAM_TAKE_RIGHTS'; diff --git a/src/api/nitro/room/widgets/messages/index.ts b/src/api/nitro/room/widgets/messages/index.ts index 9fdaba24..bf9ae219 100644 --- a/src/api/nitro/room/widgets/messages/index.ts +++ b/src/api/nitro/room/widgets/messages/index.ts @@ -9,7 +9,6 @@ export * from './RoomWidgetDanceMessage'; export * from './RoomWidgetDimmerChangeStateMessage'; export * from './RoomWidgetDimmerPreviewMessage'; export * from './RoomWidgetDimmerSavePresetMessage'; -export * from './RoomWidgetFriendRequestMessage'; export * from './RoomWidgetFurniActionMessage'; export * from './RoomWidgetFurniToWidgetMessage'; export * from './RoomWidgetLetUserInMessage'; diff --git a/src/components/friends/FriendsContext.tsx b/src/components/friends/FriendsContext.tsx deleted file mode 100644 index bac0fc85..00000000 --- a/src/components/friends/FriendsContext.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { createContext, FC, ProviderProps, useContext } from 'react'; -import { MessengerFriend } from './common/MessengerFriend'; -import { MessengerRequest } from './common/MessengerRequest'; -import { MessengerSettings } from './common/MessengerSettings'; - -interface IFriendsContext -{ - friends: MessengerFriend[]; - requests: MessengerRequest[]; - settings: MessengerSettings; - canRequestFriend: (userId: number) => boolean; - requestFriend: (userId: number, userName: string) => void; - acceptFriend: (userId: number) => void; - declineFriend: (userId: number, declineAll?: boolean) => void; -} - -const FriendsContext = createContext({ - friends: null, - requests: null, - settings: null, - canRequestFriend: null, - requestFriend: null, - acceptFriend: null, - declineFriend: null -}); - -export const FriendsContextProvider: FC> = props => -{ - return { props.children } -} - -export const useFriendsContext = () => useContext(FriendsContext); diff --git a/src/components/friends/FriendsView.tsx b/src/components/friends/FriendsView.tsx index 0f4bb3ca..5a9d271d 100644 --- a/src/components/friends/FriendsView.tsx +++ b/src/components/friends/FriendsView.tsx @@ -1,371 +1,42 @@ -import { AcceptFriendMessageComposer, FriendListFragmentEvent, FriendListUpdateEvent, FriendParser, FriendRequestsEvent, GetFriendRequestsComposer, MessengerInitComposer, MessengerInitEvent, NewFriendRequestEvent, RequestFriendComposer, RoomEngineObjectEvent, RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer'; -import { DeclineFriendMessageComposer } from '@nitrots/nitro-renderer/src'; -import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { FC } from 'react'; import { createPortal } from 'react-dom'; -import { GetRoomSession, SendMessageComposer } from '../../api'; -import { FriendEnteredRoomEvent, FriendListContentEvent, FriendRequestEvent, FriendsAcceptFriendRequestEvent, FriendsDeclineFriendRequestEvent, FriendsEvent, FriendsRequestCountEvent, FriendsSendFriendRequestEvent } from '../../events'; -import { DispatchUiEvent, UseMessageEventHook, UseRoomEngineEvent, UseUiEvent } from '../../hooks'; -import { FriendsHelper } from './common/FriendsHelper'; -import { MessengerFriend } from './common/MessengerFriend'; -import { MessengerRequest } from './common/MessengerRequest'; -import { MessengerSettings } from './common/MessengerSettings'; -import { FriendsContextProvider } from './FriendsContext'; -import { FriendBarView } from './views/friend-bar/FriendBarView'; +import { useFriends } from '../../hooks'; +import { FriendBarView } from './views/FriendBarView'; import { FriendsListView } from './views/friends-list/FriendsListView'; import { FriendsMessengerView } from './views/messenger/FriendsMessengerView'; export const FriendsView: FC<{}> = props => { - const [ isReady, setIsReady ] = useState(false); - const [ isVisible, setIsVisible ] = useState(false); - const [ friends, setFriends ] = useState([]); - const [ requests, setRequests ] = useState([]); - const [ settings, setSettings ] = useState(null); - const [ sentRequests, setSentRequests ] = useState([]); + const { settings = null, onlineFriends = [] } = useFriends(); - const getFriend = useCallback((userId: number) => - { - for(const friend of friends) - { - if(friend.id === userId) return friend; - } + // const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) => + // { + // const roomSession = GetRoomSession(); - return null; - }, [ friends ]); + // if(!roomSession) return; - FriendsHelper.getFriend = getFriend; - - const canRequestFriend = useCallback((userId: number) => - { - if(getFriend(userId)) return false; - - if(sentRequests.indexOf(userId) >= 0) return false; - - return true; - }, [ sentRequests, getFriend ]); - - FriendsHelper.canRequestFriend = canRequestFriend; - - const requestFriend = useCallback((userId: number, userName: string) => - { - if(sentRequests.indexOf(userId) >= 0) return true; - - if(!canRequestFriend(userId)) return false; - - setSentRequests(prevValue => - { - const newSentRequests = [ ...prevValue ]; - - newSentRequests.push(userId); - - return newSentRequests; - }); - - SendMessageComposer(new RequestFriendComposer(userName)); - }, [ sentRequests, canRequestFriend ]); - - const acceptFriend = useCallback((userId: number) => - { - setRequests(prevValue => - { - const newRequests: MessengerRequest[] = [ ...prevValue ]; - - const index = newRequests.findIndex(request => (request.requesterUserId === userId)); - - if(index >= 0) - { - SendMessageComposer(new AcceptFriendMessageComposer(newRequests[index].id)); - - DispatchUiEvent(new FriendRequestEvent(FriendRequestEvent.ACCEPTED, userId)); - - newRequests.splice(index, 1); - } - - return newRequests; - }); - }, []); - - const declineFriend = useCallback((userId: number, declineAll: boolean = false) => - { - setRequests(prevValue => - { - if(declineAll) - { - SendMessageComposer(new DeclineFriendMessageComposer(true)); - - for(const request of prevValue) DispatchUiEvent(new FriendRequestEvent(FriendRequestEvent.DECLINED, request.requesterUserId)); - - return []; - } - else - { - const newRequests: MessengerRequest[] = [ ...prevValue ]; - - const index = newRequests.findIndex(request => (request.requesterUserId === userId)); - - if(index >= 0) - { - SendMessageComposer(new DeclineFriendMessageComposer(false, newRequests[index].id)); - - DispatchUiEvent(new FriendRequestEvent(FriendRequestEvent.DECLINED, userId)); - - newRequests.splice(index, 1); - } - - return newRequests; - } - }); - }, []); - - const onMessengerInitEvent = useCallback((event: MessengerInitEvent) => - { - const parser = event.getParser(); - - setSettings(new MessengerSettings( - parser.userFriendLimit, - parser.normalFriendLimit, - parser.extendedFriendLimit, - parser.categories)); - - SendMessageComposer(new GetFriendRequestsComposer()); - }, []); - - UseMessageEventHook(MessengerInitEvent, onMessengerInitEvent); - - const onFriendsFragmentEvent = useCallback((event: FriendListFragmentEvent) => - { - const parser = event.getParser(); - - setFriends(prevValue => - { - const newFriends = [ ...prevValue ]; - - for(const friend of parser.fragment) - { - const index = newFriends.findIndex(existingFriend => (existingFriend.id === friend.id)); - const newFriend = new MessengerFriend(); - - newFriend.populate(friend); - - if(index > -1) newFriends[index] = newFriend; - else newFriends.push(newFriend); - } - - return newFriends; - }); - }, []); - - UseMessageEventHook(FriendListFragmentEvent, onFriendsFragmentEvent); - - const onFriendsUpdateEvent = useCallback((event: FriendListUpdateEvent) => - { - const parser = event.getParser(); - - setFriends(prevValue => - { - const newFriends = [ ...prevValue ]; - - const processUpdate = (friend: FriendParser) => - { - const index = newFriends.findIndex(existingFriend => (existingFriend.id === friend.id)); - - if(index === -1) - { - const newFriend = new MessengerFriend(); - newFriend.populate(friend); - - newFriends.unshift(newFriend); - } - else - { - newFriends[index].populate(friend); - } - } - - for(const friend of parser.addedFriends) processUpdate(friend); - - for(const friend of parser.updatedFriends) processUpdate(friend); - - for(const removedFriendId of parser.removedFriendIds) - { - const index = newFriends.findIndex(existingFriend => (existingFriend.id === removedFriendId)); - - if(index > -1) newFriends.splice(index); - } - - return newFriends; - }); - }, []); - - UseMessageEventHook(FriendListUpdateEvent, onFriendsUpdateEvent); - - const onFriendRequestsEvent = useCallback((event: FriendRequestsEvent) => - { - const parser = event.getParser(); - - setRequests(prevValue => - { - const newRequests = [ ...prevValue ]; - - for(const request of parser.requests) - { - const index = newRequests.findIndex(existing => (existing.requesterUserId === request.requesterUserId)); - - if(index > 0) continue; - - const newRequest = new MessengerRequest(); - newRequest.populate(request); - - newRequests.push(newRequest); - } - - return newRequests; - }); - }, []); - - UseMessageEventHook(FriendRequestsEvent, onFriendRequestsEvent); - - const onNewFriendRequestEvent = useCallback((event: NewFriendRequestEvent) => - { - const parser = event.getParser(); - const request = parser.request; - - setRequests(prevValue => - { - const newRequests = [ ...prevValue ]; - - const index = newRequests.findIndex(existing => (existing.requesterUserId === request.requesterUserId)); - - if(index === -1) - { - const newRequest = new MessengerRequest(); - newRequest.populate(request); - - newRequests.push(newRequest); - } - - return newRequests; - }); - }, []); - - UseMessageEventHook(NewFriendRequestEvent, onNewFriendRequestEvent); - - const onFriendsEvent = useCallback((event: FriendsEvent) => - { - switch(event.type) - { - case FriendsEvent.SHOW_FRIEND_LIST: - setIsVisible(true); - return; - case FriendsEvent.TOGGLE_FRIEND_LIST: - setIsVisible(value => !value); - return; - case FriendsSendFriendRequestEvent.SEND_FRIEND_REQUEST: - const requestEvent = (event as FriendsSendFriendRequestEvent); - requestFriend(requestEvent.userId, requestEvent.userName); - return; - case FriendsEvent.REQUEST_FRIEND_LIST: - DispatchUiEvent(new FriendListContentEvent(friends)); - return; - } - }, [ friends, requestFriend ]); - - UseUiEvent(FriendsEvent.SHOW_FRIEND_LIST, onFriendsEvent); - UseUiEvent(FriendsEvent.TOGGLE_FRIEND_LIST, onFriendsEvent); - UseUiEvent(FriendsSendFriendRequestEvent.SEND_FRIEND_REQUEST, onFriendsEvent); - UseUiEvent(FriendsEvent.REQUEST_FRIEND_LIST, onFriendsEvent); - - const onFriendsAcceptFriendRequestEvent = useCallback((event: FriendsAcceptFriendRequestEvent) => - { - acceptFriend(event.requestId); - }, [ acceptFriend ]); - - UseUiEvent(FriendsAcceptFriendRequestEvent.ACCEPT_FRIEND_REQUEST, onFriendsAcceptFriendRequestEvent); - - const onFriendsDeclineFriendRequestEvent = useCallback((event: FriendsDeclineFriendRequestEvent) => - { - declineFriend(event.requestId); - }, [ declineFriend ]); - - UseUiEvent(FriendsDeclineFriendRequestEvent.DECLINE_FRIEND_REQUEST, onFriendsDeclineFriendRequestEvent); - - const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) => - { - const roomSession = GetRoomSession(); - - if(!roomSession) return; - - if(event.category !== RoomObjectCategory.UNIT) return; + // if(event.category !== RoomObjectCategory.UNIT) return; - const userData = roomSession.userDataManager.getUserDataByIndex(event.objectId); + // const userData = roomSession.userDataManager.getUserDataByIndex(event.objectId); - if(!userData || (userData.type !== RoomObjectUserType.getTypeNumber(RoomObjectUserType.USER))) return; + // if(!userData || (userData.type !== RoomObjectUserType.getTypeNumber(RoomObjectUserType.USER))) return; - const friend = getFriend(userData.webID); + // const friend = getFriend(userData.webID); - if(!friend) return; + // if(!friend) return; - DispatchUiEvent(new FriendEnteredRoomEvent(userData.roomIndex, RoomObjectCategory.UNIT, userData.webID, userData.name, userData.type)); - }, [ getFriend ]); + // DispatchUiEvent(new FriendEnteredRoomEvent(userData.roomIndex, RoomObjectCategory.UNIT, userData.webID, userData.name, userData.type)); + // }, [ getFriend ]); - UseRoomEngineEvent(RoomEngineObjectEvent.ADDED, onRoomEngineObjectEvent); + // UseRoomEngineEvent(RoomEngineObjectEvent.ADDED, onRoomEngineObjectEvent); - const onlineFriends = useMemo(() => - { - const onlineFriends = friends.filter(friend => friend.online); - - onlineFriends.sort((a, b) => - { - if( a.name < b.name ) return -1; - - if( a.name > b.name ) return 1; - - return 0; - }); - - return onlineFriends; - }, [ friends ]); - - const offlineFriends = useMemo(() => - { - const offlineFriends = friends.filter(friend => !friend.online); - - offlineFriends.sort((a, b) => - { - if( a.name < b.name ) return -1; - - if( a.name > b.name ) return 1; - - return 0; - }); - - return offlineFriends; - }, [ friends ]); - - useEffect(() => - { - SendMessageComposer(new MessengerInitComposer()); - }, []); - - useEffect(() => - { - if(!settings) return; - - setIsReady(true); - }, [ settings ]); - - useEffect(() => - { - DispatchUiEvent(new FriendsRequestCountEvent(requests.length)); - }, [ requests ]); + if(!settings) return null; return ( - - { isReady && - createPortal(, document.getElementById('toolbar-friend-bar-container')) } - { isVisible && - setIsVisible(false) } /> } + <> + { createPortal(, document.getElementById('toolbar-friend-bar-container')) } + - + ); } diff --git a/src/components/friends/common/FriendsHelper.ts b/src/components/friends/common/FriendsHelper.ts deleted file mode 100644 index 2e80f50c..00000000 --- a/src/components/friends/common/FriendsHelper.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MessengerFriend } from './MessengerFriend'; - -export class FriendsHelper -{ - public static getFriend(userId: number): MessengerFriend - { - return null; - } - - public static canRequestFriend(userId: number): boolean - { - return false; - } -} diff --git a/src/components/friends/common/Utils.ts b/src/components/friends/common/Utils.ts deleted file mode 100644 index d80d471a..00000000 --- a/src/components/friends/common/Utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const getGroupChatData = (extraData: string) => -{ - const splitData = extraData.split('/'); - - const username = splitData[0]; - const figure = splitData[1]; - const userId = parseInt(splitData[2]); - - const result: IGroupChatData = { username: username, figure: figure, userId: userId } - return result; -} - -export interface IGroupChatData -{ - username: string; - figure: string; - userId: number; -} diff --git a/src/components/friends/views/FriendBarView.tsx b/src/components/friends/views/FriendBarView.tsx new file mode 100644 index 00000000..a9978965 --- /dev/null +++ b/src/components/friends/views/FriendBarView.tsx @@ -0,0 +1,93 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { MouseEventType } from '@nitrots/nitro-renderer'; +import { FC, useEffect, useRef, useState } from 'react'; +import { GetUserProfile, LocalizeText, MessengerFriend, OpenMessengerChat } from '../../../api'; +import { Base, Button, Flex, LayoutAvatarImageView, LayoutBadgeImageView } from '../../../common'; +import { useFriends } from '../../../hooks'; + +interface FriendBarViewProps +{ + onlineFriends: MessengerFriend[]; +} + +export const FriendBarView: FC = props => +{ + const { onlineFriends = null } = props; + const [ indexOffset, setIndexOffset ] = useState(0); + const [ maxDisplayCount, setMaxDisplayCount ] = useState(3); + const { followFriend = null } = useFriends(); + + const FriendBarItemView: FC<{ friend: MessengerFriend }> = props => + { + const { friend = null } = props; + const [ isVisible, setVisible ] = useState(false); + const elementRef = useRef(); + + useEffect(() => + { + const onClick = (event: MouseEvent) => + { + const element = elementRef.current; + + if(!element) return; + + if((event.target !== element) && !element.contains((event.target as Node))) + { + setVisible(false); + } + } + + document.addEventListener(MouseEventType.MOUSE_CLICK, onClick); + + return () => + { + document.removeEventListener(MouseEventType.MOUSE_CLICK, onClick); + } + }, []); + + if(!friend) + { + return ( +
+
+
{ LocalizeText('friend.bar.find.title') }
+
+ ); + } + + return ( +
setVisible(prevValue => !prevValue) }> +
0 ? 'avatar': 'group'}`}> + { friend.id > 0 && } + { friend.id <= 0 && } +
+
{ friend.name }
+ { isVisible && +
+ OpenMessengerChat(friend.id) } /> + { friend.followingAllowed && + followFriend(friend) } /> } + GetUserProfile(friend.id) } /> +
} +
+ ); + } + + const canDecreaseIndex = () => (indexOffset === 0) ? false : true; + const canIncreaseIndex = () => ((onlineFriends.length <= maxDisplayCount) || (indexOffset === (onlineFriends.length - 1))) ? false : true; + + return ( + + + { Array.from(Array(maxDisplayCount), (e, i) => + { + return ; + }) } + + + ); +} diff --git a/src/components/friends/views/friend-bar/FriendBarItemView.tsx b/src/components/friends/views/friend-bar/FriendBarItemView.tsx deleted file mode 100644 index c48d6ca9..00000000 --- a/src/components/friends/views/friend-bar/FriendBarItemView.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { FollowFriendMessageComposer, MouseEventType } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useRef, useState } from 'react'; -import { GetUserProfile, LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../api'; -import { Base, LayoutAvatarImageView, LayoutBadgeImageView } from '../../../../common'; -import { MessengerFriend } from '../../common/MessengerFriend'; - -interface FriendBarItemViewProps -{ - friend: MessengerFriend; -} - -export const FriendBarItemView: FC = props => -{ - const { friend = null } = props; - const [ isVisible, setVisible ] = useState(false); - const elementRef = useRef(); - - const followFriend = useCallback(() => - { - SendMessageComposer(new FollowFriendMessageComposer(friend.id)); - }, [ friend ]); - - const openMessengerChat = useCallback(() => - { - if(!friend) return; - - OpenMessengerChat(friend.id); - }, [ friend ]); - - const onClick = useCallback((event: MouseEvent) => - { - const element = elementRef.current; - - if(!element) return; - - if((event.target !== element) && !element.contains((event.target as Node))) - { - setVisible(false); - } - }, []); - - useEffect(() => - { - document.addEventListener(MouseEventType.MOUSE_CLICK, onClick); - - return () => - { - document.removeEventListener(MouseEventType.MOUSE_CLICK, onClick); - } - }, [ onClick ]); - - if(!friend) - { - return ( -
-
-
{ LocalizeText('friend.bar.find.title') }
-
- ); - } - - return ( -
setVisible(prevValue => !prevValue) }> -
0 ? 'avatar': 'group'}`}> - { friend.id > 0 && } - { friend.id <= 0 && } -
-
{ friend.name }
- { isVisible && -
- - { friend.followingAllowed && - } - GetUserProfile(friend.id) } /> -
} -
- ); -} diff --git a/src/components/friends/views/friend-bar/FriendBarView.tsx b/src/components/friends/views/friend-bar/FriendBarView.tsx deleted file mode 100644 index 365254ae..00000000 --- a/src/components/friends/views/friend-bar/FriendBarView.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { FC, useMemo, useState } from 'react'; -import { Button } from '../../../../common'; -import { Flex } from '../../../../common/Flex'; -import { MessengerFriend } from '../../common/MessengerFriend'; -import { FriendBarItemView } from './FriendBarItemView'; - -interface FriendBarViewProps -{ - onlineFriends: MessengerFriend[]; -} - -export const FriendBarView: FC = props => -{ - const { onlineFriends = null } = props; - - const [ indexOffset, setIndexOffset ] = useState(0); - const [ maxDisplayCount, setMaxDisplayCount ] = useState(3); - - const canDecreaseIndex = useMemo(() => - { - if(indexOffset === 0) return false; - - return true; - }, [ indexOffset ]); - - const canIncreaseIndex = useMemo(() => - { - if((onlineFriends.length <= maxDisplayCount) || (indexOffset === (onlineFriends.length - 1))) return false; - - return true; - }, [ maxDisplayCount, indexOffset, onlineFriends ]); - - return ( - - - { Array.from(Array(maxDisplayCount), (e, i) => - { - return ; - }) } - - - ); -} diff --git a/src/components/friends/views/friends-list/FriendsListGroupView.tsx b/src/components/friends/views/friends-list/FriendsListGroupView.tsx new file mode 100644 index 00000000..55828a31 --- /dev/null +++ b/src/components/friends/views/friends-list/FriendsListGroupView.tsx @@ -0,0 +1,105 @@ +import { FC, MouseEvent, useState } from 'react'; +import { LocalizeText, MessengerFriend, OpenMessengerChat } from '../../../../api'; +import { Base, Flex, NitroCardAccordionItemView, UserProfileIconView } from '../../../../common'; +import { useFriends } from '../../../../hooks'; + +interface FriendsListGroupViewProps +{ + list: MessengerFriend[]; + selectedFriendsIds: number[]; + selectFriend: (userId: number) => void; +} + +export const FriendsListGroupView: FC = props => +{ + const { list = null, selectedFriendsIds = null, selectFriend = null } = props; + const { followFriend = null, updateRelationship = null } = useFriends(); + + if(!list || !list.length) return null; + + const FriendsListGroupItemView: FC<{ friend: MessengerFriend, selected: boolean }> = props => + { + const { friend = null, selected = false } = props; + const [ isRelationshipOpen, setIsRelationshipOpen ] = useState(false); + + const clickFollowFriend = (event: MouseEvent) => + { + event.stopPropagation(); + + followFriend(friend); + } + + const openMessengerChat = (event: MouseEvent) => + { + event.stopPropagation(); + + OpenMessengerChat(friend.id); + } + + const openRelationship = (event: MouseEvent) => + { + event.stopPropagation(); + + setIsRelationshipOpen(true); + } + + const clickUpdateRelationship = (event: MouseEvent, type: number) => + { + event.stopPropagation(); + + updateRelationship(friend, type); + + setIsRelationshipOpen(false); + } + + const getCurrentRelationshipName = () => + { + if(!friend) return 'none'; + + switch(friend.relationshipStatus) + { + case MessengerFriend.RELATIONSHIP_HEART: return 'heart'; + case MessengerFriend.RELATIONSHIP_SMILE: return 'smile'; + case MessengerFriend.RELATIONSHIP_BOBBA: return 'bobba'; + default: return 'none'; + } + } + + if(!friend) return null; + + return ( + selectFriend(friend.id) }> + + event.stopPropagation() }> + + +
{ friend.name }
+
+ + { !isRelationshipOpen && + <> + { friend.followingAllowed && + } + { friend.online && + } + { (friend.id > 0) && + } + } + { isRelationshipOpen && + <> + clickUpdateRelationship(event, MessengerFriend.RELATIONSHIP_HEART) } /> + clickUpdateRelationship(event, MessengerFriend.RELATIONSHIP_SMILE) } /> + clickUpdateRelationship(event, MessengerFriend.RELATIONSHIP_BOBBA) } /> + clickUpdateRelationship(event, MessengerFriend.RELATIONSHIP_NONE) } /> + } + +
+ ); + } + + return ( + <> + { list.map((item, index) => ) } + + ); +} diff --git a/src/components/friends/views/friends-list/FriendsRemoveConfirmationView.tsx b/src/components/friends/views/friends-list/FriendsListRemoveConfirmationView.tsx similarity index 100% rename from src/components/friends/views/friends-list/FriendsRemoveConfirmationView.tsx rename to src/components/friends/views/friends-list/FriendsListRemoveConfirmationView.tsx diff --git a/src/components/friends/views/friends-list/FriendsListRequestView.tsx b/src/components/friends/views/friends-list/FriendsListRequestView.tsx new file mode 100644 index 00000000..bdbb414d --- /dev/null +++ b/src/components/friends/views/friends-list/FriendsListRequestView.tsx @@ -0,0 +1,48 @@ +import { FC } from 'react'; +import { LocalizeText, MessengerRequest } from '../../../../api'; +import { Base, Button, Column, Flex, NitroCardAccordionItemView, NitroCardAccordionSetView, NitroCardAccordionSetViewProps, UserProfileIconView } from '../../../../common'; +import { useFriends } from '../../../../hooks'; + +export const FriendsListRequestView: FC = props => +{ + const { children = null, ...rest } = props; + const { requests = [], requestResponse = null } = useFriends(); + + if(!requests.length) return null; + + const FriendsListRequestItemView: FC<{ request: MessengerRequest }> = props => + { + const { request = null } = props; + + if(!request) return null; + + return ( + + + +
{ request.name }
+
+ + requestResponse(request.id, true) } /> + requestResponse(request.id, false) } /> + +
+ ); + } + + return ( + + + + { requests.map((request, index) => ) } + + + + + + { children } + + ); +} diff --git a/src/components/friends/views/friends-list/FriendsRoomInviteView.tsx b/src/components/friends/views/friends-list/FriendsListRoomInviteView.tsx similarity index 99% rename from src/components/friends/views/friends-list/FriendsRoomInviteView.tsx rename to src/components/friends/views/friends-list/FriendsListRoomInviteView.tsx index 1716e104..514f8210 100644 --- a/src/components/friends/views/friends-list/FriendsRoomInviteView.tsx +++ b/src/components/friends/views/friends-list/FriendsListRoomInviteView.tsx @@ -12,7 +12,6 @@ interface FriendsRoomInviteViewProps export const FriendsRoomInviteView: FC = props => { const { selectedFriendsIds = null, onCloseClick = null, sendRoomInvite = null } = props; - const [ roomInviteMessage, setRoomInviteMessage ] = useState(''); return ( diff --git a/src/components/friends/views/friends-list/FriendsSearchView.tsx b/src/components/friends/views/friends-list/FriendsListSearchView.tsx similarity index 97% rename from src/components/friends/views/friends-list/FriendsSearchView.tsx rename to src/components/friends/views/friends-list/FriendsListSearchView.tsx index 30079e1d..d4657b8f 100644 --- a/src/components/friends/views/friends-list/FriendsSearchView.tsx +++ b/src/components/friends/views/friends-list/FriendsListSearchView.tsx @@ -2,8 +2,7 @@ import { HabboSearchComposer, HabboSearchResultData, HabboSearchResultEvent } fr import { FC, useCallback, useEffect, useState } from 'react'; import { LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../api'; import { Base, Column, Flex, NitroCardAccordionItemView, NitroCardAccordionSetView, NitroCardAccordionSetViewProps, Text, UserProfileIconView } from '../../../../common'; -import { BatchUpdates, UseMessageEventHook } from '../../../../hooks'; -import { useFriendsContext } from '../../FriendsContext'; +import { BatchUpdates, useFriends, UseMessageEventHook } from '../../../../hooks'; interface FriendsSearchViewProps extends NitroCardAccordionSetViewProps { @@ -16,7 +15,7 @@ export const FriendsSearchView: FC = props => const [ searchValue, setSearchValue ] = useState(''); const [ friendResults, setFriendResults ] = useState(null); const [ otherResults, setOtherResults ] = useState(null); - const { canRequestFriend = null, requestFriend = null } = useFriendsContext(); + const { canRequestFriend = null, requestFriend = null } = useFriends(); const onHabboSearchResultEvent = useCallback((event: HabboSearchResultEvent) => { diff --git a/src/components/friends/views/friends-list/FriendsListView.tsx b/src/components/friends/views/friends-list/FriendsListView.tsx index c0f81929..130f71b7 100644 --- a/src/components/friends/views/friends-list/FriendsListView.tsx +++ b/src/components/friends/views/friends-list/FriendsListView.tsx @@ -1,29 +1,21 @@ -import { RemoveFriendComposer, SendRoomInviteComposer } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useMemo, useState } from 'react'; -import { LocalizeText, SendMessageComposer } from '../../../../api'; +import { ILinkEventTracker, RemoveFriendComposer, SendRoomInviteComposer } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { AddEventLinkTracker, LocalizeText, MessengerFriend, RemoveLinkEventTracker, SendMessageComposer } from '../../../../api'; import { Button, Flex, NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; -import { MessengerFriend } from '../../common/MessengerFriend'; -import { MessengerRequest } from '../../common/MessengerRequest'; -import { FriendsListGroupView } from './friends-list-group/FriendsListGroupView'; -import { FriendsListRequestView } from './friends-list-request/FriendsListRequestView'; -import { FriendsRemoveConfirmationView } from './FriendsRemoveConfirmationView'; -import { FriendsRoomInviteView } from './FriendsRoomInviteView'; -import { FriendsSearchView } from './FriendsSearchView'; +import { useFriends } from '../../../../hooks'; +import { FriendsListGroupView } from './FriendsListGroupView'; +import { FriendsRemoveConfirmationView } from './FriendsListRemoveConfirmationView'; +import { FriendsListRequestView } from './FriendsListRequestView'; +import { FriendsRoomInviteView } from './FriendsListRoomInviteView'; +import { FriendsSearchView } from './FriendsListSearchView'; -interface FriendsListViewProps +export const FriendsListView: FC<{}> = props => { - onCloseClick: () => void; - onlineFriends: MessengerFriend[]; - offlineFriends: MessengerFriend[]; - friendRequests: MessengerRequest[]; -} - -export const FriendsListView: FC = props => -{ - const { onlineFriends = [], offlineFriends = [], friendRequests = [], onCloseClick = null } = props; + const [ isVisible, setIsVisible ] = useState(false); const [ selectedFriendsIds, setSelectedFriendsIds ] = useState([]); const [ showRoomInvite, setShowRoomInvite ] = useState(false); const [ showRemoveFriendsConfirmation, setShowRemoveFriendsConfirmation ] = useState(false); + const { onlineFriends = [], offlineFriends = [], requests = [], requestFriend = null } = useFriends(); const removeFriendsText = useMemo(() => { @@ -90,10 +82,46 @@ export const FriendsListView: FC = props => setShowRemoveFriendsConfirmation(false); } + useEffect(() => + { + const linkTracker: ILinkEventTracker = { + linkReceived: (url: string) => + { + const parts = url.split('/'); + + if(parts.length < 2) return; + + switch(parts[1]) + { + case 'show': + setIsVisible(true); + return; + case 'hide': + setIsVisible(false); + return; + case 'toggle': + setIsVisible(prevValue => !prevValue); + return; + case 'request': + if(parts.length < 4) return; + + requestFriend(parseInt(parts[2]), parts[3]); + } + }, + eventUrlPrefix: 'friends/' + }; + + AddEventLinkTracker(linkTracker); + + return () => RemoveLinkEventTracker(linkTracker); + }, [ requestFriend ]); + + if(!isVisible) return null; + return ( <> - + setIsVisible(false) } /> @@ -102,7 +130,7 @@ export const FriendsListView: FC = props => - + { selectedFriendsIds && selectedFriendsIds.length > 0 && diff --git a/src/components/friends/views/friends-list/friends-list-group/FriendsListGroupItemView.tsx b/src/components/friends/views/friends-list/friends-list-group/FriendsListGroupItemView.tsx deleted file mode 100644 index d1978a46..00000000 --- a/src/components/friends/views/friends-list/friends-list-group/FriendsListGroupItemView.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer'; -import { FC, MouseEvent, useState } from 'react'; -import { LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../../api'; -import { Base, Flex, NitroCardAccordionItemView, NitroCardAccordionItemViewProps, UserProfileIconView } from '../../../../../common'; -import { MessengerFriend } from '../../../common/MessengerFriend'; - -interface FriendsListGroupItemViewProps extends NitroCardAccordionItemViewProps -{ - friend: MessengerFriend; - selected?: boolean; - selectFriend: () => void; -} - -export const FriendsListGroupItemView: FC = props => -{ - const { friend = null, selected = false, selectFriend = null, children = null, ...rest } = props; - const [ isRelationshipOpen, setIsRelationshipOpen ] = useState(false); - - const followFriend = (event: MouseEvent) => - { - event.stopPropagation(); - - SendMessageComposer(new FollowFriendMessageComposer(friend.id)); - } - - const openMessengerChat = (event: MouseEvent) => - { - event.stopPropagation(); - - OpenMessengerChat(friend.id); - } - - const openRelationship = (event: MouseEvent) => - { - event.stopPropagation(); - - setIsRelationshipOpen(true); - } - - const updateRelationship = (event: MouseEvent, type: number) => - { - event.stopPropagation(); - - if(type !== friend.relationshipStatus) SendMessageComposer(new SetRelationshipStatusComposer(friend.id, type)); - - setIsRelationshipOpen(false); - } - - const getCurrentRelationshipName = () => - { - if(!friend) return 'none'; - - switch(friend.relationshipStatus) - { - case MessengerFriend.RELATIONSHIP_HEART: return 'heart'; - case MessengerFriend.RELATIONSHIP_SMILE: return 'smile'; - case MessengerFriend.RELATIONSHIP_BOBBA: return 'bobba'; - default: return 'none'; - } - } - - if(!friend) return null; - - return ( - - - event.stopPropagation() }> - - -
{ friend.name }
-
- - { !isRelationshipOpen && - <> - { friend.followingAllowed && - } - { friend.online && - } - { (friend.id > 0) && - } - } - { isRelationshipOpen && - <> - updateRelationship(event, MessengerFriend.RELATIONSHIP_HEART) } /> - updateRelationship(event, MessengerFriend.RELATIONSHIP_SMILE) } /> - updateRelationship(event, MessengerFriend.RELATIONSHIP_BOBBA) } /> - updateRelationship(event, MessengerFriend.RELATIONSHIP_NONE) } /> - } - - { children } -
- ); -} diff --git a/src/components/friends/views/friends-list/friends-list-group/FriendsListGroupView.tsx b/src/components/friends/views/friends-list/friends-list-group/FriendsListGroupView.tsx deleted file mode 100644 index be5ef658..00000000 --- a/src/components/friends/views/friends-list/friends-list-group/FriendsListGroupView.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { FC } from 'react'; -import { MessengerFriend } from '../../../common/MessengerFriend'; -import { FriendsListGroupItemView } from './FriendsListGroupItemView'; - -interface FriendsListGroupViewProps -{ - list: MessengerFriend[]; - selectedFriendsIds: number[]; - selectFriend: (userId: number) => void; -} - -export const FriendsListGroupView: FC = props => -{ - const { list = null, selectedFriendsIds = null, selectFriend = null } = props; - - if(!list || !list.length) return null; - - return ( - <> - { list.map((item, index) => selectFriend(item.id) } />) } - - - ); -} diff --git a/src/components/friends/views/friends-list/friends-list-request/FriendsListRequestItemView.tsx b/src/components/friends/views/friends-list/friends-list-request/FriendsListRequestItemView.tsx deleted file mode 100644 index 57a33e52..00000000 --- a/src/components/friends/views/friends-list/friends-list-request/FriendsListRequestItemView.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { FC } from 'react'; -import { Base, Flex, NitroCardAccordionItemView, NitroCardAccordionItemViewProps, UserProfileIconView } from '../../../../../common'; -import { MessengerRequest } from '../../../common/MessengerRequest'; -import { useFriendsContext } from '../../../FriendsContext'; - -interface FriendsListRequestItemViewProps extends NitroCardAccordionItemViewProps -{ - request: MessengerRequest; -} - -export const FriendsListRequestItemView: FC = props => -{ - const { request = null, children = null, ...rest } = props; - const { acceptFriend = null, declineFriend = null } = useFriendsContext(); - - if(!request) return null; - - return ( - - - -
{ request.name }
-
- - acceptFriend(request.requesterUserId) } /> - declineFriend(request.requesterUserId) } /> - - { children } -
- ); -}; diff --git a/src/components/friends/views/friends-list/friends-list-request/FriendsListRequestView.tsx b/src/components/friends/views/friends-list/friends-list-request/FriendsListRequestView.tsx deleted file mode 100644 index 7b99131f..00000000 --- a/src/components/friends/views/friends-list/friends-list-request/FriendsListRequestView.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { FC } from 'react'; -import { LocalizeText } from '../../../../../api'; -import { Button, Column, Flex, NitroCardAccordionSetView, NitroCardAccordionSetViewProps } from '../../../../../common'; -import { MessengerRequest } from '../../../common/MessengerRequest'; -import { useFriendsContext } from '../../../FriendsContext'; -import { FriendsListRequestItemView } from './FriendsListRequestItemView'; - -interface FriendsListRequestViewProps extends NitroCardAccordionSetViewProps -{ - requests: MessengerRequest[]; -} - -export const FriendsListRequestView: FC = props => -{ - const { requests = [], children = null, ...rest } = props; - const { declineFriend = null } = useFriendsContext(); - - if(!requests.length) return null; - - return ( - - - - { requests.map((request, index) => ) } - - - - - - { children } - - ); -} diff --git a/src/components/friends/views/messenger/FriendsMessengerThreadGroup.tsx b/src/components/friends/views/messenger/FriendsMessengerThreadGroup.tsx deleted file mode 100644 index cea6edfc..00000000 --- a/src/components/friends/views/messenger/FriendsMessengerThreadGroup.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { FC, useMemo } from 'react'; -import { GetSessionDataManager, LocalizeText } from '../../../../api'; -import { Base, Flex, LayoutAvatarImageView } from '../../../../common'; -import { GroupType } from '../../common/GroupType'; -import { MessengerThread } from '../../common/MessengerThread'; -import { MessengerThreadChat } from '../../common/MessengerThreadChat'; -import { MessengerThreadChatGroup } from '../../common/MessengerThreadChatGroup'; -import { getGroupChatData } from '../../common/Utils'; - -interface FriendsMessengerThreadGroupProps -{ - thread: MessengerThread; - group: MessengerThreadChatGroup; -} - -export const FriendsMessengerThreadGroup: FC = props => -{ - const { thread = null, group = null } = props; - - const isOwnChat = useMemo(() => - { - if(!thread || !group) return false; - - if(group.type === GroupType.PRIVATE_CHAT && (group.userId === GetSessionDataManager().userId)) return true; - - if( (group.type === GroupType.GROUP_CHAT) && (group.chats.length && getGroupChatData(group.chats[0].extraData).userId === GetSessionDataManager().userId)) return true; - - return false; - }, [group, thread]); - - if(!thread || !group) return null; - - if(!group.userId) - { - return ( - <> - { group.chats.map((chat, index) => - { - return ( -
- - { chat.type === MessengerThreadChat.SECURITY_NOTIFICATION && - - - { chat.message } - } - { chat.type === MessengerThreadChat.ROOM_INVITE && - - - {(LocalizeText('messenger.invitation') + ' ') }{ chat.message } - } - -
- ); - }) } - - ); - } - - return ( - - - { (group.type === GroupType.PRIVATE_CHAT && !isOwnChat) && - - } - { (group.type === GroupType.GROUP_CHAT && !isOwnChat) && - - } - - - - { (isOwnChat) && GetSessionDataManager().userName } - { (!isOwnChat) && ((group.type === GroupType.GROUP_CHAT) ? getGroupChatData(group.chats[0].extraData).username : thread.participant.name) - } - - { group.chats.map((chat, index) =>{ chat.message }) } - - { (isOwnChat) && - - - } - - ); -} diff --git a/src/components/friends/views/messenger/FriendsMessengerThreadView.tsx b/src/components/friends/views/messenger/FriendsMessengerThreadView.tsx index 98e34965..f5d3533e 100644 --- a/src/components/friends/views/messenger/FriendsMessengerThreadView.tsx +++ b/src/components/friends/views/messenger/FriendsMessengerThreadView.tsx @@ -1,22 +1,84 @@ -import { FC } from 'react'; -import { MessengerThread } from '../../common/MessengerThread'; -import { FriendsMessengerThreadGroup } from './FriendsMessengerThreadGroup'; +import { FC, useMemo } from 'react'; +import { GetGroupChatData, GetSessionDataManager, GroupType, LocalizeText, MessengerThread, MessengerThreadChat, MessengerThreadChatGroup } from '../../../../api'; +import { Base, Flex, LayoutAvatarImageView } from '../../../../common'; -interface FriendsMessengerThreadViewProps -{ - thread: MessengerThread; -} - -export const FriendsMessengerThreadView: FC = props => +export const FriendsMessengerThreadView: FC<{ thread: MessengerThread }> = props => { const { thread = null } = props; + const FriendsMessengerThreadGroup: FC<{ thread: MessengerThread, group: MessengerThreadChatGroup }> = props => + { + const { thread = null, group = null, ...rest } = props; + + const isOwnChat = useMemo(() => + { + if(!thread || !group) return false; + + if(group.type === GroupType.PRIVATE_CHAT && (group.userId === GetSessionDataManager().userId)) return true; + + if( (group.type === GroupType.GROUP_CHAT) && (group.chats.length && GetGroupChatData(group.chats[0].extraData).userId === GetSessionDataManager().userId)) return true; + + return false; + }, [ group, thread ]); + + if(!thread || !group) return null; + + if(!group.userId) + { + return ( + <> + { group.chats.map((chat, index) => + { + return ( + + + { (chat.type === MessengerThreadChat.SECURITY_NOTIFICATION) && + + + { chat.message } + } + { (chat.type === MessengerThreadChat.ROOM_INVITE) && + + + { `${ LocalizeText('messenger.invitation') } ${ chat.message }` } + } + + + ); + }) } + + ); + } + + return ( + + + { (group.type === GroupType.PRIVATE_CHAT && !isOwnChat) && + + } + { (group.type === GroupType.GROUP_CHAT && !isOwnChat) && + + } + + + + { (isOwnChat) && GetSessionDataManager().userName } + { (!isOwnChat) && ((group.type === GroupType.GROUP_CHAT) ? GetGroupChatData(group.chats[0].extraData).username : thread.participant.name) + } + + { group.chats.map((chat, index) =>{ chat.message }) } + + { (isOwnChat) && + + + } + + ); + } + return ( <> - { (thread.groups.length > 0) && thread.groups.map((group, index) => - { - return ; - }) } + { (thread.groups.length > 0) && thread.groups.map((group, index) => ) } ); } diff --git a/src/components/friends/views/messenger/FriendsMessengerView.tsx b/src/components/friends/views/messenger/FriendsMessengerView.tsx index a7e10741..074abc08 100644 --- a/src/components/friends/views/messenger/FriendsMessengerView.tsx +++ b/src/components/friends/views/messenger/FriendsMessengerView.tsx @@ -1,281 +1,123 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { FollowFriendMessageComposer, ILinkEventTracker, NewConsoleMessageEvent, RoomInviteErrorEvent, RoomInviteEvent, SendMessageComposer as SendMessageComposerPacket } from '@nitrots/nitro-renderer'; -import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { AddEventLinkTracker, GetSessionDataManager, GetUserProfile, LocalizeText, NotificationAlertType, NotificationUtilities, PlaySound, RemoveLinkEventTracker, SendMessageComposer, SoundNames } from '../../../../api'; +import { FollowFriendMessageComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; +import { FC, KeyboardEvent, useEffect, useRef, useState } from 'react'; +import { AddEventLinkTracker, GetUserProfile, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../../../api'; import { Base, Button, ButtonGroup, Column, Flex, Grid, LayoutAvatarImageView, LayoutBadgeImageView, LayoutGridItem, LayoutItemCountView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; -import { FriendsMessengerIconEvent } from '../../../../events'; -import { BatchUpdates, DispatchUiEvent, UseMessageEventHook } from '../../../../hooks'; -import { MessengerThread } from '../../common/MessengerThread'; -import { MessengerThreadChat } from '../../common/MessengerThreadChat'; -import { useFriendsContext } from '../../FriendsContext'; +import { BatchUpdates, useMessenger } from '../../../../hooks'; import { FriendsMessengerThreadView } from './FriendsMessengerThreadView'; export const FriendsMessengerView: FC<{}> = props => { - const [isVisible, setIsVisible] = useState(false); - const [messageThreads, setMessageThreads] = useState([]); - const [activeThreadIndex, setActiveThreadIndex] = useState(-1); - const [hiddenThreadIndexes, setHiddenThreadIndexes] = useState([]); - const [messageText, setMessageText] = useState(''); - const [updateValue, setUpdateValue] = useState({}); - const { friends = [] } = useFriendsContext(); + const [ isVisible, setIsVisible ] = useState(false); + const [ lastThreadId, setLastThreadId ] = useState(-1); + const [ messageText, setMessageText ] = useState(''); + const { visibleThreads = [], activeThread = null, getMessageThread = null, sendMessage = null, setActiveThread = null, closeThread = null } = useMessenger(); const messagesBox = useRef(); - const followFriend = useCallback(() => + const followFriend = () => (activeThread && activeThread.participant && SendMessageComposer(new FollowFriendMessageComposer(activeThread.participant.id))); + const openProfile = () => (activeThread && activeThread.participant && GetUserProfile(activeThread.participant.id)); + + const send = () => { - SendMessageComposer(new FollowFriendMessageComposer(messageThreads[activeThreadIndex].participant.id)); - }, [messageThreads, activeThreadIndex]); + if(!activeThread || !messageText.length) return; - const openProfile = useCallback(() => - { - GetUserProfile(messageThreads[activeThreadIndex].participant.id); - }, [messageThreads, activeThreadIndex]); + sendMessage(activeThread, messageText); - const getFriend = useCallback((userId: number) => - { - return ((friends.find(friend => (friend.id === userId))) || null); - }, [friends]); + setMessageText(''); + } - 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]); - - UseMessageEventHook(NewConsoleMessageEvent, onNewConsoleMessageEvent); - - const onRoomInviteEvent = useCallback((event: RoomInviteEvent) => - { - const parser = event.getParser(); - - const [threadIndex, thread] = getMessageThreadWithIndex(parser.senderId); - - if((threadIndex === -1) || !thread) return; - - thread.addMessage(null, parser.messageText, 0, null, MessengerThreadChat.ROOM_INVITE); - - setMessageThreads(prevValue => [...prevValue]); - }, [getMessageThreadWithIndex]); - - UseMessageEventHook(RoomInviteEvent, onRoomInviteEvent); - - const onRoomInviteErrorEvent = useCallback((event: RoomInviteErrorEvent) => - { - const parser = event.getParser(); - const message = ((('Received room invite error: errorCode: ' + parser.errorCode) + ', recipients: ') + parser.failedRecipients); - - NotificationUtilities.simpleAlert(message, NotificationAlertType.DEFAULT, null, null, LocalizeText('friendlist.alert.title')); - }, []); - - UseMessageEventHook(RoomInviteErrorEvent, onRoomInviteErrorEvent); - - const sendMessage = useCallback(() => - { - if(!messageText || !messageText.length) return; - - if(activeThreadIndex === -1) return; - - const thread = messageThreads[activeThreadIndex]; - - if(!thread) return; - - SendMessageComposer(new SendMessageComposerPacket(thread.participant.id, messageText)); - - if(messageThreads.length === 1 && thread.groups.length === 1) PlaySound(SoundNames.MESSENGER_NEW_THREAD); - - thread.addMessage(GetSessionDataManager().userId, messageText, 0, null, MessengerThreadChat.CHAT); - - BatchUpdates(() => - { - setMessageThreads(prevValue => [...prevValue]); - setMessageText(''); - }); - }, [messageThreads, activeThreadIndex, messageText]); - - const onKeyDown = useCallback((event: KeyboardEvent) => + const onKeyDown = (event: KeyboardEvent) => { if(event.key !== 'Enter') return; - sendMessage(); - }, [sendMessage]); - - const linkReceived = useCallback((url: string) => - { - const parts = url.split('/'); - - if(parts.length < 3) return; - - if(parts[2] === 'open') - { - setIsVisible(true); - - return; - } - - const [threadIndex] = getMessageThreadWithIndex(parseInt(parts[2])); - - if(threadIndex === -1) return; - - BatchUpdates(() => - { - setActiveThreadIndex(threadIndex); - setIsVisible(true); - }); - }, [getMessageThreadWithIndex]); - - const closeThread = useCallback((threadIndex: number) => - { - setHiddenThreadIndexes(prevValue => - { - const values = [...prevValue]; - - if(values.indexOf(threadIndex) === -1) values.push(threadIndex); - - return values; - }); - }, []); + send(); + } useEffect(() => { const linkTracker: ILinkEventTracker = { - linkReceived, - eventUrlPrefix: 'friends/messenger/' + linkReceived: (url: string) => + { + const parts = url.split('/'); + + if(parts.length === 2) + { + if(parts[1] === 'open') + { + setIsVisible(true); + + return; + } + else + { + const thread = getMessageThread(parseInt(parts[1])); + + if(!thread) return; + + BatchUpdates(() => + { + setActiveThread(thread); + setIsVisible(true); + }); + } + } + }, + eventUrlPrefix: 'friends-messenger/' }; AddEventLinkTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker); - }, [linkReceived]); + }, [ getMessageThread, setActiveThread ]); + + // useEffect(() => + // { + // if(!isVisible || activeThread || !visibleThreads || !visibleThreads.length) return; + + // setActiveThread(visibleThreads[0]); + // }, [ isVisible, visibleThreads, activeThread, setActiveThread ]); useEffect(() => { - if(!isVisible) return; + if(!isVisible || !activeThread) return; - if(activeThreadIndex === -1) setActiveThreadIndex(0); - }, [isVisible, activeThreadIndex]); + messagesBox.current.scrollTop = messagesBox.current.scrollHeight; + }, [ isVisible, activeThread ]); useEffect(() => { - if(hiddenThreadIndexes.indexOf(activeThreadIndex) >= 0) setActiveThreadIndex(0); - }, [activeThreadIndex, hiddenThreadIndexes]); - - useEffect(() => - { - if(!isVisible || (activeThreadIndex === -1)) return; - - const activeThread = messageThreads[activeThreadIndex]; - if(!activeThread) return; - if(activeThread.unread) + return () => { - messagesBox.current.scrollTop = messagesBox.current.scrollHeight; - activeThread.setRead(); - setUpdateValue({}); + console.log('set last thread id', activeThread.threadId); + setLastThreadId(activeThread.threadId); } - }, [isVisible, messageThreads, activeThreadIndex]); + }, [ activeThread ]); useEffect(() => { - if(!visibleThreads.length) + return () => { - setIsVisible(false); - - DispatchUiEvent(new FriendsMessengerIconEvent(FriendsMessengerIconEvent.UPDATE_ICON, FriendsMessengerIconEvent.HIDE_ICON)); - - return; + console.log('clear thread') + setActiveThread(null); } - - let isUnread = false; - - for(const thread of visibleThreads) - { - if(thread.unreadCount > 0) - { - isUnread = true; - - break; - } - } - - if(isUnread) PlaySound(SoundNames.MESSENGER_MESSAGE_RECEIVED); - - DispatchUiEvent(new FriendsMessengerIconEvent(FriendsMessengerIconEvent.UPDATE_ICON, isUnread ? FriendsMessengerIconEvent.UNREAD_ICON : FriendsMessengerIconEvent.SHOW_ICON)); - }, [visibleThreads, updateValue]); + }, [ setActiveThread ]); if(!isVisible) return null; return ( - setIsVisible(false)} /> + setIsVisible(false) } /> { LocalizeText('toolbar.icon.label.messenger') } - { visibleThreads && (visibleThreads.length > 0) && visibleThreads.map((thread, index) => + { visibleThreads && (visibleThreads.length > 0) && visibleThreads.map(thread => { - const messageThreadIndex = messageThreads.indexOf(thread); - return ( - setActiveThreadIndex(messageThreadIndex) }> + setActiveThread(thread) }> { thread.unread && } @@ -293,9 +135,9 @@ export const FriendsMessengerView: FC<{}> = props => - { visibleThreads && (visibleThreads.length > 0) && (activeThreadIndex >= 0) && + { activeThread && <> - { LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) } + { LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ activeThread.participant.name ]) } @@ -307,21 +149,21 @@ export const FriendsMessengerView: FC<{}> = props => - - + - setMessageText(event.target.value) } onKeyDown={ onKeyDown } /> - diff --git a/src/components/room/RoomView.tsx b/src/components/room/RoomView.tsx index ccb1c1fd..38cf2b4e 100644 --- a/src/components/room/RoomView.tsx +++ b/src/components/room/RoomView.tsx @@ -1,6 +1,6 @@ import { EventDispatcher, IRoomSession, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer'; import { FC, useEffect, useRef, useState } from 'react'; -import { DispatchMouseEvent, DispatchTouchEvent, DoorbellWidgetHandler, FriendRequestHandler, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurnitureInternalLinkHandler, FurnitureMannequinWidgetHandler, FurniturePresentWidgetHandler, FurnitureRoomLinkHandler, FurnitureYoutubeDisplayWidgetHandler, GetNitroInstance, GetRoomEngine, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, PollWidgetHandler, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, RoomWidgetRoomToolsHandler, RoomWidgetUpdateRoomViewEvent, UserChooserWidgetHandler, WordQuizWidgetHandler } from '../../api'; +import { DispatchMouseEvent, DispatchTouchEvent, DoorbellWidgetHandler, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurnitureInternalLinkHandler, FurnitureMannequinWidgetHandler, FurniturePresentWidgetHandler, FurnitureRoomLinkHandler, FurnitureYoutubeDisplayWidgetHandler, GetNitroInstance, GetRoomEngine, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, PollWidgetHandler, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, RoomWidgetRoomToolsHandler, RoomWidgetUpdateRoomViewEvent, UserChooserWidgetHandler, WordQuizWidgetHandler } from '../../api'; import { Base } from '../../common'; import { RoomColorView } from './RoomColorView'; import { RoomContextProvider } from './RoomContext'; @@ -41,7 +41,6 @@ export const RoomView: FC = props => widgetHandlerManager.registerHandler(new DoorbellWidgetHandler()); widgetHandlerManager.registerHandler(new WordQuizWidgetHandler()); widgetHandlerManager.registerHandler(new PollWidgetHandler()); - widgetHandlerManager.registerHandler(new FriendRequestHandler()); widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler()); widgetHandlerManager.registerHandler(new FurnitureContextMenuWidgetHandler()); diff --git a/src/components/room/widgets/RoomWidgetsView.tsx b/src/components/room/widgets/RoomWidgetsView.tsx index 55bdeaad..2cc43ce7 100644 --- a/src/components/room/widgets/RoomWidgetsView.tsx +++ b/src/components/room/widgets/RoomWidgetsView.tsx @@ -1,8 +1,7 @@ -import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFavoriteGroupUpdateEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionPollEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer'; +import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFavoriteGroupUpdateEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionPollEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback } from 'react'; import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, NotificationAlertType, NotificationUtilities, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api'; -import { FriendRequestEvent } from '../../../events'; -import { UseRoomEngineEvent, UseRoomSessionManagerEvent, UseUiEvent } from '../../../hooks'; +import { UseRoomEngineEvent, UseRoomSessionManagerEvent } from '../../../hooks'; import { useRoomContext } from '../RoomContext'; import { AvatarInfoWidgetView } from './avatar-info/AvatarInfoWidgetView'; import { ChatInputView } from './chat-input/ChatInputView'; @@ -269,7 +268,6 @@ export const RoomWidgetsView: FC<{}> = props => UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.RSDE_REJECTED, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.RSDE_ACCEPTED, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionDimmerPresetsEvent.ROOM_DIMMER_PRESETS, onRoomSessionEvent); - UseRoomSessionManagerEvent(RoomSessionFriendRequestEvent.RSFRE_FRIEND_REQUEST, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionPresentEvent.RSPE_PRESENT_OPENED, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionPetInfoUpdateEvent.PET_INFO, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionWordQuizEvent.ANSWERED, onRoomSessionEvent); @@ -278,8 +276,6 @@ export const RoomWidgetsView: FC<{}> = props => UseRoomSessionManagerEvent(RoomSessionPollEvent.OFFER, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionPollEvent.ERROR, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionPollEvent.CONTENT, onRoomSessionEvent); - UseUiEvent(FriendRequestEvent.ACCEPTED, onRoomSessionEvent); - UseUiEvent(FriendRequestEvent.DECLINED, onRoomSessionEvent); const onRoomSessionErrorMessageEvent = useCallback((event: RoomSessionErrorMessageEvent) => { diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx index 98a04354..21dca4b6 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx @@ -1,9 +1,9 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { RoomControllerLevel, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { GetOwnRoomObject, GetUserProfile, LocalizeText, RoomWidgetMessage, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUserActionMessage } from '../../../../api'; +import { CreateLinkEvent, GetOwnRoomObject, GetUserProfile, LocalizeText, RoomWidgetMessage, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUserActionMessage } from '../../../../api'; import { Base, Flex } from '../../../../common'; -import { BatchUpdates } from '../../../../hooks'; +import { BatchUpdates, useFriends } from '../../../../hooks'; import { useRoomContext } from '../../RoomContext'; import { ContextMenuHeaderView } from '../context-menu/ContextMenuHeaderView'; import { ContextMenuListItemView } from '../context-menu/ContextMenuListItemView'; @@ -28,6 +28,7 @@ export const AvatarInfoWidgetAvatarView: FC = p const { userData = null, close = null } = props; const [ mode, setMode ] = useState(MODE_NORMAL); const [ respectsLeft, setRespectsLeft ] = useState(0); + const { canRequestFriend = null } = useFriends(); const { widgetHandler = null } = useRoomContext(); const isShowGiveRights = useMemo(() => @@ -107,8 +108,7 @@ export const AvatarInfoWidgetAvatarView: FC = p messageType = RoomWidgetUserActionMessage.WHISPER_USER; break; case 'friend': - //userData.canBeAskedAsFriend = false; - messageType = RoomWidgetUserActionMessage.SEND_FRIEND_REQUEST; + CreateLinkEvent(`friends/request/${ userData.webID }/${ userData.name }`); break; case 'relationship': hideMenu = false; @@ -222,7 +222,7 @@ export const AvatarInfoWidgetAvatarView: FC = p { (mode === MODE_NORMAL) && <> - { userData.canBeAskedAsFriend && + { canRequestFriend(userData.webID) && processAction('friend') }> { LocalizeText('infostand.button.friend') } } @@ -236,7 +236,7 @@ export const AvatarInfoWidgetAvatarView: FC = p processAction('respect') }> { LocalizeText('infostand.button.respect', [ 'count' ], [ respectsLeft.toString() ]) } } - { !userData.canBeAskedAsFriend && + { !canRequestFriend(userData.webID) && processAction('relationship') }> {LocalizeText('infostand.link.relationship')} diff --git a/src/components/room/widgets/friend-request/FriendRequestDialogView.tsx b/src/components/room/widgets/friend-request/FriendRequestDialogView.tsx deleted file mode 100644 index 1e230ec8..00000000 --- a/src/components/room/widgets/friend-request/FriendRequestDialogView.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { FC } from 'react'; -import { LocalizeText, RoomWidgetFriendRequestMessage } from '../../../../api'; -import { Base, Button, Column, Flex, Text } from '../../../../common'; -import { useRoomContext } from '../../RoomContext'; -import { UserLocationView } from '../user-location/UserLocationView'; - -interface FriendRequestDialogViewProps -{ - requestId: number; - userId: number; - userName: string; - close: () => void; -} - -export const FriendRequestDialogView: FC = props => -{ - const { requestId = -1, userId = -1, userName = null, close = null } = props; - const { widgetHandler = null } = useRoomContext(); - - const respond = (flag: boolean) => - { - widgetHandler.processWidgetMessage(new RoomWidgetFriendRequestMessage((flag ? RoomWidgetFriendRequestMessage.ACCEPT : RoomWidgetFriendRequestMessage.DECLINE), requestId)); - - close(); - } - - return ( - - - - - { LocalizeText('widget.friendrequest.from', [ 'username' ], [ userName ]) } - - - - - - - - - - ); -} diff --git a/src/components/room/widgets/friend-request/FriendRequestWidgetView.tsx b/src/components/room/widgets/friend-request/FriendRequestWidgetView.tsx index 996af565..4274b663 100644 --- a/src/components/room/widgets/friend-request/FriendRequestWidgetView.tsx +++ b/src/components/room/widgets/friend-request/FriendRequestWidgetView.tsx @@ -1,69 +1,123 @@ -import { FC, useCallback, useState } from 'react'; -import { RoomWidgetUpdateFriendRequestEvent } from '../../../../api'; -import { UseEventDispatcherHook } from '../../../../hooks'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { LocalizeText, MessengerRequest, RoomWidgetUpdateRoomObjectEvent } from '../../../../api'; +import { Base, Button, Column, Flex, Text } from '../../../../common'; +import { UseEventDispatcherHook, useFriends } from '../../../../hooks'; import { useRoomContext } from '../../RoomContext'; -import { FriendRequestDialogView } from './FriendRequestDialogView'; +import { ObjectLocationView } from '../object-location/ObjectLocationView'; export const FriendRequestWidgetView: FC<{}> = props => { - const [ friendRequests, setFriendRequests ] = useState<{ requestId: number, userId: number, userName: string }[]>([]); - const { eventDispatcher = null } = useRoomContext(); + const [ displayedRequests, setDisplayedRequests ] = useState<{ roomIndex: number, request: MessengerRequest }[]>([]); + const [ dismissedRequestIds, setDismissedRequestIds ] = useState([]); + const { roomSession = null, eventDispatcher = null } = useRoomContext(); + const { requests = [], requestResponse = null } = useFriends(); - const showFriendRequest = useCallback((requestId: number, userId: number, userName: string) => + const hideFriendRequest = (userId: number) => { - const index = friendRequests.findIndex(value => (value.userId === userId)); - - if(index >= 0) return; - - setFriendRequests(prevValue => + setDismissedRequestIds(prevValue => { + if(prevValue.indexOf(userId) >= 0) return prevValue; + const newValue = [ ...prevValue ]; - newValue.push({ requestId, userId, userName }); + newValue.push(userId); return newValue; }); - }, [ friendRequests ]); + } - const hideFriendRequest = useCallback((requestId: number) => + const onRoomWidgetUpdateRoomObjectEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) => { - const index = friendRequests.findIndex(value => (value.requestId === requestId)); + if(event.category !== RoomObjectCategory.UNIT) return; + + const userData = roomSession.userDataManager.getUserDataByIndex(event.id); - if(index === -1) return; - - setFriendRequests(prevValue => - { - const newValue = [ ...prevValue ]; - - newValue.splice(index, 1); - - return newValue; - }); - }, [ friendRequests ]); - - const onRoomWidgetUpdateFriendRequestEvent = useCallback((event: RoomWidgetUpdateFriendRequestEvent) => - { - switch(event.type) + if(userData && (userData.type === RoomObjectUserType.getTypeNumber(RoomObjectUserType.USER))) { - case RoomWidgetUpdateFriendRequestEvent.SHOW_FRIEND_REQUEST: - showFriendRequest(event.requestId, event.userId, event.userName); - return; - case RoomWidgetUpdateFriendRequestEvent.HIDE_FRIEND_REQUEST: - hideFriendRequest(event.requestId); - return; + if(event.type === RoomWidgetUpdateRoomObjectEvent.USER_ADDED) + { + const request = requests.find(request => (request.requesterUserId === userData.webID)); + + if(!request || displayedRequests.find(request => (request.request.requesterUserId === userData.webID))) return; + + const newValue = [ ...displayedRequests ]; + + newValue.push({ roomIndex: userData.roomIndex, request }); + + setDisplayedRequests(newValue); + } + + return; } - }, [ showFriendRequest, hideFriendRequest ]); - UseEventDispatcherHook(RoomWidgetUpdateFriendRequestEvent.SHOW_FRIEND_REQUEST, eventDispatcher, onRoomWidgetUpdateFriendRequestEvent); - UseEventDispatcherHook(RoomWidgetUpdateFriendRequestEvent.HIDE_FRIEND_REQUEST, eventDispatcher, onRoomWidgetUpdateFriendRequestEvent); + if(event.type === RoomWidgetUpdateRoomObjectEvent.USER_REMOVED) + { + const index = displayedRequests.findIndex(request => (request.roomIndex === event.id)); - if(!friendRequests.length) return null; + if(index === -1) return; + + const newValue = [ ...displayedRequests ]; + + newValue.splice(index, 1); + + setDisplayedRequests(newValue); + } + }, [ roomSession, requests, displayedRequests ]); + + UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_ADDED, eventDispatcher, onRoomWidgetUpdateRoomObjectEvent); + UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, eventDispatcher, onRoomWidgetUpdateRoomObjectEvent); + + useEffect(() => + { + if(!requests || !requests.length) return; + + const newDisplayedRequests: { roomIndex: number, request: MessengerRequest }[] = []; + + for(const request of requests) + { + const userData = roomSession.userDataManager.getUserData(request.requesterUserId); + + if(!userData) continue; + + newDisplayedRequests.push({ roomIndex: userData.roomIndex, request }); + } + + setDisplayedRequests(newDisplayedRequests); + }, [ roomSession, requests ]); + + if(!requests.length) return null; + + const FriendRequestDialogView: FC<{ roomIndex: number, request: MessengerRequest }> = props => + { + const { roomIndex = -1, request = null } = props; + + return ( + + + + + { LocalizeText('widget.friendrequest.from', [ 'username' ], [ request.name ]) } + hideFriendRequest(request.requesterUserId) } /> + + + + + + + + + ); + } return ( <> - { friendRequests.map((request, index) => + { displayedRequests.map((request, index) => { - return hideFriendRequest(request.userId) } /> + if(dismissedRequestIds.indexOf(request.request.requesterUserId) >= 0) return null; + + return ; }) } ); diff --git a/src/components/toolbar/ToolbarMeView.tsx b/src/components/toolbar/ToolbarMeView.tsx index 6e0b7a3e..7861c38f 100644 --- a/src/components/toolbar/ToolbarMeView.tsx +++ b/src/components/toolbar/ToolbarMeView.tsx @@ -1,19 +1,21 @@ -import { RoomObjectCategory } from '@nitrots/nitro-renderer'; -import { FC, useEffect } from 'react'; -import { CreateLinkEvent, GetRoomEngine, GetRoomSession } from '../../api'; +import { MouseEventType, RoomObjectCategory } from '@nitrots/nitro-renderer'; +import { Dispatch, FC, SetStateAction, useEffect, useRef } from 'react'; +import { CreateLinkEvent, GetRoomEngine, GetRoomSession, GetSessionDataManager, GetUserProfile } from '../../api'; import { Base, Flex, LayoutItemCountView } from '../../common'; -import { ToolbarViewItems } from './common/ToolbarViewItems'; +import { GuideToolEvent, UserSettingsUIEvent } from '../../events'; +import { DispatchUiEvent } from '../../hooks'; -export interface ToolbarMeViewProps +interface ToolbarMeViewProps { useGuideTool: boolean; unseenAchievementCount: number; - handleToolbarItemClick: (item: string) => void; + setMeExpanded: Dispatch>; } export const ToolbarMeView: FC = props => { - const { useGuideTool = false, unseenAchievementCount = 0, handleToolbarItemClick = null } = props; + const { useGuideTool = false, unseenAchievementCount = 0, setMeExpanded = null, children = null, ...rest } = props; + const elementRef = useRef(); useEffect(() => { @@ -24,18 +26,28 @@ export const ToolbarMeView: FC = props => GetRoomEngine().selectRoomObject(roomSession.roomId, roomSession.ownRoomIndex, RoomObjectCategory.UNIT); }, []); + useEffect(() => + { + const onClick = (event: MouseEvent) => setMeExpanded(false); + + document.addEventListener('click', onClick); + + return () => document.removeEventListener(MouseEventType.MOUSE_CLICK, onClick); + }, [ setMeExpanded ]); + return ( - + { useGuideTool && - handleToolbarItemClick(ToolbarViewItems.GUIDE_TOOL_ITEM) } /> } - handleToolbarItemClick(ToolbarViewItems.ACHIEVEMENTS_ITEM) }> + DispatchUiEvent(new GuideToolEvent(GuideToolEvent.TOGGLE_GUIDE_TOOL)) } /> } + CreateLinkEvent('achievements/toggle') }> { (unseenAchievementCount > 0) && } - handleToolbarItemClick(ToolbarViewItems.PROFILE_ITEM) } /> + GetUserProfile(GetSessionDataManager().userId) } /> CreateLinkEvent('navigator/search/myworld_view')} /> - handleToolbarItemClick(ToolbarViewItems.CLOTHING_ITEM) } /> - handleToolbarItemClick(ToolbarViewItems.SETTINGS_ITEM) } /> + CreateLinkEvent('avatar-editor/toggle') } /> + DispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS)) } /> + { children } ); } diff --git a/src/components/toolbar/ToolbarView.tsx b/src/components/toolbar/ToolbarView.tsx index c502baed..75e8e313 100644 --- a/src/components/toolbar/ToolbarView.tsx +++ b/src/components/toolbar/ToolbarView.tsx @@ -1,22 +1,12 @@ import { Dispose, DropBounce, EaseOut, FigureUpdateEvent, JumpBy, Motions, NitroToolbarAnimateIconEvent, PerkAllowancesMessageEvent, PerkEnum, Queue, UserInfoDataParser, UserInfoEvent, Wait } from '@nitrots/nitro-renderer'; import { FC, useCallback, useState } from 'react'; -import { CreateLinkEvent, GetSessionDataManager, GetUserProfile, OpenMessengerChat, VisitDesktop } from '../../api'; +import { CreateLinkEvent, GetSessionDataManager, MessengerIconState, OpenMessengerChat, VisitDesktop } from '../../api'; import { Base, Flex, LayoutAvatarImageView, LayoutItemCountView, TransitionAnimation, TransitionAnimationTypes } from '../../common'; -import { AchievementsUIUnseenCountEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, GuideToolEvent, ModToolsEvent, UserSettingsUIEvent } from '../../events'; -import { BatchUpdates, DispatchUiEvent, useInventoryUnseenTracker, UseMessageEventHook, UseRoomEngineEvent, UseUiEvent } from '../../hooks'; -import { ToolbarViewItems } from './common/ToolbarViewItems'; +import { AchievementsUIUnseenCountEvent, ModToolsEvent } from '../../events'; +import { BatchUpdates, DispatchUiEvent, useFriends, useInventoryUnseenTracker, UseMessageEventHook, useMessenger, UseRoomEngineEvent, UseUiEvent } from '../../hooks'; import { ToolbarMeView } from './ToolbarMeView'; -export interface ToolbarViewProps -{ - isInRoom: boolean; -} - -const CHAT_ICON_HIDDEN: number = 0; -const CHAT_ICON_SHOWING: number = 1; -const CHAT_ICON_UNREAD: number = 2; - -export const ToolbarView: FC = props => +export const ToolbarView: FC<{ isInRoom: boolean }> = props => { const { isInRoom } = props; @@ -24,10 +14,10 @@ export const ToolbarView: FC = props => const [ userFigure, setUserFigure ] = useState(null); const [ isMeExpanded, setMeExpanded ] = useState(false); const [ useGuideTool, setUseGuideTool ] = useState(false); - const [ chatIconType, setChatIconType ] = useState(CHAT_ICON_HIDDEN); const [ unseenAchievementCount, setUnseenAchievementCount ] = useState(0); - const [ unseenFriendRequestCount, setFriendRequestCount ] = useState(0); const { getFullCount = null } = useInventoryUnseenTracker(); + const { requests = [] } = useFriends(); + const { iconState = MessengerIconState.HIDDEN } = useMessenger(); const isMod = GetSessionDataManager().isModerator; const onUserInfoEvent = useCallback((event: UserInfoEvent) => @@ -61,13 +51,6 @@ export const ToolbarView: FC = props => UseMessageEventHook(PerkAllowancesMessageEvent, onPerkAllowancesMessageEvent); - const onFriendsMessengerIconEvent = useCallback((event: FriendsMessengerIconEvent) => - { - setChatIconType(event.iconType); - }, []); - - UseUiEvent(FriendsMessengerIconEvent.UPDATE_ICON, onFriendsMessengerIconEvent); - const onAchievementsUIUnseenCountEvent = useCallback((event: AchievementsUIUnseenCountEvent) => { setUnseenAchievementCount(event.count); @@ -75,13 +58,6 @@ export const ToolbarView: FC = props => UseUiEvent(AchievementsUIUnseenCountEvent.UNSEEN_COUNT, onAchievementsUIUnseenCountEvent); - const onFriendsRequestCountEvent = useCallback((event: FriendsRequestCountEvent) => - { - setFriendRequestCount(event.count); - }, []); - - UseUiEvent(FriendsRequestCountEvent.UPDATE_COUNT, onFriendsRequestCountEvent); - const animationIconToToolbar = useCallback((iconName: string, image: HTMLImageElement, x: number, y: number) => { const target = (document.body.getElementsByClassName(iconName)[0] as HTMLElement); @@ -123,60 +99,12 @@ export const ToolbarView: FC = props => UseRoomEngineEvent(NitroToolbarAnimateIconEvent.ANIMATE_ICON, onNitroToolbarAnimateIconEvent); - const handleToolbarItemClick = useCallback((item: string) => - { - switch(item) - { - case ToolbarViewItems.NAVIGATOR_ITEM: - CreateLinkEvent('navigator/toggle'); - return; - case ToolbarViewItems.INVENTORY_ITEM: - CreateLinkEvent('inventory/toggle'); - return; - case ToolbarViewItems.CATALOG_ITEM: - CreateLinkEvent('catalog/toggle'); - return; - case ToolbarViewItems.FRIEND_LIST_ITEM: - DispatchUiEvent(new FriendsEvent(FriendsEvent.TOGGLE_FRIEND_LIST)); - return; - case ToolbarViewItems.CAMERA_ITEM: - CreateLinkEvent('camera/toggle'); - return; - case ToolbarViewItems.CLOTHING_ITEM: - CreateLinkEvent('avatar-editor/toggle'); - setMeExpanded(false); - return; - case ToolbarViewItems.MOD_TOOLS_ITEM: - DispatchUiEvent(new ModToolsEvent(ModToolsEvent.TOGGLE_MOD_TOOLS)); - return; - case ToolbarViewItems.ACHIEVEMENTS_ITEM: - CreateLinkEvent('achievements/toggle'); - setMeExpanded(false); - return; - case ToolbarViewItems.PROFILE_ITEM: - GetUserProfile(GetSessionDataManager().userId); - setMeExpanded(false); - return; - case ToolbarViewItems.SETTINGS_ITEM: - DispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS)); - setMeExpanded(false); - return; - case ToolbarViewItems.GUIDE_TOOL_ITEM: - DispatchUiEvent(new GuideToolEvent(GuideToolEvent.TOGGLE_GUIDE_TOOL)); - setMeExpanded(false); - return; - case ToolbarViewItems.FRIEND_CHAT_ITEM: - OpenMessengerChat(); - return; - } - }, []); - const unseenInventoryCount = getFullCount(); return ( <> - + @@ -190,27 +118,27 @@ export const ToolbarView: FC = props => VisitDesktop() } /> } { !isInRoom && CreateLinkEvent('navigator/goto/home') } /> } - handleToolbarItemClick(ToolbarViewItems.NAVIGATOR_ITEM) } /> - handleToolbarItemClick(ToolbarViewItems.CATALOG_ITEM) } /> - handleToolbarItemClick(ToolbarViewItems.INVENTORY_ITEM) }> + CreateLinkEvent('navigator/toggle') } /> + CreateLinkEvent('catalog/toggle') } /> + CreateLinkEvent('inventory/toggle') }> { (unseenInventoryCount > 0) && } { isInRoom && - handleToolbarItemClick(ToolbarViewItems.CAMERA_ITEM) } /> } + CreateLinkEvent('camera/toggle') } /> } { isMod && - handleToolbarItemClick(ToolbarViewItems.MOD_TOOLS_ITEM) } /> } + DispatchUiEvent(new ModToolsEvent(ModToolsEvent.TOGGLE_MOD_TOOLS)) } /> } - handleToolbarItemClick(ToolbarViewItems.FRIEND_LIST_ITEM) }> - { (unseenFriendRequestCount > 0) && - } + CreateLinkEvent('friends/toggle') }> + { (requests.length > 0) && + } - { ((chatIconType === CHAT_ICON_SHOWING) || (chatIconType === CHAT_ICON_UNREAD)) && - handleToolbarItemClick(ToolbarViewItems.FRIEND_CHAT_ITEM) } /> } + { ((iconState === MessengerIconState.SHOW) || (iconState === MessengerIconState.UNREAD)) && + OpenMessengerChat() } /> } diff --git a/src/events/friends/FriendListContentEvent.ts b/src/events/friends/FriendListContentEvent.ts deleted file mode 100644 index 61d509c9..00000000 --- a/src/events/friends/FriendListContentEvent.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; -import { MessengerFriend } from '../../components/friends/common/MessengerFriend'; - -export class FriendListContentEvent extends NitroEvent -{ - public static FRIEND_LIST_CONTENT: string = 'FLSFRE_FRIEND_LIST_CONTENT'; - - private _friends: Map; - - constructor(friends: MessengerFriend[]) - { - super(FriendListContentEvent.FRIEND_LIST_CONTENT); - - this._friends = new Map(); - - friends.forEach(entry => - { - this._friends.set(entry.id, entry.name); - }); - } - - public get friends(): Map - { - return this._friends; - } -} diff --git a/src/events/friends/FriendRequestEvent.ts b/src/events/friends/FriendRequestEvent.ts deleted file mode 100644 index 33c78132..00000000 --- a/src/events/friends/FriendRequestEvent.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; - -export class FriendRequestEvent extends NitroEvent -{ - public static ACCEPTED: string = 'FRE_ACCEPTED'; - public static DECLINED: string = 'FRE_DECLINED'; - - private _requestId: number; - - constructor(type: string, requestId: number) - { - super(type); - - this._requestId = requestId; - } - - public get requestId(): number - { - return this._requestId; - } -} diff --git a/src/events/friends/FriendsAcceptFriendRequestEvent.ts b/src/events/friends/FriendsAcceptFriendRequestEvent.ts deleted file mode 100644 index 565cd5f0..00000000 --- a/src/events/friends/FriendsAcceptFriendRequestEvent.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; - -export class FriendsAcceptFriendRequestEvent extends NitroEvent -{ - public static ACCEPT_FRIEND_REQUEST: string = 'FAFRE_ACCEPT_FRIEND_REQUEST'; - - private _requestId: number; - - constructor(requestId: number) - { - super(FriendsAcceptFriendRequestEvent.ACCEPT_FRIEND_REQUEST); - - this._requestId = requestId; - } - - public get requestId(): number - { - return this._requestId; - } -} diff --git a/src/events/friends/FriendsDeclineFriendRequestEvent.ts b/src/events/friends/FriendsDeclineFriendRequestEvent.ts deleted file mode 100644 index 3fa71ac6..00000000 --- a/src/events/friends/FriendsDeclineFriendRequestEvent.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; - -export class FriendsDeclineFriendRequestEvent extends NitroEvent -{ - public static DECLINE_FRIEND_REQUEST: string = 'FAFRE_DECLINE_FRIEND_REQUEST'; - - private _requestId: number; - - constructor(requestId: number) - { - super(FriendsDeclineFriendRequestEvent.DECLINE_FRIEND_REQUEST); - - this._requestId = requestId; - } - - public get requestId(): number - { - return this._requestId; - } -} diff --git a/src/events/friends/FriendsEvent.ts b/src/events/friends/FriendsEvent.ts deleted file mode 100644 index 0f8be1d7..00000000 --- a/src/events/friends/FriendsEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; - -export class FriendsEvent extends NitroEvent -{ - public static SHOW_FRIEND_LIST: string = 'IE_SHOW_FRIEND_LIST'; - public static TOGGLE_FRIEND_LIST: string = 'IE_TOGGLE_FRIEND_LIST'; - public static SHOW_FRIEND_MESSENGER: string = 'IE_SHOW_FRIEND_MESSENGER'; - public static TOGGLE_FRIEND_MESSENGER: string = 'IE_TOGGLE_FRIEND_MESSENGER'; - public static REQUEST_FRIEND_LIST: string = 'FLSFRE_REQUEST_FRIEND_LIST'; -} diff --git a/src/events/friends/FriendsMessengerIconEvent.ts b/src/events/friends/FriendsMessengerIconEvent.ts deleted file mode 100644 index 602eb726..00000000 --- a/src/events/friends/FriendsMessengerIconEvent.ts +++ /dev/null @@ -1,23 +0,0 @@ -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; - } -} diff --git a/src/events/friends/FriendsRequestCountEvent.ts b/src/events/friends/FriendsRequestCountEvent.ts deleted file mode 100644 index 884819e0..00000000 --- a/src/events/friends/FriendsRequestCountEvent.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; - -export class FriendsRequestCountEvent extends NitroEvent -{ - public static UPDATE_COUNT: string = 'FRCE_UPDATE_COUNT'; - - private _count: number; - - constructor(count: number) - { - super(FriendsRequestCountEvent.UPDATE_COUNT); - - this._count = count; - } - - public get count(): number - { - return this._count; - } -} diff --git a/src/events/friends/FriendsSendFriendRequestEvent.ts b/src/events/friends/FriendsSendFriendRequestEvent.ts deleted file mode 100644 index 177ce5b7..00000000 --- a/src/events/friends/FriendsSendFriendRequestEvent.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NitroEvent } from '@nitrots/nitro-renderer'; - -export class FriendsSendFriendRequestEvent extends NitroEvent -{ - public static SEND_FRIEND_REQUEST: string = 'FLSFRE_SEND_FRIEND_REQUEST'; - - private _userId: number; - private _userName: string; - - constructor(userId: number, userName: string) - { - super(FriendsSendFriendRequestEvent.SEND_FRIEND_REQUEST); - - this._userId = userId; - this._userName = userName; - } - - public get userId(): number - { - return this._userId; - } - - public get userName(): string - { - return this._userName; - } -} diff --git a/src/events/friends/index.ts b/src/events/friends/index.ts index 21f764ef..fe4cbb5b 100644 --- a/src/events/friends/index.ts +++ b/src/events/friends/index.ts @@ -1,9 +1 @@ export * from './FriendEnteredRoomEvent'; -export * from './FriendListContentEvent'; -export * from './FriendRequestEvent'; -export * from './FriendsAcceptFriendRequestEvent'; -export * from './FriendsDeclineFriendRequestEvent'; -export * from './FriendsEvent'; -export * from './FriendsMessengerIconEvent'; -export * from './FriendsRequestCountEvent'; -export * from './FriendsSendFriendRequestEvent'; diff --git a/src/hooks/friends/index.ts b/src/hooks/friends/index.ts new file mode 100644 index 00000000..45c983f9 --- /dev/null +++ b/src/hooks/friends/index.ts @@ -0,0 +1,2 @@ +export * from './useFriends'; +export * from './useMessenger'; diff --git a/src/hooks/friends/useFriends.ts b/src/hooks/friends/useFriends.ts new file mode 100644 index 00000000..6b70c0d2 --- /dev/null +++ b/src/hooks/friends/useFriends.ts @@ -0,0 +1,265 @@ +import { AcceptFriendMessageComposer, DeclineFriendMessageComposer, FollowFriendMessageComposer, FriendListFragmentEvent, FriendListUpdateEvent, FriendParser, FriendRequestsEvent, GetFriendRequestsComposer, MessengerInitComposer, MessengerInitEvent, NewFriendRequestEvent, RequestFriendComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useBetween } from 'use-between'; +import { CloneObject, MessengerFriend, MessengerRequest, MessengerSettings, SendMessageComposer } from '../../api'; +import { UseMessageEventHook } from '../messages'; + +const useFriendsState = () => +{ + const [ friends, setFriends ] = useState([]); + const [ requests, setRequests ] = useState([]); + const [ sentRequests, setSentRequests ] = useState([]); + const [ settings, setSettings ] = useState(null); + + const onlineFriends = useMemo(() => + { + const onlineFriends = friends.filter(friend => friend.online); + + onlineFriends.sort((a, b) => + { + if( a.name < b.name ) return -1; + + if( a.name > b.name ) return 1; + + return 0; + }); + + return onlineFriends; + }, [ friends ]); + + const offlineFriends = useMemo(() => + { + const offlineFriends = friends.filter(friend => !friend.online); + + offlineFriends.sort((a, b) => + { + if( a.name < b.name ) return -1; + + if( a.name > b.name ) return 1; + + return 0; + }); + + return offlineFriends; + }, [ friends ]); + + const followFriend = useCallback((friend: MessengerFriend) => SendMessageComposer(new FollowFriendMessageComposer(friend.id)), []); + const updateRelationship = useCallback((friend: MessengerFriend, type: number) => ((type !== friend.relationshipStatus) && SendMessageComposer(new SetRelationshipStatusComposer(friend.id, type))), []); + + const getFriend = useCallback((userId: number) => + { + for(const friend of friends) + { + if(friend.id === userId) return friend; + } + + return null; + }, [ friends ]); + + const canRequestFriend = useCallback((userId: number) => + { + if(getFriend(userId)) return false; + + if(requests.find(request => (request.requesterUserId === userId))) return false; + + if(sentRequests.indexOf(userId) >= 0) return false; + + return true; + }, [ requests, sentRequests, getFriend ]); + + const requestFriend = useCallback((userId: number, userName: string) => + { + if(!canRequestFriend(userId)) return false; + + setSentRequests(prevValue => + { + const newSentRequests = [ ...prevValue ]; + + newSentRequests.push(userId); + + return newSentRequests; + }); + + SendMessageComposer(new RequestFriendComposer(userName)); + }, [ canRequestFriend ]); + + const requestResponse = useCallback((requestId: number, flag: boolean) => + { + if(requestId === -1 && !flag) + { + SendMessageComposer(new DeclineFriendMessageComposer(true)); + + setRequests([]); + } + else + { + setRequests(prevValue => + { + const newRequests = [ ...prevValue ]; + const index = newRequests.findIndex(request => (request.id === requestId)); + + if(index === -1) return prevValue; + + if(flag) + { + SendMessageComposer(new AcceptFriendMessageComposer(newRequests[index].id)); + } + else + { + SendMessageComposer(new DeclineFriendMessageComposer(false, newRequests[index].id)); + } + + newRequests.splice(index, 1); + + return newRequests; + }); + } + }, []); + + const onMessengerInitEvent = useCallback((event: MessengerInitEvent) => + { + const parser = event.getParser(); + + setSettings(new MessengerSettings( + parser.userFriendLimit, + parser.normalFriendLimit, + parser.extendedFriendLimit, + parser.categories)); + + SendMessageComposer(new GetFriendRequestsComposer()); + }, []); + + UseMessageEventHook(MessengerInitEvent, onMessengerInitEvent); + + const onFriendsFragmentEvent = useCallback((event: FriendListFragmentEvent) => + { + const parser = event.getParser(); + + setFriends(prevValue => + { + const newValue = [ ...prevValue ]; + + for(const friend of parser.fragment) + { + const index = newValue.findIndex(existingFriend => (existingFriend.id === friend.id)); + const newFriend = new MessengerFriend(); + newFriend.populate(friend); + + if(index > -1) newValue[index] = newFriend; + else newValue.push(newFriend); + } + + return newValue; + }); + }, []); + + UseMessageEventHook(FriendListFragmentEvent, onFriendsFragmentEvent); + + const onFriendsUpdateEvent = useCallback((event: FriendListUpdateEvent) => + { + const parser = event.getParser(); + + setFriends(prevValue => + { + const newValue = [ ...prevValue ]; + + const processUpdate = (friend: FriendParser) => + { + const index = newValue.findIndex(existingFriend => (existingFriend.id === friend.id)); + + if(index === -1) + { + const newFriend = new MessengerFriend(); + newFriend.populate(friend); + + newValue.unshift(newFriend); + } + else + { + newValue[index].populate(friend); + } + } + + for(const friend of parser.addedFriends) processUpdate(friend); + + for(const friend of parser.updatedFriends) processUpdate(friend); + + for(const removedFriendId of parser.removedFriendIds) + { + const index = newValue.findIndex(existingFriend => (existingFriend.id === removedFriendId)); + + if(index > -1) newValue.splice(index); + } + + return newValue; + }); + }, []); + + UseMessageEventHook(FriendListUpdateEvent, onFriendsUpdateEvent); + + const onFriendRequestsEvent = useCallback((event: FriendRequestsEvent) => + { + const parser = event.getParser(); + + setRequests(prevValue => + { + const newValue = [ ...prevValue ]; + + for(const request of parser.requests) + { + const index = newValue.findIndex(existing => (existing.requesterUserId === request.requesterUserId)); + + if(index > 0) + { + newValue[index] = CloneObject(newValue[index]); + newValue[index].populate(request); + } + else + { + const newRequest = new MessengerRequest(); + newRequest.populate(request); + + newValue.push(newRequest); + } + } + + return newValue; + }); + }, []); + + UseMessageEventHook(FriendRequestsEvent, onFriendRequestsEvent); + + const onNewFriendRequestEvent = useCallback((event: NewFriendRequestEvent) => + { + const parser = event.getParser(); + const request = parser.request; + + setRequests(prevValue => + { + const newRequests = [ ...prevValue ]; + + const index = newRequests.findIndex(existing => (existing.requesterUserId === request.requesterUserId)); + + if(index === -1) + { + const newRequest = new MessengerRequest(); + newRequest.populate(request); + + newRequests.push(newRequest); + } + + return newRequests; + }); + }, []); + + UseMessageEventHook(NewFriendRequestEvent, onNewFriendRequestEvent); + + useEffect(() => + { + SendMessageComposer(new MessengerInitComposer()); + }, []); + + return { friends, requests, sentRequests, settings, onlineFriends, offlineFriends, getFriend, canRequestFriend, requestFriend, requestResponse, followFriend, updateRelationship }; +} + +export const useFriends = () => useBetween(useFriendsState); diff --git a/src/hooks/friends/useMessenger.ts b/src/hooks/friends/useMessenger.ts new file mode 100644 index 00000000..00259b21 --- /dev/null +++ b/src/hooks/friends/useMessenger.ts @@ -0,0 +1,222 @@ +import { NewConsoleMessageEvent, RoomInviteErrorEvent, RoomInviteEvent, SendMessageComposer as SendMessageComposerPacket } from '@nitrots/nitro-renderer'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useBetween } from 'use-between'; +import { CloneObject, GetSessionDataManager, LocalizeText, MessengerIconState, MessengerThread, MessengerThreadChat, NotificationAlertType, NotificationUtilities, PlaySound, SendMessageComposer, SoundNames } from '../../api'; +import { UseMessageEventHook } from '../messages'; +import { useFriends } from './useFriends'; + +const useMessengerState = () => +{ + const [ messageThreads, setMessageThreads ] = useState([]); + const [ activeThreadId, setActiveThreadId ] = useState(-1); + const [ hiddenThreadIds, setHiddenThreadIds ] = useState([]); + const [ iconState, setIconState ] = useState(MessengerIconState.HIDDEN); + const { getFriend = null } = useFriends(); + + const visibleThreads = useMemo(() => messageThreads.filter(thread => (hiddenThreadIds.indexOf(thread.threadId) === -1)), [ messageThreads, hiddenThreadIds ]); + const activeThread = useMemo(() => ((activeThreadId > 0) && visibleThreads.find(thread => (thread.threadId === activeThreadId) || null)), [ activeThreadId, visibleThreads ]); + + const getMessageThread = useCallback((userId: number) => + { + let thread = messageThreads.find(thread => (thread.participant && (thread.participant.id === userId))); + + if(thread) return thread; + + const friend = getFriend(userId); + + if(!friend) return null; + + thread = new MessengerThread(friend); + + setMessageThreads(prevValue => + { + const newValue = [ ...prevValue ]; + + newValue.push(thread); + + return newValue; + }); + + setHiddenThreadIds(prevValue => + { + const index = prevValue.indexOf(thread.threadId); + + if(index === -1) return prevValue; + + const newValue = [ ...prevValue ]; + + newValue.splice(index, 1); + + return newValue; + }); + + return thread; + }, [ messageThreads, getFriend ]); + + const setActiveThread = useCallback((thread: MessengerThread) => + { + if(!thread) + { + setActiveThreadId(-1); + + return; + } + + setMessageThreads(prevValue => + { + const newValue = [ ...prevValue ]; + const index = newValue.findIndex(newThread => (newThread.threadId === thread.threadId)); + + if(index === -1) return prevValue; + + const newThread = CloneObject(newValue[index]); + + newValue[index] = newThread; + + newThread.setRead(); + + return newValue; + }); + + setActiveThreadId(thread.threadId); + }, []); + + const closeThread = (threadId: number) => + { + setHiddenThreadIds(prevValue => + { + const newValue = [ ...prevValue ]; + + if(newValue.indexOf(threadId) >= 0) return prevValue; + + newValue.push(threadId); + + return newValue; + }); + } + + const sendMessage = (thread: MessengerThread, text: string) => + { + if(!thread || !text || !text.length) return; + + SendMessageComposer(new SendMessageComposerPacket(thread.participant.id, text)); + + if((messageThreads.length === 1) && (thread.groups.length === 1)) PlaySound(SoundNames.MESSENGER_NEW_THREAD); + + setMessageThreads(prevValue => + { + const newValue = [ ...prevValue ]; + const index = newValue.findIndex(newThread => (newThread.threadId === thread.threadId)); + + if(index === -1) return prevValue; + + const newThread = CloneObject(newValue[index]); + + newValue[index] = newThread; + + newThread.addMessage(GetSessionDataManager().userId, text, 0, null, MessengerThreadChat.CHAT); + + if(activeThreadId === newThread.threadId) newThread.setRead(); + + return newValue; + }); + } + + const onNewConsoleMessageEvent = useCallback((event: NewConsoleMessageEvent) => + { + const parser = event.getParser(); + const thread = getMessageThread(parser.senderId); + + if(!thread) return; + + setMessageThreads(prevValue => + { + const newValue = [ ...prevValue ]; + const index = newValue.findIndex(newThread => (newThread.threadId === thread.threadId)); + + if(index === -1) return prevValue; + + const newThread = CloneObject(newValue[index]); + + newValue[index] = newThread; + + newThread.addMessage(parser.senderId, parser.messageText, parser.secondsSinceSent, parser.extraData); + + if(activeThreadId === newThread.threadId) newThread.setRead(); + + if(newThread.unreadCount > 0) PlaySound(SoundNames.MESSENGER_MESSAGE_RECEIVED); + + return newValue; + }); + }, [ activeThreadId, getMessageThread ]); + + UseMessageEventHook(NewConsoleMessageEvent, onNewConsoleMessageEvent); + + const onRoomInviteEvent = useCallback((event: RoomInviteEvent) => + { + const parser = event.getParser(); + const thread = getMessageThread(parser.senderId); + + if(!thread) return; + + setMessageThreads(prevValue => + { + const newValue = [ ...prevValue ]; + const index = newValue.findIndex(newThread => (newThread.threadId === thread.threadId)); + + if(index === -1) return prevValue; + + const newThread = CloneObject(newValue[index]); + + newValue[index] = newThread; + + newThread.addMessage(null, parser.messageText, 0, null, MessengerThreadChat.ROOM_INVITE); + + if(activeThreadId === newThread.threadId) newThread.setRead(); + + if(newThread.unreadCount > 0) PlaySound(SoundNames.MESSENGER_MESSAGE_RECEIVED); + + return newValue; + }); + }, [ activeThreadId, getMessageThread ]); + + UseMessageEventHook(RoomInviteEvent, onRoomInviteEvent); + + const onRoomInviteErrorEvent = useCallback((event: RoomInviteErrorEvent) => + { + const parser = event.getParser(); + const message = ((('Received room invite error: errorCode: ' + parser.errorCode) + ', recipients: ') + parser.failedRecipients); + + NotificationUtilities.simpleAlert(message, NotificationAlertType.DEFAULT, null, null, LocalizeText('friendlist.alert.title')); + }, []); + + UseMessageEventHook(RoomInviteErrorEvent, onRoomInviteErrorEvent); + + useEffect(() => + { + setIconState(prevValue => + { + if(!visibleThreads.length) return MessengerIconState.HIDDEN; + + let isUnread = false; + + for(const thread of visibleThreads) + { + if(thread.unreadCount > 0) + { + isUnread = true; + + break; + } + } + + if(isUnread) return MessengerIconState.UNREAD; + + return MessengerIconState.SHOW; + }); + }, [ visibleThreads ]); + + return { messageThreads, activeThread, iconState, visibleThreads, getMessageThread, setActiveThread, closeThread, sendMessage }; +} + +export const useMessenger = () => useBetween(useMessengerState); diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 615bbe74..431fa1a9 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -3,6 +3,7 @@ export * from './events'; export * from './events/core'; export * from './events/nitro'; export * from './events/ui'; +export * from './friends'; export * from './inventory'; export * from './messages'; export * from './navigator';