diff --git a/src/App.scss b/src/App.scss index 8a14b0eb..e888fa9e 100644 --- a/src/App.scss +++ b/src/App.scss @@ -35,8 +35,8 @@ $navigator-height: 420px; $chat-input-style-selector-widget-width: 210px; $chat-input-style-selector-widget-height: 200px; -$user-profile-width: 560px; -$user-profile-height: 500px; +$user-profile-width: 470px; +$user-profile-height: 460px; $nitro-widget-custom-stack-height-width: 275px; $nitro-widget-custom-stack-height-height: 220px; diff --git a/src/assets/images/groups/no-group-1.png b/src/assets/images/groups/no-group-1.png new file mode 100644 index 00000000..caf0182d Binary files /dev/null and b/src/assets/images/groups/no-group-1.png differ diff --git a/src/assets/images/groups/no-group-2.png b/src/assets/images/groups/no-group-2.png new file mode 100644 index 00000000..797c9468 Binary files /dev/null and b/src/assets/images/groups/no-group-2.png differ diff --git a/src/assets/images/groups/no-group-3.png b/src/assets/images/groups/no-group-3.png new file mode 100644 index 00000000..e5d6a7b7 Binary files /dev/null and b/src/assets/images/groups/no-group-3.png differ diff --git a/src/assets/images/groups/no-group-spritesheet.png b/src/assets/images/groups/no-group-spritesheet.png new file mode 100644 index 00000000..3eed4b97 Binary files /dev/null and b/src/assets/images/groups/no-group-spritesheet.png differ diff --git a/src/common/index.scss b/src/common/index.scss index 319117a5..5d72ac6d 100644 --- a/src/common/index.scss +++ b/src/common/index.scss @@ -1,5 +1,5 @@ .layout-grid-item { - height: var(--nitro-grid-column-min-height, 45px); + height: var(--nitro-grid-column-min-height, unset); background-position: center; background-repeat: no-repeat; background-color: $grid-bg-color; diff --git a/src/components/user-profile/UserProfileVew.scss b/src/components/user-profile/UserProfileVew.scss index 5adc408a..659ead77 100644 --- a/src/components/user-profile/UserProfileVew.scss +++ b/src/components/user-profile/UserProfileVew.scss @@ -1,106 +1,67 @@ .user-profile { - width: 560px; - - .content-area { - color: black; - } + width: $user-profile-width; + height: $user-profile-height; .user-container { border-right: 1px solid gray; - .avatar-image { - left: -10px; + .avatar-container { + width: 75px; + height: 120px; } - - .add-friend { - margin: 5px; - margin-left: 10px; - } - } - - .badge-container { - min-height: 50px; - background: rgba(0, 0, 0, 0.1); - border-radius: 5px; - margin: 0px; - margin-bottom: 2px; } .rooms-button-container { border-top: 1px solid gray; border-bottom: 1px solid gray; - padding: 1px; - - .rooms-button { - display: inline-block; - text-align: center; - height: 100%; - text-decoration: underline; - margin-left: 10px; - } } - .friends-container { - height: 100%; - } -} + .user-relationship { + height: 25px; -.profile-groups { - height: 219px; - - .profile-groups-item { - width: 50px; - height: 50px; - border-radius: $border-radius; - border-color: $grid-border-color !important; - background-color: $grid-bg-color; - border: nth(map-values($border-widths), 2) solid; - - &.active { - border-color: $grid-active-border-color !important; - background-color: $grid-active-bg-color !important; - } - - .icon { - z-index: 1; - top: 0px; - right: 0px; - } - } -} - -.relationships-container { - - .relationship-container { - - .relationship - { - position: relative; - - &.advanced { - background-color: white; - padding: 5px; - border-radius: 5px; - } - - .relationship-text { - text-decoration: underline; - } + .avatar-image-container { + width: 50px; + height: 50px; .avatar-image { - position: absolute; - width: 50px; - height: 80px; - right: 0; - margin-top: -60px; + top: 20px; + right: -8pxpx; } } + } - .others-text { - margin-left: 20px; - height: 21px; - color: #939392; + .user-relationship-count { + margin-top: 2px; + margin-left: 5px; + color: #939392 !important; + } + + .user-groups-container { + + .layout-grid-item { + width: 50px; } } - + + .no-group-spritesheet { + background: transparent url('../../assets/images/groups/no-group-spritesheet.png') no-repeat; + + &.image-1 { + width: 95px; + height: 136px; + background-position: -3px -3px; + } + + &.image-2 { + width: 95px; + height: 136px; + background-position: -104px -3px; + } + + &.image-3 { + width: 95px; + height: 136px; + background-position: -205px -3px; + } + } } diff --git a/src/components/user-profile/UserProfileView.tsx b/src/components/user-profile/UserProfileView.tsx index bf95085a..2786c382 100644 --- a/src/components/user-profile/UserProfileView.tsx +++ b/src/components/user-profile/UserProfileView.tsx @@ -1,7 +1,7 @@ import { RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, UserCurrentBadgesComposer, UserCurrentBadgesEvent, UserProfileEvent, UserProfileParser, UserRelationshipsComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useState } from 'react'; import { GetSessionDataManager, GetUserProfile, LocalizeText, SendMessageComposer } from '../../api'; -import { Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common'; +import { Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; import { BatchUpdates, UseMessageEventHook } from '../../hooks'; import { BadgesContainerView } from './views/BadgesContainerView'; import { FriendsContainerView } from './views/FriendsContainerView'; @@ -26,10 +26,9 @@ export const UserProfileView: FC<{}> = props => const onLeaveGroup = useCallback(() => { - if(userProfile && userProfile.id === GetSessionDataManager().userId) - { - GetUserProfile(userProfile.id); - } + if(!userProfile || (userProfile.id !== GetSessionDataManager().userId)) return; + + GetUserProfile(userProfile.id); }, [ userProfile ]); const onUserCurrentBadgesEvent = useCallback((event: UserCurrentBadgesEvent) => @@ -43,7 +42,7 @@ export const UserProfileView: FC<{}> = props => UseMessageEventHook(UserCurrentBadgesEvent, onUserCurrentBadgesEvent); - const OnUserRelationshipsEvent = useCallback((event: RelationshipStatusInfoEvent) => + const onUserRelationshipsEvent = useCallback((event: RelationshipStatusInfoEvent) => { const parser = event.getParser(); @@ -52,27 +51,22 @@ export const UserProfileView: FC<{}> = props => setUserRelationships(parser); }, [ userProfile ]); - UseMessageEventHook(RelationshipStatusInfoEvent, OnUserRelationshipsEvent); + UseMessageEventHook(RelationshipStatusInfoEvent, onUserRelationshipsEvent); const onUserProfileEvent = useCallback((event: UserProfileEvent) => { const parser = event.getParser(); - - if(userProfile) - { - BatchUpdates(() => - { - setUserProfile(null); - setUserBadges([]); - setUserRelationships(null); - }); - } - setUserProfile(parser); + BatchUpdates(() => + { + setUserProfile(parser); + setUserBadges([]); + setUserRelationships(null); + }); SendMessageComposer(new UserCurrentBadgesComposer(parser.id)); SendMessageComposer(new UserRelationshipsComposer(parser.id)); - }, [ userProfile ]); + }, []); UseMessageEventHook(UserProfileEvent, onUserProfileEvent); @@ -81,22 +75,26 @@ export const UserProfileView: FC<{}> = props => return ( - - - + + + - + + + - { - userRelationships && - } + { userRelationships && + } - - {LocalizeText('extendedprofile.rooms')} + + + + { LocalizeText('extendedprofile.rooms') } + - + ) diff --git a/src/components/user-profile/views/BadgesContainerView.tsx b/src/components/user-profile/views/BadgesContainerView.tsx index 365dcada..06180be6 100644 --- a/src/components/user-profile/views/BadgesContainerView.tsx +++ b/src/components/user-profile/views/BadgesContainerView.tsx @@ -1,27 +1,25 @@ import { FC } from 'react'; -import { Grid, LayoutBadgeImageView, LayoutGridItem } from '../../../common'; +import { Column, FlexProps, LayoutBadgeImageView } from '../../../common'; -interface BadgesContainerViewProps +interface BadgesContainerViewProps extends FlexProps { badges: string[]; } export const BadgesContainerView: FC = props => { - const { badges = null } = props; + const { badges = null, gap = 1, justifyContent = 'between', ...rest } = props; return ( -
- - { badges && (badges.length > 0) && badges.map((badge, index) => - { - return ( - - - - ) - }) } - -
- ) + <> + { badges && (badges.length > 0) && badges.map((badge, index) => + { + return ( + + + + ); + }) } + + ); } diff --git a/src/components/user-profile/views/FriendsContainerView.tsx b/src/components/user-profile/views/FriendsContainerView.tsx index b206fc80..756616e6 100644 --- a/src/components/user-profile/views/FriendsContainerView.tsx +++ b/src/components/user-profile/views/FriendsContainerView.tsx @@ -1,6 +1,7 @@ import { RelationshipStatusInfoMessageParser } from '@nitrots/nitro-renderer'; import { FC } from 'react'; import { LocalizeText } from '../../../api'; +import { Column, Text } from '../../../common'; import { RelationshipsContainerView } from './RelationshipsContainerView'; interface FriendsContainerViewProps @@ -14,12 +15,14 @@ export const FriendsContainerView: FC = props => const { relationships = null, friendsCount = null } = props; return ( -
-
-
{LocalizeText('extendedprofile.relstatus')}
-
+ + + { LocalizeText('extendedprofile.friends.count') } { friendsCount } + + { LocalizeText('extendedprofile.relstatus') } + -
-
+ + ) } diff --git a/src/components/user-profile/views/GroupsContainerView.tsx b/src/components/user-profile/views/GroupsContainerView.tsx index a971afc3..843842ea 100644 --- a/src/components/user-profile/views/GroupsContainerView.tsx +++ b/src/components/user-profile/views/GroupsContainerView.tsx @@ -1,12 +1,11 @@ import { GroupFavoriteComposer, GroupInformationComposer, GroupInformationEvent, GroupInformationParser, HabboGroupEntryData } from '@nitrots/nitro-renderer'; -import classNames from 'classnames'; import { FC, useCallback, useEffect, useState } from 'react'; import { SendMessageComposer } from '../../../api'; -import { LayoutBadgeImageView } from '../../../common'; -import { UseMessageEventHook } from '../../../hooks'; +import { AutoGrid, Base, Column, Flex, Grid, GridProps, LayoutBadgeImageView, LayoutGridItem } from '../../../common'; +import { BatchUpdates, UseMessageEventHook } from '../../../hooks'; import { GroupInformationView } from '../../groups/views/GroupInformationView'; -interface GroupsContainerViewProps +interface GroupsContainerViewProps extends GridProps { itsMe: boolean; groups: HabboGroupEntryData[]; @@ -15,16 +14,17 @@ interface GroupsContainerViewProps export const GroupsContainerView: FC = props => { - const { itsMe = null, groups = null, onLeaveGroup = null } = props; - + const { itsMe = null, groups = null, onLeaveGroup = null, overflow = 'hidden', gap = 2, ...rest } = props; const [ selectedGroupId, setSelectedGroupId ] = useState(null); const [ groupInformation, setGroupInformation ] = useState(null); + const favoriteGroup = (groupId: number) => SendMessageComposer(new GroupFavoriteComposer(groupId)); + const onGroupInformationEvent = useCallback((event: GroupInformationEvent) => { const parser = event.getParser(); - if(!selectedGroupId || selectedGroupId !== parser.id || parser.flag) return; + if(!selectedGroupId || (selectedGroupId !== parser.id) || parser.flag) return; if(groupInformation) setGroupInformation(null); @@ -35,37 +35,54 @@ export const GroupsContainerView: FC = props => useEffect(() => { - if(groups.length > 0 && !selectedGroupId) setSelectedGroupId(groups[0].groupId); - }, [ groups, selectedGroupId ]); + if(!selectedGroupId) return; + + SendMessageComposer(new GroupInformationComposer(selectedGroupId, false)); + }, [ selectedGroupId ]); useEffect(() => { - if(selectedGroupId) SendMessageComposer(new GroupInformationComposer(selectedGroupId, false)); - }, [ selectedGroupId ]); + BatchUpdates(() => + { + setGroupInformation(null); - const favoriteGroup = useCallback((groupId: number) => + if(groups.length > 0) setSelectedGroupId(groups[0].groupId); + }); + }, [ groups ]); + + if(!groups || !groups.length) { - SendMessageComposer(new GroupFavoriteComposer(groupId)); - }, []); - - if(!groups) return null; + return ( + + + + + + + + ); + } return ( -
-
-
+ + + { groups.map((group, index) => { - return
setSelectedGroupId(group.groupId) } className={ 'profile-groups-item position-relative flex-shrink-0 d-flex align-items-center justify-content-center cursor-pointer' + classNames({ ' active': selectedGroupId === group.groupId }) }> - { itsMe && favoriteGroup(group.groupId) } /> } - -
+ return ( + setSelectedGroupId(group.groupId) } className="p-1"> + { itsMe && + favoriteGroup(group.groupId) } /> } + + + ) }) } -
-
-
- { groupInformation && } -
-
+ + + + { groupInformation && + } + + ); } diff --git a/src/components/user-profile/views/RelationshipsContainerView.tsx b/src/components/user-profile/views/RelationshipsContainerView.tsx index 3ed16601..9b698ecd 100644 --- a/src/components/user-profile/views/RelationshipsContainerView.tsx +++ b/src/components/user-profile/views/RelationshipsContainerView.tsx @@ -1,75 +1,62 @@ -import { RelationshipStatusEnum, RelationshipStatusInfo, RelationshipStatusInfoMessageParser } from '@nitrots/nitro-renderer'; -import { FC, useCallback } from 'react'; +import { RelationshipStatusEnum, RelationshipStatusInfoMessageParser } from '@nitrots/nitro-renderer'; +import { FC } from 'react'; import { GetUserProfile, LocalizeText } from '../../../api'; -import { LayoutAvatarImageView } from '../../../common'; +import { Column, Flex, LayoutAvatarImageView, Text } from '../../../common'; interface RelationshipsContainerViewProps { relationships: RelationshipStatusInfoMessageParser; - simple?: boolean; +} + +interface RelationshipsContainerRelationshipViewProps +{ + type: number; } export const RelationshipsContainerView: FC = props => { - const { relationships = null, simple = false } = props; + const { relationships = null } = props; - const OnUserClick = useCallback((user: RelationshipStatusInfo) => - { - if(!user) return; - - GetUserProfile(user.randomFriendId); - }, []); - - const RelationshipComponent = useCallback(({ type }) => + const RelationshipComponent = ({ type }: RelationshipsContainerRelationshipViewProps) => { const relationshipInfo = (relationships && relationships.relationshipStatusMap.hasKey(type)) ? relationships.relationshipStatusMap.getValue(type) : null; - - if(simple && !relationshipInfo) return null; - const relationshipName = RelationshipStatusEnum.RELATIONSHIP_NAMES[type].toLocaleLowerCase(); return ( -
- -
- - OnUserClick(relationshipInfo)}> - { - (relationshipInfo && relationshipInfo.friendCount > 0) ? relationshipInfo.randomFriendName : LocalizeText('extendedprofile.add.friends') - } - - { - (simple && relationshipInfo && relationshipInfo.friendCount > 1) && - - {' ' + LocalizeText(`extendedprofile.relstatus.others.${relationshipName}`, ['count'], [(relationshipInfo.friendCount - 1).toString()])} - - } - { - (!simple && relationshipInfo && relationshipInfo.friendCount > 0) && - - } - - - { - !simple &&
- { - (relationshipInfo && relationshipInfo.friendCount > 1) ? LocalizeText(`extendedprofile.relstatus.others.${relationshipName}`, ['count'], [(relationshipInfo.friendCount - 1).toString()]) : '' - } - { - (relationshipInfo && relationshipInfo.friendCount < 1) ? LocalizeText('extendedprofile.no.friends.in.this.category') : '' - } -
- } -
-
+ + + + + + + (relationshipInfo && (relationshipInfo.randomFriendId >= 1) && GetUserProfile(relationshipInfo.randomFriendId)) }> + { (!relationshipInfo || (relationshipInfo.friendCount === 0)) && + LocalizeText('extendedprofile.add.friends') } + { (relationshipInfo && (relationshipInfo.friendCount >= 1)) && + relationshipInfo.randomFriendName } + + { (relationshipInfo && (relationshipInfo.friendCount >= 1)) && + + + } + + + { (!relationshipInfo || (relationshipInfo.friendCount === 0)) && + LocalizeText('extendedprofile.no.friends.in.this.category') } + { (relationshipInfo && (relationshipInfo.friendCount > 1)) && + LocalizeText(`extendedprofile.relstatus.others.${ relationshipName }`, [ 'count' ], [ (relationshipInfo.friendCount - 1).toString() ]) } +   + + + ); - }, [OnUserClick, relationships, simple]); + } return ( -
- - - -
+ <> + + + + ); } diff --git a/src/components/user-profile/views/UserContainerView.tsx b/src/components/user-profile/views/UserContainerView.tsx index 99847ef8..0dd17d33 100644 --- a/src/components/user-profile/views/UserContainerView.tsx +++ b/src/components/user-profile/views/UserContainerView.tsx @@ -1,7 +1,7 @@ import { FriendlyTime, UserProfileParser } from '@nitrots/nitro-renderer'; -import { FC, useCallback } from 'react'; +import { FC } from 'react'; import { GetSessionDataManager, LocalizeText } from '../../../api'; -import { LayoutAvatarImageView } from '../../../common'; +import { Button, Column, Flex, LayoutAvatarImageView, Text } from '../../../common'; interface UserContainerViewProps { @@ -11,42 +11,51 @@ interface UserContainerViewProps export const UserContainerView: FC = props => { const { userProfile = null } = props; - - const OnlineIcon = useCallback(() => -{ - if(userProfile.isOnline) return (); - else return (); - }, [ userProfile ]); - - const FriendRequestComponent = useCallback(() => -{ - if(userProfile.id === GetSessionDataManager().userId) return ({LocalizeText('extendedprofile.me')} ); - - if(userProfile.isMyFriend) return ({LocalizeText('extendedprofile.friend')}); - - if(userProfile.requestSent) return ({LocalizeText('extendedprofile.friendrequestsent')}); - - return () - }, [ userProfile ]); + const isOwnProfile = (userProfile.id === GetSessionDataManager().userId); + const canSendFriendRequest = (!isOwnProfile && !userProfile.isMyFriend && !userProfile.requestSent); return ( -
-
- -
-
-
-
{userProfile.username}
-
{userProfile.motto}
-
-
-
{LocalizeText('extendedprofile.achievementscore')} {userProfile.achievementPoints}
-
- - -
-
-
-
+ + + + + + + { userProfile.username } + { userProfile.motto }  + + + + { LocalizeText('extendedprofile.created') } { userProfile.registration } + + + { LocalizeText('extendedprofile.last.login') } { FriendlyTime.format(userProfile.secondsSinceLastVisit, '.ago', 2) } + + + { LocalizeText('extendedprofile.achievementscore') } { userProfile.achievementPoints } + + + + { userProfile.isOnline && + } + { !userProfile.isOnline && + } + + { canSendFriendRequest && + } + { !canSendFriendRequest && + <> + + { isOwnProfile && + { LocalizeText('extendedprofile.me') } } + { userProfile.isMyFriend && + { LocalizeText('extendedprofile.friend') } } + { userProfile.requestSent && + { LocalizeText('extendedprofile.friendrequestsent') } } + } + + + + ) }