diff --git a/src/components/help/HelpView.scss b/src/components/help/HelpView.scss index 02397a8a..5335a6ad 100644 --- a/src/components/help/HelpView.scss +++ b/src/components/help/HelpView.scss @@ -12,3 +12,7 @@ .nitro-cfh-sanction-status { width: 400px; } + +.nitro-change-username { + width: 300px; +} diff --git a/src/components/help/HelpView.tsx b/src/components/help/HelpView.tsx index e70c7049..1c823c64 100644 --- a/src/components/help/HelpView.tsx +++ b/src/components/help/HelpView.tsx @@ -10,6 +10,7 @@ import { HelpContextProvider } from './HelpContext'; import { HelpMessageHandler } from './HelpMessageHandler'; import { DescribeReportView } from './views/DescribeReportView'; import { HelpIndexView } from './views/HelpIndexView'; +import { NameChangeView } from './views/name-change/NameChangeView'; import { SanctionSatusView } from './views/SanctionStatusView'; import { SelectReportedChatsView } from './views/SelectReportedChatsView'; import { SelectReportedUserView } from './views/SelectReportedUserView'; @@ -124,6 +125,7 @@ export const HelpView: FC<{}> = props => } + ); } diff --git a/src/components/help/views/name-change/NameChangeConfirmationView.tsx b/src/components/help/views/name-change/NameChangeConfirmationView.tsx new file mode 100644 index 00000000..6a91d4c2 --- /dev/null +++ b/src/components/help/views/name-change/NameChangeConfirmationView.tsx @@ -0,0 +1,47 @@ +import { ChangeUserNameMessageComposer, UserNameChangeMessageEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { GetSessionDataManager, LocalizeText } from '../../../../api'; +import { CreateMessageHook, SendMessageHook } from '../../../../hooks'; +import { NameChangeLayoutViewProps } from './NameChangeView.types'; + +export const NameChangeConfirmationView:FC = props => +{ + const { username = '', onAction = null } = props; + + const [ isConfirming, setIsConfirming ] = useState(false); + + const onUserNameChangeMessageEvent = useCallback((event: UserNameChangeMessageEvent) => + { + const parser = event.getParser(); + + if(!parser) return; + + if(parser.webId !== GetSessionDataManager().userId) return; + + onAction('close'); + }, [ onAction ]); + + CreateMessageHook(UserNameChangeMessageEvent, onUserNameChangeMessageEvent); + + const confirm = useCallback(() => + { + if(isConfirming) return; + + setIsConfirming(true); + SendMessageHook(new ChangeUserNameMessageComposer(username)); + }, [ isConfirming, username ]); + + return ( +
+
{ LocalizeText('tutorial.name_change.info.confirm') }
+
+
{ LocalizeText('tutorial.name_change.confirm') }
+
{ username }
+
+
+ + +
+
+ ); +} diff --git a/src/components/help/views/name-change/NameChangeInitView.tsx b/src/components/help/views/name-change/NameChangeInitView.tsx new file mode 100644 index 00000000..d995dc3c --- /dev/null +++ b/src/components/help/views/name-change/NameChangeInitView.tsx @@ -0,0 +1,19 @@ +import { FC } from 'react'; +import { GetSessionDataManager, LocalizeText } from '../../../../api'; +import { NameChangeLayoutViewProps } from './NameChangeView.types'; + +export const NameChangeInitView:FC = props => +{ + const { onAction = null } = props; + + return ( +
+
{ LocalizeText('tutorial.name_change.info.main') }
+
{ LocalizeText('tutorial.name_change.current', ['name'], [GetSessionDataManager().userName]) }
+
+ + +
+
+ ); +} diff --git a/src/components/help/views/name-change/NameChangeInputView.tsx b/src/components/help/views/name-change/NameChangeInputView.tsx new file mode 100644 index 00000000..3e1b3d12 --- /dev/null +++ b/src/components/help/views/name-change/NameChangeInputView.tsx @@ -0,0 +1,102 @@ +import { CheckUserNameMessageComposer, CheckUserNameResultMessageEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { LocalizeText } from '../../../../api'; +import { CreateMessageHook, SendMessageHook } from '../../../../hooks'; +import { NameChangeLayoutViewProps } from './NameChangeView.types'; + +const AVAILABLE: number = 0; +const TOO_SHORT: number = 2; +const TOO_LONG: number = 3; +const NOT_VALID: number = 4; +const TAKEN_WITH_SUGGESTIONS: number = 5; +const DISABLED: number = 6; + +export const NameChangeInputView:FC = props => +{ + const { onAction = null } = props; + + const [ newUsername, setNewUsername ] = useState(''); + const [ canProceed, setCanProceed ] = useState(false); + const [ isChecking, setIsChecking ] = useState(false); + const [ errorCode, setErrorCode ] = useState(null); + const [ suggestions, setSuggestions ] = useState([]); + + const onCheckUserNameResultMessageEvent = useCallback((event: CheckUserNameResultMessageEvent) => + { + setIsChecking(false); + + const parser = event.getParser(); + + if(!parser) return; + + switch(parser.resultCode) + { + case AVAILABLE: + setCanProceed(true); + break; + case TOO_SHORT: + setErrorCode('short'); + break; + case TOO_LONG: + setErrorCode('long'); + break; + case NOT_VALID: + setErrorCode('invalid'); + break; + case TAKEN_WITH_SUGGESTIONS: + setSuggestions(parser.nameSuggestions); + setErrorCode('taken'); + break; + case DISABLED: + setErrorCode('change_not_allowed'); + } + }, []); + + CreateMessageHook(CheckUserNameResultMessageEvent, onCheckUserNameResultMessageEvent); + + const check = useCallback(() => + { + if(newUsername === '') return; + + setCanProceed(false); + setSuggestions([]); + setErrorCode(null); + + setIsChecking(true); + SendMessageHook(new CheckUserNameMessageComposer(newUsername)); + }, [ newUsername ]); + + const handleUsernameChange = useCallback((username: string) => + { + setCanProceed(false); + setSuggestions([]); + setErrorCode(null); + + setNewUsername(username); + }, []); + + return ( +
+
{ LocalizeText('tutorial.name_change.info.select') }
+
+ handleUsernameChange(e.target.value) } /> + +
+ { !errorCode && !canProceed &&
{ LocalizeText('help.tutorial.name.info') }
} + { errorCode &&
{ LocalizeText(`help.tutorial.name.${errorCode}`, ['name'], [newUsername]) }
} + { canProceed &&
{ LocalizeText('help.tutorial.name.available', ['name'], [newUsername]) }
} + { suggestions &&
+ { + suggestions.map((suggestion, i) => + { + return (
handleUsernameChange(suggestion) }>{ suggestion }
); + }) + } +
} +
+ + +
+
+ ); +} diff --git a/src/components/help/views/name-change/NameChangeView.tsx b/src/components/help/views/name-change/NameChangeView.tsx new file mode 100644 index 00000000..e40613fe --- /dev/null +++ b/src/components/help/views/name-change/NameChangeView.tsx @@ -0,0 +1,68 @@ +import { FC, useCallback, useMemo, useState } from 'react'; +import { LocalizeText } from '../../../../api'; +import { HelpNameChangeEvent } from '../../../../events/help/HelpNameChangeEvent'; +import { useUiEvent } from '../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { NameChangeConfirmationView } from './NameChangeConfirmationView'; +import { NameChangeInitView } from './NameChangeInitView'; +import { NameChangeInputView } from './NameChangeInputView'; + +const INIT: string = 'INIT'; +const INPUT: string = 'INPUT'; +const CONFIRMATION: string = 'CONFIRMATION'; + +export const NameChangeView:FC<{}> = props => +{ + const [ isVisible, setIsVisible ] = useState(false); + const [ layout, setLayout ] = useState(INIT); + const [ newUsername, setNewUsername ] = useState(''); + + const onHelpNameChangeEvent = useCallback((event: HelpNameChangeEvent) => + { + setLayout(INIT); + setIsVisible(true); + }, []); + + useUiEvent(HelpNameChangeEvent.INIT, onHelpNameChangeEvent); + + const onAction = useCallback((action: string, value?: string) => + { + switch(action) + { + case 'start': + setLayout(INPUT); + break; + case 'confirmation': + setNewUsername(value); + setLayout(CONFIRMATION); + break; + case 'close': + setNewUsername(''); + setIsVisible(false); + break; + } + }, []); + + const titleKey = useMemo(() => + { + switch(layout) + { + case INIT: return 'tutorial.name_change.title.main'; + case INPUT: return 'tutorial.name_change.title.select'; + case CONFIRMATION: return 'tutorial.name_change.title.confirm'; + } + }, [layout]); + + if(!isVisible) return null; + + return ( + + onAction('close') } /> + + { layout === INIT && } + { layout === INPUT && } + { layout === CONFIRMATION && } + + + ) +} diff --git a/src/components/help/views/name-change/NameChangeView.types.ts b/src/components/help/views/name-change/NameChangeView.types.ts new file mode 100644 index 00000000..13222e1f --- /dev/null +++ b/src/components/help/views/name-change/NameChangeView.types.ts @@ -0,0 +1,5 @@ +export interface NameChangeLayoutViewProps +{ + username?: string; + onAction: (action: string, value?: string) => void; +} diff --git a/src/events/help/HelpNameChangeEvent.ts b/src/events/help/HelpNameChangeEvent.ts new file mode 100644 index 00000000..076f5b51 --- /dev/null +++ b/src/events/help/HelpNameChangeEvent.ts @@ -0,0 +1,6 @@ +import { NitroEvent } from '@nitrots/nitro-renderer/src/core/events/NitroEvent'; + +export class HelpNameChangeEvent extends NitroEvent +{ + public static INIT: string = 'HC_NAME_CHANGE_INIT'; +} diff --git a/src/views/friends/FriendsView.scss b/src/views/friends/FriendsView.scss index 6bfa3033..5ac057b6 100644 --- a/src/views/friends/FriendsView.scss +++ b/src/views/friends/FriendsView.scss @@ -92,5 +92,13 @@ } } +.nitro-friends-room-invite { + width: $friends-list-width; +} + +.nitro-friends-remove-confirmation { + width: $friends-list-width; +} + @import "./views/friend-bar/FriendBarView"; @import "./views/messenger/FriendsMessengerView"; diff --git a/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx b/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx index 5e9d745e..888c0c57 100644 --- a/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx +++ b/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx @@ -1,4 +1,5 @@ import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer'; +import classNames from 'classnames'; import { FC, useCallback, useState } from 'react'; import { LocalizeText, OpenMessengerChat } from '../../../../api'; import { SendMessageHook } from '../../../../hooks'; @@ -9,7 +10,7 @@ import { FriendsGroupItemViewProps } from './FriendsGroupItemView.types'; export const FriendsGroupItemView: FC = props => { - const { friend = null, selected = false, children = null, ...rest } = props; + const { friend = null, selected = false, selectFriend = null, children = null, ...rest } = props; const [ isExpanded, setIsExpanded ] = useState(false); @@ -20,8 +21,10 @@ export const FriendsGroupItemView: FC = props => SendMessageHook(new FollowFriendMessageComposer(friend.id)); }, [ friend ]); - const openMessengerChat = useCallback(() => + const openMessengerChat = useCallback((e) => { + e.stopPropagation(); + if(!friend) return; OpenMessengerChat(friend.id); @@ -40,8 +43,16 @@ export const FriendsGroupItemView: FC = props => } }, [ friend ]); - const updateRelationship = useCallback((type: number) => + const initUpdateRelationship = useCallback((e) => { + e.stopPropagation(); + setIsExpanded(true); + }, []); + + const updateRelationship = useCallback((e, type: number) => + { + e.stopPropagation(); + if(type !== friend.relationshipStatus) SendMessageHook(new SetRelationshipStatusComposer(friend.id, type)); setIsExpanded(false); @@ -50,8 +61,10 @@ export const FriendsGroupItemView: FC = props => if(!friend) return null; return ( - - + +
e.stopPropagation() }> + +
{ friend.name }
{ !isExpanded && @@ -60,14 +73,14 @@ export const FriendsGroupItemView: FC = props => } { friend.online && } - setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } /> + } { isExpanded && <> - updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } /> - updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } /> - updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } /> - updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } /> + updateRelationship(e, MessengerFriend.RELATIONSHIP_HEART) } /> + updateRelationship(e, MessengerFriend.RELATIONSHIP_SMILE) } /> + updateRelationship(e, MessengerFriend.RELATIONSHIP_BOBBA) } /> + updateRelationship(e, MessengerFriend.RELATIONSHIP_NONE) } /> } { children } diff --git a/src/views/friends/views/friends-group-item/FriendsGroupItemView.types.ts b/src/views/friends/views/friends-group-item/FriendsGroupItemView.types.ts index ed8ccd86..392d57d7 100644 --- a/src/views/friends/views/friends-group-item/FriendsGroupItemView.types.ts +++ b/src/views/friends/views/friends-group-item/FriendsGroupItemView.types.ts @@ -5,4 +5,5 @@ export interface FriendsGroupItemViewProps extends NitroLayoutFlexProps { friend: MessengerFriend; selected?: boolean; + selectFriend: () => void; } diff --git a/src/views/friends/views/friends-group/FriendsGroupView.tsx b/src/views/friends/views/friends-group/FriendsGroupView.tsx index 55b82fca..e1cad75e 100644 --- a/src/views/friends/views/friends-group/FriendsGroupView.tsx +++ b/src/views/friends/views/friends-group/FriendsGroupView.tsx @@ -4,15 +4,15 @@ import { FriendsGroupViewProps } from './FriendsGroupView.types'; export const FriendsGroupView: FC = props => { - const { list = null } = props; + const { list = null, selectedFriendsIds = null, selectFriend = null } = props; if(!list) return null; return ( <> - { list.map((item, index) => + { selectedFriendsIds && list && list.map((item, index) => { - return ; + return selectFriend(item.id) } />; }) } ); diff --git a/src/views/friends/views/friends-group/FriendsGroupView.types.ts b/src/views/friends/views/friends-group/FriendsGroupView.types.ts index 5ce24421..3400723c 100644 --- a/src/views/friends/views/friends-group/FriendsGroupView.types.ts +++ b/src/views/friends/views/friends-group/FriendsGroupView.types.ts @@ -3,4 +3,6 @@ import { MessengerFriend } from '../../common/MessengerFriend'; export interface FriendsGroupViewProps { list: MessengerFriend[]; + selectedFriendsIds: number[]; + selectFriend: (userId: number) => void; } diff --git a/src/views/friends/views/friends-list/FriendsListView.tsx b/src/views/friends/views/friends-list/FriendsListView.tsx index 7ceaa48d..66f2cb2e 100644 --- a/src/views/friends/views/friends-list/FriendsListView.tsx +++ b/src/views/friends/views/friends-list/FriendsListView.tsx @@ -1,9 +1,13 @@ -import { FC, useEffect, useState } from 'react'; +import { RemoveFriendComposer, SendRoomInviteComposer } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useMemo, useState } from 'react'; import { LocalizeText } from '../../../../api'; +import { SendMessageHook } from '../../../../hooks'; import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout'; import { MessengerFriend } from '../../common/MessengerFriend'; import { FriendsGroupView } from '../friends-group/FriendsGroupView'; +import { FriendsRemoveConfirmationView } from '../friends-remove-confirmation/FriendsRemoveConfirmationView'; import { FriendsRequestView } from '../friends-request/FriendsRequestView'; +import { FriendsRoomInviteView } from '../friends-room-invite/FriendsRoomInviteView'; import { FriendsSearchView } from '../friends-search/FriendsSearchView'; import { FriendsListViewProps } from './FriendsListView.types'; @@ -13,39 +17,107 @@ const MODE_SEARCH: number = 1; export const FriendsListView: FC = props => { const { onlineFriends = [], offlineFriends = [], friendRequests = [], onCloseClick = null } = props; - const [ selectedFriends, setSelectedFriends ] = useState([]); - const [ mode, setMode ] = useState(0); - useEffect(() => + const [ selectedFriendsIds, setSelectedFriendsIds ] = useState([]); + const [ mode, setMode ] = useState(0); + + const [ showRoomInvite, setShowRoomInvite ] = useState(false); + const [ showRemoveFriendsConfirmation, setShowRemoveFriendsConfirmation ] = useState(false); + + const removeFriendsText = useMemo(() => { - setSelectedFriends([]); - }, [ onlineFriends, offlineFriends ]); + if(!selectedFriendsIds || !selectedFriendsIds.length) return ''; + + const userNames: string[] = []; + + for(const userId of selectedFriendsIds) + { + let existingFriend: MessengerFriend = onlineFriends.find(f => f.id === userId); + + if(!existingFriend) existingFriend = offlineFriends.find(f => f.id === userId); + + if(!existingFriend) continue; + + userNames.push(existingFriend.name); + } + + return LocalizeText('friendlist.removefriendconfirm.userlist', ['user_names'], [userNames.join(', ')]); + }, [offlineFriends, onlineFriends, selectedFriendsIds]); + + const selectFriend = useCallback((userId: number) => + { + if(userId < 0) return; + + const existingUserIdIndex: number = selectedFriendsIds.indexOf(userId); + + if(existingUserIdIndex > -1) + { + const clonedFriend = [...selectedFriendsIds]; + clonedFriend.splice(existingUserIdIndex, 1) + + setSelectedFriendsIds([...clonedFriend]); + } + else + { + setSelectedFriendsIds([...selectedFriendsIds, userId]); + } + }, [ selectedFriendsIds, setSelectedFriendsIds ]); + + const sendRoomInvite = useCallback((message: string) => + { + if(selectedFriendsIds.length === 0 || !message || message.length === 0) return; + + SendMessageHook(new SendRoomInviteComposer(message, ...selectedFriendsIds)); + setShowRoomInvite(false); + }, [ selectedFriendsIds, setShowRoomInvite ]); + + const removeSelectedFriends = useCallback(() => + { + if(selectedFriendsIds.length === 0) return; + + SendMessageHook(new RemoveFriendComposer(...selectedFriendsIds)); + setSelectedFriendsIds([]); + setShowRemoveFriendsConfirmation(false); + }, [ selectedFriendsIds ]); return ( - - - - setMode(MODE_FRIENDS) }> - { LocalizeText('friendlist.friends') } - - setMode(MODE_SEARCH) }> - { LocalizeText('generic.search') } - - - - { (mode === MODE_FRIENDS) && - - - - - - - - - } - { (mode === MODE_SEARCH) && - } - - + <> + + + + setMode(MODE_FRIENDS) }> + { LocalizeText('friendlist.friends') } + + setMode(MODE_SEARCH) }> + { LocalizeText('generic.search') } + + + + { (mode === MODE_FRIENDS) && + <> + + + + + + + + + + { selectedFriendsIds && selectedFriendsIds.length > 0 &&
+ + +
} + + } + { (mode === MODE_SEARCH) && + } +
+
+ { showRoomInvite && + setShowRoomInvite(false) } sendRoomInvite={ sendRoomInvite } /> } + { showRemoveFriendsConfirmation && + setShowRemoveFriendsConfirmation(false) } removeSelectedFriends={ removeSelectedFriends } /> } + ); }; diff --git a/src/views/friends/views/friends-remove-confirmation/FriendsRemoveConfirmationView.tsx b/src/views/friends/views/friends-remove-confirmation/FriendsRemoveConfirmationView.tsx new file mode 100644 index 00000000..cc3327a8 --- /dev/null +++ b/src/views/friends/views/friends-remove-confirmation/FriendsRemoveConfirmationView.tsx @@ -0,0 +1,22 @@ +import { FC } from 'react'; +import { LocalizeText } from '../../../../api'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { FriendsRemoveConfirmationViewProps } from './FriendsRemoveConfirmationView.types'; + +export const FriendsRemoveConfirmationView: FC = props => +{ + const { selectedFriendsIds = null, removeFriendsText = null, removeSelectedFriends = null, onCloseClick = null } = props; + + return ( + + + +
{ removeFriendsText }
+
+ + +
+
+
+ ); +}; diff --git a/src/views/friends/views/friends-remove-confirmation/FriendsRemoveConfirmationView.types.ts b/src/views/friends/views/friends-remove-confirmation/FriendsRemoveConfirmationView.types.ts new file mode 100644 index 00000000..eeff1c9c --- /dev/null +++ b/src/views/friends/views/friends-remove-confirmation/FriendsRemoveConfirmationView.types.ts @@ -0,0 +1,7 @@ +export interface FriendsRemoveConfirmationViewProps +{ + selectedFriendsIds: number[]; + removeFriendsText: string; + removeSelectedFriends: () => void; + onCloseClick: () => void; +} diff --git a/src/views/friends/views/friends-room-invite/FriendsRoomInviteView.tsx b/src/views/friends/views/friends-room-invite/FriendsRoomInviteView.tsx new file mode 100644 index 00000000..3c4c9d26 --- /dev/null +++ b/src/views/friends/views/friends-room-invite/FriendsRoomInviteView.tsx @@ -0,0 +1,26 @@ +import { FC, useState } from 'react'; +import { LocalizeText } from '../../../../api'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { FriendsRoomInviteViewProps } from './FriendsRoomInviteView.types'; + +export const FriendsRoomInviteView: FC = props => +{ + const { selectedFriendsIds = null, onCloseClick = null, sendRoomInvite = null } = props; + + const [ roomInviteMessage, setRoomInviteMessage ] = useState(''); + + return ( + + + + { LocalizeText('friendlist.invite.summary', ['count'], [selectedFriendsIds.length.toString()]) } + +
{ LocalizeText('friendlist.invite.note') }
+
+ + +
+
+
+ ); +}; diff --git a/src/views/friends/views/friends-room-invite/FriendsRoomInviteView.types.ts b/src/views/friends/views/friends-room-invite/FriendsRoomInviteView.types.ts new file mode 100644 index 00000000..1fc54ff3 --- /dev/null +++ b/src/views/friends/views/friends-room-invite/FriendsRoomInviteView.types.ts @@ -0,0 +1,6 @@ +export interface FriendsRoomInviteViewProps +{ + selectedFriendsIds: number[]; + onCloseClick: () => void; + sendRoomInvite: (message: string) => void; +} diff --git a/src/views/friends/views/messenger/FriendsMessengerView.tsx b/src/views/friends/views/messenger/FriendsMessengerView.tsx index 5544e2b1..919f3bd4 100644 --- a/src/views/friends/views/messenger/FriendsMessengerView.tsx +++ b/src/views/friends/views/messenger/FriendsMessengerView.tsx @@ -1,4 +1,4 @@ -import { FollowFriendMessageComposer, ILinkEventTracker, NewConsoleMessageEvent, SendMessageComposer } from '@nitrots/nitro-renderer'; +import { FollowFriendMessageComposer, ILinkEventTracker, NewConsoleMessageEvent, RoomInviteEvent, SendMessageComposer } from '@nitrots/nitro-renderer'; import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AddEventLinkTracker, GetSessionDataManager, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../../api'; import { MESSENGER_MESSAGE_RECEIVED, MESSENGER_NEW_THREAD, PlaySound } from '../../../../api/utils/PlaySound'; @@ -105,6 +105,21 @@ export const FriendsMessengerView: FC<{}> = props => CreateMessageHook(NewConsoleMessageEvent, onNewConsoleMessageEvent); + const onRoomInviteEvent = useCallback((event: RoomInviteEvent) => + { + const parser = event.getParser(); + + const [threadIndex, thread] = getMessageThreadWithIndex(parser.senderId); + + if((threadIndex === -1) || !thread) return; + + thread.addMessage(parser.senderId, parser.messageText, 0, null, MessengerThreadChat.ROOM_INVITE); + + setMessageThreads(prevValue => [...prevValue]); + }, [getMessageThreadWithIndex]); + + CreateMessageHook(RoomInviteEvent, onRoomInviteEvent); + const sendMessage = useCallback(() => { if(!messageText || !messageText.length) return; diff --git a/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx b/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx index 8d1d9c46..c83c58b5 100644 --- a/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx +++ b/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx @@ -2,6 +2,7 @@ import { AvatarAction, AvatarExpressionEnum, RoomControllerLevel, RoomObjectCate import { FC, useCallback, useMemo, useState } from 'react'; import { GetCanStandUp, GetCanUseExpression, GetOwnPosture, GetUserProfile, HasHabboClub, HasHabboVip, IsRidingHorse, LocalizeText, RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUserActionMessage } from '../../../../../../api'; import { AvatarEditorEvent } from '../../../../../../events'; +import { HelpNameChangeEvent } from '../../../../../../events/help/HelpNameChangeEvent'; import { dispatchUiEvent } from '../../../../../../hooks'; import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon'; import { useRoomContext } from '../../../../context/RoomContext'; @@ -42,6 +43,9 @@ export const AvatarInfoWidgetOwnAvatarView: FC = props => @@ -16,6 +15,12 @@ export const RoomToolsWidgetView: FC<{}> = props => const [ isLiked, setIsLiked ] = useState(false); const { widgetHandler = null } = useRoomContext(); + const [ roomName, setRoomName ] = useState(null); + const [ roomOwner, setRoomOwner ] = useState(null); + const [ roomTags, setRoomTags ] = useState(null); + const [ roomInfoDisplay, setRoomInfoDisplay ] = useState(false); + const [ isOpen, setIsOpen ] = useState(false); + const handleToolClick = useCallback((action: string) => { switch(action) @@ -41,13 +46,50 @@ export const RoomToolsWidgetView: FC<{}> = props => return; } }, [ isZoomedIn, isLiked, widgetHandler ]); + + const onGetGuestRoomResultEvent = useCallback((event: GetGuestRoomResultEvent) => + { + const parser = event.getParser(); + + if(roomName !== parser.data.roomName) setRoomName(parser.data.roomName); + + if(roomOwner !== parser.data.ownerName) setRoomOwner(parser.data.ownerName); + + if(roomTags !== parser.data.tags) setRoomTags(parser.data.tags); + }, [ roomName, roomOwner, roomTags ]); + + CreateMessageHook(GetGuestRoomResultEvent, onGetGuestRoomResultEvent); + + useEffect(() => + { + setIsOpen(true); + + const timeout = setTimeout(() => setIsOpen(false), 5000); + + return () => clearTimeout(timeout); + }, [ roomName, roomOwner, roomTags ]); return ( - - handleToolClick('settings') } /> - handleToolClick('zoom') } className={ 'icon ' + classNames({ 'icon-zoom-less': !isZoomedIn, 'icon-zoom-more': isZoomedIn }) } /> - handleToolClick('chat_history') } className="icon icon-chat-history" /> - { !isLiked && handleToolClick('like_room') } className="icon icon-like-room" /> } - + + + handleToolClick('settings') } /> + handleToolClick('zoom') } className={ 'icon ' + classNames({ 'icon-zoom-less': !isZoomedIn, 'icon-zoom-more': isZoomedIn }) } /> + handleToolClick('chat_history') } className="icon icon-chat-history" /> + { !isLiked && handleToolClick('like_room') } className="icon icon-like-room" /> } + + { isOpen && + + + + { roomName } + { roomOwner } + + { roomTags && roomTags.length > 0 && +
+ { roomTags.map((tag: string) =>
#{ tag }
) } +
} +
+
} +
); } diff --git a/src/views/user-settings/UserSettingsView.tsx b/src/views/user-settings/UserSettingsView.tsx index 9a915b33..d8870e07 100644 --- a/src/views/user-settings/UserSettingsView.tsx +++ b/src/views/user-settings/UserSettingsView.tsx @@ -114,44 +114,48 @@ export const UserSettingsView: FC<{}> = props => if(!isVisible) return null; return ( - + processAction('close_view')} /> - -
- processAction('oldchat', event.target.checked) } /> - -
-
- processAction('room_invites', event.target.checked) } /> - -
-
- processAction('camera_follow', event.target.checked) } /> - -
-
{ LocalizeText('widget.memenu.settings.volume') }
-
- -
- 0) ? ' fa-volume-down' : '') + ((userSettings.volumeSystem >= 50) ? ' text-muted' : '') } /> - processAction('system_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/> - + +
+
+ processAction('oldchat', event.target.checked) } /> + +
+
+ processAction('room_invites', event.target.checked) } /> + +
+
+ processAction('camera_follow', event.target.checked) } /> +
-
- -
- 0) ? ' fa-volume-down' : '') + ((userSettings.volumeFurni >= 50) ? ' text-muted' : '') } /> - processAction('furni_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/> - +
+
{ LocalizeText('widget.memenu.settings.volume') }
+
+ +
+ 0) ? ' fa-volume-down' : '') + ((userSettings.volumeSystem >= 50) ? ' text-muted' : '') } /> + processAction('system_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/> + +
-
-
- -
- 0) ? ' fa-volume-down' : '') + ((userSettings.volumeTrax >= 50) ? ' text-muted' : '') } /> - processAction('trax_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/> - +
+ +
+ 0) ? ' fa-volume-down' : '') + ((userSettings.volumeFurni >= 50) ? ' text-muted' : '') } /> + processAction('furni_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/> + +
+
+
+ +
+ 0) ? ' fa-volume-down' : '') + ((userSettings.volumeTrax >= 50) ? ' text-muted' : '') } /> + processAction('trax_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/> + +