started user profiles

This commit is contained in:
Dank074 2021-08-12 01:30:02 -05:00
parent 3b5974a67b
commit 9edaa66e94
19 changed files with 355 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

View File

@ -513,6 +513,42 @@
height: 21px;
}
&.icon-pf-online {
background: url('../images/profile/icons/online.gif');
width: 40px;
height: 16px;
}
&.icon-pf-offline {
background: url('../images/profile/icons/offline.png');
width: 40px;
height: 16px;
}
&.icon-pf-tick {
background: url('../images/profile/icons/tick.png');
width: 11px;
height: 10px;
}
&.icon-relationship-heart {
background: url('../images/profile/icons/heart.png');
width: 16px;
height: 14px;
}
&.icon-relationship-bobba {
background: url('../images/profile/icons/bobba.png');
width: 16px;
height: 14px;
}
&.icon-relationship-smile {
background: url('../images/profile/icons/smile.png');
width: 16px;
height: 14px;
}
&.spin {
animation: rotating 1s linear infinite;
}

View File

@ -16,3 +16,4 @@
@import './wired/WiredView';
@import './mod-tools/ModToolsView';
@import './achievements/AchievementsView';
@import './user-profile/UserProfileVew';

View File

@ -1,4 +1,4 @@
import { FollowFriendComposer, MouseEventType } from '@nitrots/nitro-renderer';
import { FollowFriendComposer, MouseEventType, UserProfileComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { SendMessageHook } from '../../../../hooks/messages';
import { LocalizeText } from '../../../../utils/LocalizeText';
@ -16,6 +16,11 @@ export const FriendBarItemView: FC<FriendBarItemViewProps> = props =>
SendMessageHook(new FollowFriendComposer(friend.id));
}, [ friend ]);
const openProfile = useCallback(() =>
{
SendMessageHook(new UserProfileComposer(friend.id));
}, [ friend ]);
const onClick = useCallback((event: MouseEvent) =>
{
const element = elementRef.current;
@ -56,7 +61,7 @@ export const FriendBarItemView: FC<FriendBarItemViewProps> = props =>
<div className="d-flex justify-content-between">
<i className="icon icon-fb-chat cursor-pointer" />
{ friend.followingAllowed && <i onClick={ followFriend } className="icon icon-fb-visit cursor-pointer" /> }
<i className="icon icon-fb-profile cursor-pointer" />
<i onClick={ openProfile } className="icon icon-fb-profile cursor-pointer" />
</div> }
</div>
);

View File

@ -14,6 +14,7 @@ import { NotificationCenterView } from '../notification-center/NotificationCente
import { RightSideView } from '../right-side/RightSideView';
import { RoomHostView } from '../room-host/RoomHostView';
import { ToolbarView } from '../toolbar/ToolbarView';
import { UserProfileView } from '../user-profile/UserProfileView';
import { WiredView } from '../wired/WiredView';
import { MainViewProps } from './MainView.types';
@ -60,6 +61,7 @@ export const MainView: FC<MainViewProps> = props =>
<FriendListView />
<RightSideView />
<NotificationCenterView />
<UserProfileView />
</div>
);
}

View File

