diff --git a/src/components/toolbar/ToolbarMeView.tsx b/src/components/toolbar/ToolbarMeView.tsx new file mode 100644 index 00000000..4d9f3e4d --- /dev/null +++ b/src/components/toolbar/ToolbarMeView.tsx @@ -0,0 +1,58 @@ +import { RoomObjectCategory } from '@nitrots/nitro-renderer'; +import { FC, useEffect } from 'react'; +import { GetRoomEngine, GetRoomSession } from '../../api'; +import { ItemCountView } from '../../views/shared/item-count/ItemCountView'; +import { ToolbarViewItems } from './common/ToolbarViewItems'; + +export interface ToolbarMeViewProps +{ + unseenAchievementCount: number; + handleToolbarItemClick: (item: string) => void; +} + +export const ToolbarMeView: FC = props => +{ + const { unseenAchievementCount = 0, handleToolbarItemClick = null } = props; + + useEffect(() => + { + const roomSession = GetRoomSession(); + + if(!roomSession) return; + + GetRoomEngine().selectRoomObject(roomSession.roomId, roomSession.ownRoomIndex, RoomObjectCategory.UNIT); + }, []); + + return ( +
+
+
+ +
+
+ +
+
handleToolbarItemClick(ToolbarViewItems.ACHIEVEMENTS_ITEM) }> + + { (unseenAchievementCount > 0) && + } +
+
handleToolbarItemClick(ToolbarViewItems.PROFILE_ITEM) }> + +
+
+ +
+
handleToolbarItemClick(ToolbarViewItems.CLOTHING_ITEM) }> + +
+
+ +
+
handleToolbarItemClick(ToolbarViewItems.SETTINGS_ITEM) }> + +
+
+
+ ); +} diff --git a/src/components/toolbar/ToolbarView.scss b/src/components/toolbar/ToolbarView.scss new file mode 100644 index 00000000..0b7fb99a --- /dev/null +++ b/src/components/toolbar/ToolbarView.scss @@ -0,0 +1,166 @@ +.nitro-toolbar-container { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: $toolbar-height; + z-index: $toolbar-zindex; + + .nitro-toolbar { + height: 100%; + pointer-events: all; + background: rgba($dark, 0.95); + box-shadow: inset 0px 5px lighten(rgba($dark, 0.6), 2.5), + inset 0 -4px darken(rgba($dark, 0.6), 4); + + #toolbar-chat-input-container { + margin: 0 10px; + + @include media-breakpoint-down(sm) { + width: 0px; + height: 0px; + } + } + + .navigation-items { + display: flex; + align-items: center; + + &.navigation-avatar { + border-right: 1px solid rgba(0, 0, 0, 0.3); + } + + .navigation-item { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + //margin: 0 1px; + position: relative; + + &.item-avatar { + width: 50px; + height: 45px; + overflow: hidden; + + .avatar-image { + margin-left: -5px; + margin-top: 25px; + } + } + + .icon, + &.item-avatar { + position: relative; + //transition: transform .2s ease-out; + + &:hover, + &.active { + -webkit-transform: translate(-1px, -1px); + transform: translate(-1px, -1px); + filter: drop-shadow(2px 2px 0 rgba($black, 0.8)); + } + } + + .avatar-image { + pointer-events: none; + } + + .chat-input-container { + left: 60px; + } + } + } + + .nitro-toolbar-me-menu { + bottom: 77px; + left: 200px; + position: absolute; + font-size: 12px; + z-index: $toolbar-memenu-zindex; + + .list-group { + .list-group-item { + min-width: 70px; + transition: all 0.3s; + font-size: 10px; + text-align: center; + + i { + filter: grayscale(1); + } + + &:hover { + color: $cyan; + text-decoration: underline; + + i { + filter: grayscale(0); + } + } + + .count { + top: 0px; + right: 5px; + font-size: 10px; + } + } + } + } + } +} + +.toolbar-icon-animation { + position: absolute; + object-fit: cover; + height: auto; + width: auto; + max-width: 120px; + max-height: 150px; + z-index: 500; + filter: drop-shadow(2px 1px 0 rgba($white, 1)) + drop-shadow(-2px 1px 0 rgba($white, 1)) + drop-shadow(0 -2px 0 rgba($white, 1)); +} + +.nitro-toolbar-me { + position: absolute; + bottom: 65px; + left: 15px; + z-index: $toolbar-me-zindex; + background: rgba(20, 20, 20, .95); + border: 1px solid #101010; + box-shadow: inset 2px 2px rgba(255, 255, 255, .1), inset -2px -2px rgba(255, 255, 255, .1); + border-radius: $border-radius; + + .navigation-items { + display: flex; + align-items: center; + + &.navigation-avatar { + border-right: 1px solid rgba(0, 0, 0, .3); + } + + .navigation-item { + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + cursor: pointer; + width: 50px; + font-size: 11px; + + .icon { + transition: filter .2s ease-out; + filter: grayscale(1); + } + + &:hover { + .icon { + filter: grayscale(0) drop-shadow(2px 2px 0 rgba($black, 0.8)); + } + } + } + } +} diff --git a/src/components/toolbar/ToolbarView.tsx b/src/components/toolbar/ToolbarView.tsx new file mode 100644 index 00000000..95f5da09 --- /dev/null +++ b/src/components/toolbar/ToolbarView.tsx @@ -0,0 +1,244 @@ +import { Dispose, DropBounce, EaseOut, FigureUpdateEvent, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserInfoDataParser, UserInfoEvent, Wait } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { CreateLinkEvent, GetRoomSession, GetRoomSessionManager, GetSessionDataManager, GetUserProfile, GoToDesktop, OpenMessengerChat } from '../../api'; +import { AvatarEditorEvent, CatalogEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events'; +import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events/achievements'; +import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent'; +import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent'; +import { UserSettingsUIEvent } from '../../events/user-settings/UserSettingsUIEvent'; +import { BatchUpdates, dispatchUiEvent, useRoomEngineEvent, useUiEvent } from '../../hooks'; +import { CreateMessageHook } from '../../hooks/messages/message-event'; +import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation'; +import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types'; +import { AvatarImageView } from '../../views/shared/avatar-image/AvatarImageView'; +import { ItemCountView } from '../../views/shared/item-count/ItemCountView'; +import { ToolbarViewItems } from './common/ToolbarViewItems'; +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 => +{ + const { isInRoom } = props; + + const [ userInfo, setUserInfo ] = useState(null); + const [ userFigure, setUserFigure ] = useState(null); + const [ isMeExpanded, setMeExpanded ] = useState(false); + const [ chatIconType, setChatIconType ] = useState(CHAT_ICON_HIDDEN); + const [ unseenInventoryCount, setUnseenInventoryCount ] = useState(0); + const [ unseenAchievementCount, setUnseenAchievementCount ] = useState(0); + const [ unseenFriendRequestCount, setFriendRequestCount ] = useState(0); + const isMod = GetSessionDataManager().isModerator; + + const onUserInfoEvent = useCallback((event: UserInfoEvent) => + { + const parser = event.getParser(); + + BatchUpdates(() => + { + setUserInfo(parser.userInfo); + setUserFigure(parser.userInfo.figure); + }); + }, []); + + CreateMessageHook(UserInfoEvent, onUserInfoEvent); + + const onUserFigureEvent = useCallback((event: FigureUpdateEvent) => + { + const parser = event.getParser(); + + setUserFigure(parser.figure); + }, []); + + CreateMessageHook(FigureUpdateEvent, onUserFigureEvent); + + const onFriendsMessengerIconEvent = useCallback((event: FriendsMessengerIconEvent) => + { + setChatIconType(event.iconType); + }, []); + + useUiEvent(FriendsMessengerIconEvent.UPDATE_ICON, onFriendsMessengerIconEvent); + + const onUnseenItemTrackerUpdateEvent = useCallback((event: UnseenItemTrackerUpdateEvent) => + { + setUnseenInventoryCount(event.count); + }, []); + + useUiEvent(UnseenItemTrackerUpdateEvent.UPDATE_COUNT, onUnseenItemTrackerUpdateEvent); + + const onAchievementsUIUnseenCountEvent = useCallback((event: AchievementsUIUnseenCountEvent) => + { + setUnseenAchievementCount(event.count); + }, []); + + 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); + + if(!target) return; + + image.className = 'toolbar-icon-animation'; + image.style.visibility = 'visible'; + image.style.left = (x + 'px'); + image.style.top = (y + 'px'); + + document.body.append(image); + + const targetBounds = target.getBoundingClientRect(); + const imageBounds = image.getBoundingClientRect(); + + const left = (imageBounds.x - targetBounds.x); + const top = (imageBounds.y - targetBounds.y); + const squared = Math.sqrt(((left * left) + (top * top))); + const wait = (500 - Math.abs(((((1 / squared) * 100) * 500) * 0.5))); + const height = 20; + + const motionName = (`ToolbarBouncing[${ iconName }]`); + + if(!Motions.getMotionByTag(motionName)) + { + Motions.runMotion(new Queue(new Wait((wait + 8)), new DropBounce(target, 400, 12))).tag = motionName; + } + + const motion = new Queue(new EaseOut(new JumpBy(image, wait, ((targetBounds.x - imageBounds.x) + height), (targetBounds.y - imageBounds.y), 100, 1), 1), new Dispose(image)); + + Motions.runMotion(motion); + }, []); + + const onNitroToolbarAnimateIconEvent = useCallback((event: NitroToolbarAnimateIconEvent) => + { + animationIconToToolbar('icon-inventory', event.image, event.x, event.y); + }, [ animationIconToToolbar ]); + + useRoomEngineEvent(NitroToolbarAnimateIconEvent.ANIMATE_ICON, onNitroToolbarAnimateIconEvent); + + const handleToolbarItemClick = useCallback((item: string) => + { + switch(item) + { + case ToolbarViewItems.NAVIGATOR_ITEM: + dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_NAVIGATOR)); + return; + case ToolbarViewItems.INVENTORY_ITEM: + dispatchUiEvent(new InventoryEvent(InventoryEvent.TOGGLE_INVENTORY)); + return; + case ToolbarViewItems.CATALOG_ITEM: + dispatchUiEvent(new CatalogEvent(CatalogEvent.TOGGLE_CATALOG)); + return; + case ToolbarViewItems.FRIEND_LIST_ITEM: + dispatchUiEvent(new CatalogEvent(FriendsEvent.TOGGLE_FRIEND_LIST)); + return; + case ToolbarViewItems.CAMERA_ITEM: + dispatchUiEvent(new RoomWidgetCameraEvent(RoomWidgetCameraEvent.TOGGLE_CAMERA)); + return; + case ToolbarViewItems.CLOTHING_ITEM: + dispatchUiEvent(new AvatarEditorEvent(AvatarEditorEvent.TOGGLE_EDITOR)); + setMeExpanded(false); + return; + case ToolbarViewItems.MOD_TOOLS_ITEM: + dispatchUiEvent(new ModToolsEvent(ModToolsEvent.TOGGLE_MOD_TOOLS)); + return; + case ToolbarViewItems.ACHIEVEMENTS_ITEM: + dispatchUiEvent(new AchievementsUIEvent(AchievementsUIEvent.TOGGLE_ACHIEVEMENTS)); + 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.FRIEND_CHAT_ITEM: + OpenMessengerChat(); + return; + } + }, []); + + const visitDesktop = useCallback(() => + { + if(!GetRoomSession()) return; + + GoToDesktop(); + GetRoomSessionManager().removeSession(-1); + }, []); + + return ( +
+ + + +
+
+
+
setMeExpanded(!isMeExpanded) }> + + { (unseenAchievementCount > 0) && + } +
+ { isInRoom && ( +
+ +
) } + { !isInRoom && ( +
CreateLinkEvent('navigator/goto/home') }> + +
) } +
handleToolbarItemClick(ToolbarViewItems.NAVIGATOR_ITEM) }> + +
+
handleToolbarItemClick(ToolbarViewItems.CATALOG_ITEM) }> + +
+
handleToolbarItemClick(ToolbarViewItems.INVENTORY_ITEM) }> + + { (unseenInventoryCount > 0) && + } +
+ { isInRoom && ( +
handleToolbarItemClick(ToolbarViewItems.CAMERA_ITEM) }> + +
) } + { isMod && ( +
handleToolbarItemClick(ToolbarViewItems.MOD_TOOLS_ITEM) }> + +
) } +
+
+
+
+
+
handleToolbarItemClick(ToolbarViewItems.FRIEND_LIST_ITEM) }> + + { (unseenFriendRequestCount > 0) && + } +
+ { ((chatIconType === CHAT_ICON_SHOWING) || (chatIconType === CHAT_ICON_UNREAD)) && +
handleToolbarItemClick(ToolbarViewItems.FRIEND_CHAT_ITEM) }> + { (chatIconType === CHAT_ICON_SHOWING) && } + { (chatIconType === CHAT_ICON_UNREAD) && } +
} +
+
+
+
+
+ ); +} diff --git a/src/components/toolbar/common/ToolbarViewItems.ts b/src/components/toolbar/common/ToolbarViewItems.ts new file mode 100644 index 00000000..48a376d1 --- /dev/null +++ b/src/components/toolbar/common/ToolbarViewItems.ts @@ -0,0 +1,19 @@ +export interface ToolbarViewProps +{ + isInRoom: boolean; +} + +export class ToolbarViewItems +{ + public static NAVIGATOR_ITEM: string = 'TVI_NAVIGATOR_ITEM'; + public static INVENTORY_ITEM: string = 'TVI_INVENTORY_ITEM'; + public static CATALOG_ITEM: string = 'TVI_CATALOG_ITEM'; + public static FRIEND_LIST_ITEM: string = 'TVI_FRIEND_LIST_ITEM'; + public static FRIEND_CHAT_ITEM: string = 'TVI_FRIEND_CHAT_ITEM'; + public static CLOTHING_ITEM: string = 'TVI_CLOTHING_ITEM'; + public static CAMERA_ITEM: string = 'TVI_CAMERA_ITEM'; + public static MOD_TOOLS_ITEM: string = 'TVI_MOD_TOOLS_ITEM'; + public static ACHIEVEMENTS_ITEM: string = 'TVI_ACHIEVEMENTS_ITEM'; + public static PROFILE_ITEM: string = 'TVI_PROFILE_ITEM'; + public static SETTINGS_ITEM: string = 'TVI_SETTINGS_ITEM'; +} diff --git a/src/views/toolbar/me/ToolbarMeView.scss b/src/views/toolbar/me/ToolbarMeView.scss deleted file mode 100644 index 491ffad6..00000000 --- a/src/views/toolbar/me/ToolbarMeView.scss +++ /dev/null @@ -1,41 +0,0 @@ -.nitro-toolbar-me { - position: absolute; - bottom: 65px; - left: 15px; - z-index: $toolbar-me-zindex; - background: rgba(20, 20, 20, .95); - border: 1px solid #101010; - box-shadow: inset 2px 2px rgba(255, 255, 255, .1), inset -2px -2px rgba(255, 255, 255, .1); - border-radius: $border-radius; - - .navigation-items { - display: flex; - align-items: center; - - &.navigation-avatar { - border-right: 1px solid rgba(0, 0, 0, .3); - } - - .navigation-item { - position: relative; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - cursor: pointer; - width: 50px; - font-size: 11px; - - .icon { - transition: filter .2s ease-out; - filter: grayscale(1); - } - - &:hover { - .icon { - filter: grayscale(0) drop-shadow(2px 2px 0 rgba($black, 0.8)); - } - } - } - } -} diff --git a/src/views/toolbar/me/ToolbarMeView.types.ts b/src/views/toolbar/me/ToolbarMeView.types.ts deleted file mode 100644 index aeb05a5b..00000000 --- a/src/views/toolbar/me/ToolbarMeView.types.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export interface ToolbarMeViewProps -{ - unseenAchievementCount: number; - handleToolbarItemClick: (item: string) => void; -}