@ -1,5 +1,7 @@
import { MouseEventType } from '@nitrots/nitro-renderer';
import { FC, useEffect, useRef } from 'react';
import { MouseEventType, UserProfileComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useRef } from 'react';
import { GetSessionDataManager } from '../../../api';
import { SendMessageHook } from '../../../hooks';
import { ToolbarViewItems } from '../ToolbarView.types';
import { ToolbarMeViewProps } from './ToolbarMeView.types';
@ -29,6 +31,11 @@ export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
}
}, [ elementRef, setMeExpanded ]);
const openProfile = useCallback(() =>
{
SendMessageHook(new UserProfileComposer(GetSessionDataManager().userId));
}, []);
return (
<div ref={ elementRef } className="d-flex nitro-toolbar-me px-1 py-2">
<div className="navigation-items">
@ -42,7 +49,7 @@ export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
<i className="icon icon-me-achievements"></i>
</div>
<div className="navigation-item">
<i className="icon icon-me-profile"></i>
<i className="icon icon-me-profile" onClick={() => openProfile()}></i>
</div>
<div className="navigation-item">
<i className="icon icon-me-rooms"></i>

View File

@ -0,0 +1,77 @@
.user-profile {
.content-area {
color: black;
}
.user-container
{
border-right: 1px solid gray;
.avatar-image {
left: -10px;
}
}
.badge-container
{
min-height: 50px;
background: rgba(0, 0, 0, .1);
border-radius: 5px;
margin: 0px;
margin-bottom: 2px;
.badge-image {
margin-left: 2px;
margin-right: 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%;
.relationships-container {
.relationship-container {
margin: 10px;
.relationship
{
margin-left: 10px;
background-color: white;
padding: 5px;
border-radius: 5px;
display: inline-block;
text-decoration: underline;
width: 100%;
.avatar-image {
position: absolute;
width: 50px;
height: 80px;
right: 0;
margin-top: -60px;
margin-right: 10px;
}
}
}
}
}
}

View File

@ -0,0 +1,92 @@
import { UserCurrentBadgesComposer, UserCurrentBadgesEvent, UserProfileEvent, UserProfileParser, UserRelationshipsComposer, UserRelationshipsEvent, UserRelationshipsParser } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { CreateMessageHook, SendMessageHook } from '../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../layout';
import { LocalizeText } from '../../utils';
import { BadgesContainerView } from './views/badges-container/BadgesContainerView';
import { FriendsContainerView } from './views/friends-container/FriendsContainerView';
import { UserContainerView } from './views/user-container/UserContainerView';
export const UserProfileView: FC = props =>
{
const [userProfile, setUserProfile] = useState<UserProfileParser>(null);
const [userBadges, setUserBadges] = useState<string[]>([]);
const [userRelationships, setUserRelationships] = useState<UserRelationshipsParser>(null);
const OnClose = useCallback(() =>
{
setUserProfile(null);
setUserBadges([]);
setUserRelationships(null);
}, []);
const OnUserCurrentBadgesEvent = useCallback((event: UserCurrentBadgesEvent) =>
{
const parser = event.getParser();
if (userProfile && parser.userId === userProfile.id)
setUserBadges(parser.badges);
}, [userProfile, setUserBadges]);
CreateMessageHook(UserCurrentBadgesEvent, OnUserCurrentBadgesEvent);
const OnUserRelationshipsEvent = useCallback((event: UserRelationshipsEvent) =>
{
const parser = event.getParser();
if (userProfile && parser.id === userProfile.id)
setUserRelationships(parser);
}, [userProfile, setUserRelationships]);
CreateMessageHook(UserRelationshipsEvent, OnUserRelationshipsEvent);
const OnUserProfileEvent = useCallback((event: UserProfileEvent) =>
{
const parser = event.getParser();
if(userProfile && userProfile.id === parser.id)
OnClose();
setUserProfile(parser);
SendMessageHook(new UserCurrentBadgesComposer(parser.id));
SendMessageHook(new UserRelationshipsComposer(parser.id));
}, [OnClose, userProfile]);
CreateMessageHook(UserProfileEvent, OnUserProfileEvent);
if (!userProfile) return null;
return (
<div className="user-profile">
<NitroCardView>
<NitroCardHeaderView headerText={LocalizeText('extendedprofile.caption')} onCloseClick={OnClose} />
<NitroCardContentView>
<div className="row">
<div className="col-sm-7 user-container">
<UserContainerView id={userProfile.id} username={userProfile.username} motto={userProfile.motto} figure={userProfile.figure} secondsSinceLastLogin={userProfile.lastVisit} creation={userProfile.registration} achievementScore={userProfile.achievementPoints} isFriend={userProfile.isMyFriend} isOnline={userProfile.isOnline} requestSent={userProfile.requestSent} />
<BadgesContainerView badges={userBadges} />
</div>
<div className="col-sm-5">
{
userRelationships && <FriendsContainerView relationships={new Map([['heart', userRelationships.hearts], ['smile', userRelationships.smiles], ['bobba', userRelationships.bobbas]])} friendsCount={userProfile.friendsCount} />
}
</div>
</div>
<div className="row rooms-button-container align-items-center">
<div className="col-sm-12 d-flex align-content-center w-100">
<i className="icon icon-rooms" /><span className="rooms-button">{LocalizeText('extendedprofile.rooms')}</span>
</div>
</div>
<div className="row">
<div className="col-sm-4">
groups list goes here
</div>
<div className="col-sm-8">
group info goes here
</div>
</div>
</NitroCardContentView>
</NitroCardView>
</div>
)
}

View File

@ -0,0 +1,20 @@
import { FC } from 'react';
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
import { BadgesContainerViewProps } from './BadgesContainerView.types';
export const BadgesContainerView: FC<BadgesContainerViewProps> = props =>
{
const {badges = null} = props;
return (
<div className="row badge-container">
<div className="col-sm-12 d-flex">
{
badges.map( (badge, index) => {
return <BadgeImageView badgeCode={badge} key={index}/>
})
}
</div>
</div>
);
}

View File

@ -0,0 +1,3 @@
export interface BadgesContainerViewProps {
badges: string[];
}

View File

@ -0,0 +1,44 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../utils';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { FriendsContainerViewProps } from './FriendsContainerView.types';
export const FriendsContainerView: FC<FriendsContainerViewProps> = props =>
{
const { relationships = null, friendsCount = null } = props;
const RelationshipComponent = useCallback(({ type }) =>
{
const randomUserIndex = (relationships && relationships.has(type) && relationships.get(type).length) ?
Math.floor(Math.random() * relationships.get(type).length) : -1;
const randomUser = randomUserIndex > -1 ? relationships.get(type)[randomUserIndex] : null;
return (
<div className="relationship-container d-flex">
<i className={`icon icon-relationship-${type}`} />
<span className="relationship">
{
randomUser ? randomUser.username : LocalizeText('extendedprofile.add.friends')
}
{
randomUser &&
<AvatarImageView figure={randomUser.figure} headOnly={true} direction={4} />
}
</span>
</div>
);
}, [relationships]);
return (
<div className="friends-container">
<div className="mb-1" dangerouslySetInnerHTML={{ __html: LocalizeText('extendedprofile.friends.count', ['count'], [friendsCount.toString()]) }} />
<div className="mb-1"><b>{LocalizeText('extendedprofile.relstatus')}</b></div>
<div className="row justify-content-center relationships-container align-items-center">
<RelationshipComponent type={'heart'} />
<RelationshipComponent type={'smile'} />
<RelationshipComponent type={'bobba'} />
</div>
</div>
)
}

View File

@ -0,0 +1,6 @@
import { UserRelationshipDataParser } from '@nitrots/nitro-renderer';
export interface FriendsContainerViewProps {
relationships: Map<string, UserRelationshipDataParser[]>;
friendsCount: number;
}

View File

@ -0,0 +1,45 @@
import { FriendlyTime } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { GetSessionDataManager } from '../../../../api';
import { LocalizeText } from '../../../../utils';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { UserContainerViewProps } from './UserContainerView.types';
export const UserContainerView: FC<UserContainerViewProps> = props =>
{
const {figure = null, username = null, motto = null, creation = null, secondsSinceLastLogin = null, achievementScore, isFriend = null, isOnline = null, id = null, requestSent = null} = props;
const OnlineIcon = useCallback(() => {
if(isOnline) return (<i className="icon icon-pf-online" />);
else return (<i className="icon icon-pf-offline" />);
}, [isOnline]);
const FriendRequestComponent = useCallback(() => {
if(id === GetSessionDataManager().userId) return (<span><i className="icon icon-pf-tick" />{LocalizeText('extendedprofile.me')}</span> );
if(isFriend) return (<span><i className="icon icon-pf-tick" />{LocalizeText('extendedprofile.friend')}</span>);
if(requestSent) return (<span><i className="icon icon-pf-tick" />{LocalizeText('extendedprofile.friendrequestsent')}</span>);
return (<button className="btn btn-success btn-sm w-100">{LocalizeText('extendedprofile.addasafriend')}</button>)
}, [id, isFriend, requestSent]);
return (
<div className="row">
<div className="col-sm-4">
<AvatarImageView figure={figure} direction={2} />
</div>
<div className="col-sm-8">
<div className="user-info-container">
<h5>{username}</h5>
<div className="mb-1">{motto}</div>
<div className="mb-1" dangerouslySetInnerHTML={{ __html: LocalizeText('extendedprofile.created', ['created'], [creation]) }} />
<div className="mb-1" dangerouslySetInnerHTML={{ __html: LocalizeText('extendedprofile.last.login', ['lastlogin'], [FriendlyTime.format(secondsSinceLastLogin, '.ago', 1)]) }} />
<div className="mb-1"><b>{LocalizeText('extendedprofile.achievementscore')}</b> {achievementScore}</div>
<OnlineIcon />
<FriendRequestComponent />
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,12 @@
export interface UserContainerViewProps {
id: number;
username: string;
figure: string;
motto: string;
creation: string;
secondsSinceLastLogin: number;
achievementScore: number;
isFriend: boolean;
requestSent: boolean;
isOnline: boolean;
}