Friend List
BIN
src/assets/images/friendlist/icons/icon_chat.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
src/assets/images/friendlist/icons/icon_follow.png
Normal file
After Width: | Height: | Size: 162 B |
BIN
src/assets/images/friendlist/icons/icon_relationship_none.png
Normal file
After Width: | Height: | Size: 177 B |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 174 B |
Before Width: | Height: | Size: 173 B After Width: | Height: | Size: 173 B |
BIN
src/assets/images/icons/icon_cog.png
Normal file
After Width: | Height: | Size: 218 B |
@ -119,6 +119,12 @@
|
|||||||
height: 34px;
|
height: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-cog {
|
||||||
|
background: url('../images/icons/icon_cog.png');
|
||||||
|
width: 14px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-joinroom {
|
&.icon-joinroom {
|
||||||
background-image: url('../images/toolbar/icons/joinroom.png');
|
background-image: url('../images/toolbar/icons/joinroom.png');
|
||||||
width: 21px;
|
width: 21px;
|
||||||
@ -147,6 +153,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-deny {
|
||||||
|
background: url('../images/icons/deny.png');
|
||||||
|
width: 13px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-accept {
|
||||||
|
background: url('../images/icons/accept.png');
|
||||||
|
width: 13px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-wired-trigger {
|
&.icon-wired-trigger {
|
||||||
background-image: url('../images/wired/icon_trigger.png');
|
background-image: url('../images/wired/icon_trigger.png');
|
||||||
width: 13px;
|
width: 13px;
|
||||||
@ -531,6 +549,12 @@
|
|||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-relationship-none {
|
||||||
|
background: url('../images/friendlist/icons/icon_relationship_none.png');
|
||||||
|
width: 16px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-relationship-heart {
|
&.icon-relationship-heart {
|
||||||
background: url('../images/profile/icons/heart.png');
|
background: url('../images/profile/icons/heart.png');
|
||||||
width: 16px;
|
width: 16px;
|
||||||
@ -615,18 +639,6 @@
|
|||||||
height: 13px;
|
height: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.icon-group-remove-member {
|
|
||||||
background: url('../images/groups/icons/group_icon_remove_member.png');
|
|
||||||
width: 13px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.icon-group-accept-member {
|
|
||||||
background: url('../images/groups/icons/group_icon_accept_member.png');
|
|
||||||
width: 13px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.icon-group-small-owner {
|
&.icon-group-small-owner {
|
||||||
background: url('../images/groups/icons/group_icon_small_owner.png');
|
background: url('../images/groups/icons/group_icon_small_owner.png');
|
||||||
width: 13px;
|
width: 13px;
|
||||||
@ -663,6 +675,18 @@
|
|||||||
height: 11px;
|
height: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-friendlist-follow {
|
||||||
|
background: url('../images/friendlist/icons/icon_follow.png');
|
||||||
|
width: 16px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-friendlist-chat {
|
||||||
|
background: url('../images/friendlist/icons/icon_chat.png');
|
||||||
|
width: 17px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
&.spin {
|
&.spin {
|
||||||
animation: rotating 1s linear infinite;
|
animation: rotating 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,7 @@
|
|||||||
border-bottom: 1px solid rgba($black, 0.2);
|
border-bottom: 1px solid rgba($black, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
|
||||||
> .nitro-card-accordion-item-header {
|
|
||||||
background: rgba($white, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nitro-card-accordion-item-content {
|
.nitro-card-accordion-item-content {
|
||||||
background: rgba($black, 0.1);
|
background: rgba($white, 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FriendListFragmentEvent, FriendListUpdateEvent, GetFriendRequestsComposer, MessengerInitEvent } from '@nitrots/nitro-renderer';
|
import { FriendListFragmentEvent, FriendListUpdateEvent, FriendRequestsEvent, GetFriendRequestsComposer, MessengerInitEvent } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { MessengerSettings } from './common/MessengerSettings';
|
import { MessengerSettings } from './common/MessengerSettings';
|
||||||
@ -52,9 +52,22 @@ export const FriendListMessageHandler: FC<FriendListMessageHandlerProps> = props
|
|||||||
});
|
});
|
||||||
}, [ dispatchFriendListState ]);
|
}, [ dispatchFriendListState ]);
|
||||||
|
|
||||||
|
const onFriendRequestsEvent = useCallback((event: FriendRequestsEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
dispatchFriendListState({
|
||||||
|
type: FriendListActions.PROCESS_REQUESTS,
|
||||||
|
payload: {
|
||||||
|
requests: parser.requests
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ dispatchFriendListState ]);
|
||||||
|
|
||||||
CreateMessageHook(MessengerInitEvent, onMessengerInitEvent);
|
CreateMessageHook(MessengerInitEvent, onMessengerInitEvent);
|
||||||
CreateMessageHook(FriendListFragmentEvent, onFriendListFragmentEvent);
|
CreateMessageHook(FriendListFragmentEvent, onFriendListFragmentEvent);
|
||||||
CreateMessageHook(FriendListUpdateEvent, onFriendListUpdateEvent);
|
CreateMessageHook(FriendListUpdateEvent, onFriendListUpdateEvent);
|
||||||
|
CreateMessageHook(FriendRequestsEvent, onFriendRequestsEvent);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { MessengerInitComposer, RoomEngineObjectEvent, RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer';
|
import { MessengerInitComposer, RoomEngineObjectEvent, RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { GetRoomSession, LocalizeText } from '../../api';
|
import { GetRoomSession, LocalizeText } from '../../api';
|
||||||
import { FriendEnteredRoomEvent, FriendListEvent } from '../../events';
|
import { FriendEnteredRoomEvent, FriendListEvent } from '../../events';
|
||||||
@ -8,20 +8,25 @@ import { FriendListSendFriendRequestEvent } from '../../events/friend-list/Frien
|
|||||||
import { useRoomEngineEvent } from '../../hooks/events';
|
import { useRoomEngineEvent } from '../../hooks/events';
|
||||||
import { dispatchUiEvent, useUiEvent } from '../../hooks/events/ui/ui-event';
|
import { dispatchUiEvent, useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||||
import { SendMessageHook } from '../../hooks/messages/message-event';
|
import { SendMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../layout';
|
import { NitroCardAccordionItemView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
||||||
import { FriendListContextProvider } from './context/FriendListContext';
|
import { FriendListContextProvider } from './context/FriendListContext';
|
||||||
import { FriendListMessageHandler } from './FriendListMessageHandler';
|
import { FriendListMessageHandler } from './FriendListMessageHandler';
|
||||||
import { FriendListViewProps } from './FriendListView.types';
|
import { FriendListViewProps } from './FriendListView.types';
|
||||||
import { FriendListReducer, initialFriendList } from './reducers/FriendListReducer';
|
import { FriendListReducer, initialFriendList } from './reducers/FriendListReducer';
|
||||||
import { FriendBarView } from './views/friend-bar/FriendBarView';
|
import { FriendBarView } from './views/friend-bar/FriendBarView';
|
||||||
import { FriendListFriendsView } from './views/friends/FriendListFriendsView';
|
import { FriendListFriendsView } from './views/friends/FriendListFriendsView';
|
||||||
|
import { FriendListRequestsView } from './views/requests/FriendListRequestsView';
|
||||||
|
|
||||||
|
const TABS: string[] = ['friendlist.friends', 'generic.search'];
|
||||||
|
|
||||||
export const FriendListView: FC<FriendListViewProps> = props =>
|
export const FriendListView: FC<FriendListViewProps> = props =>
|
||||||
{
|
{
|
||||||
|
const [ friendListState, dispatchFriendListState ] = useReducer(FriendListReducer, initialFriendList);
|
||||||
|
const { friends = null, requests = null, settings = null } = friendListState;
|
||||||
|
|
||||||
const [ isVisible, setIsVisible ] = useState(false);
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
const [ isReady, setIsReady ] = useState(false);
|
const [ isReady, setIsReady ] = useState(false);
|
||||||
const [ friendListState, dispatchFriendListState ] = useReducer(FriendListReducer, initialFriendList);
|
const [ currentTab, setCurrentTab ] = useState<number>(0);
|
||||||
const { settings = null } = friendListState;
|
|
||||||
|
|
||||||
const onFriendListEvent = useCallback((event: FriendListEvent) =>
|
const onFriendListEvent = useCallback((event: FriendListEvent) =>
|
||||||
{
|
{
|
||||||
@ -37,7 +42,6 @@ export const FriendListView: FC<FriendListViewProps> = props =>
|
|||||||
const requestEvent = (event as FriendListSendFriendRequestEvent);
|
const requestEvent = (event as FriendListSendFriendRequestEvent);
|
||||||
return;
|
return;
|
||||||
case FriendListEvent.REQUEST_FRIEND_LIST:
|
case FriendListEvent.REQUEST_FRIEND_LIST:
|
||||||
console.log('requested');
|
|
||||||
dispatchUiEvent(new FriendListContentEvent(friendListState.friends));
|
dispatchUiEvent(new FriendListContentEvent(friendListState.friends));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -84,20 +88,49 @@ export const FriendListView: FC<FriendListViewProps> = props =>
|
|||||||
SendMessageHook(new MessengerInitComposer());
|
SendMessageHook(new MessengerInitComposer());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const onlineFriends = useMemo(() =>
|
||||||
|
{
|
||||||
|
if(!friends) return [];
|
||||||
|
|
||||||
|
return friends.filter(f => f.online);
|
||||||
|
}, [ friends ]);
|
||||||
|
|
||||||
|
const offlineFriends = useMemo(() =>
|
||||||
|
{
|
||||||
|
if(!friends) return [];
|
||||||
|
|
||||||
|
return friends.filter(f => !f.online);
|
||||||
|
}, [ friends ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FriendListContextProvider value={ { friendListState, dispatchFriendListState } }>
|
<FriendListContextProvider value={ { friendListState, dispatchFriendListState } }>
|
||||||
<FriendListMessageHandler />
|
<FriendListMessageHandler />
|
||||||
{ isReady && createPortal(<FriendBarView />, document.getElementById('toolbar-friend-bar-container')) }
|
{ isReady && createPortal(<FriendBarView />, document.getElementById('toolbar-friend-bar-container')) }
|
||||||
{ isVisible &&
|
{ isVisible &&
|
||||||
<NitroCardView uniqueKey="friend-list" className="nitro-friend-list">
|
<NitroCardView className="nitro-friend-list">
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ event => setIsVisible(false) } />
|
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ () => setIsVisible(false) } />
|
||||||
<NitroCardContentView>
|
<NitroCardContentView className="p-0">
|
||||||
<div className="text-black fw-bold">{ LocalizeText('friendlist.search.friendscaption') }</div>
|
<NitroCardTabsView>
|
||||||
<FriendListFriendsView online={ true } />
|
{ TABS.map((tab, index) =>
|
||||||
<div className="text-black fw-bold">{ LocalizeText('friendlist.search.friendscaption') }</div>
|
{
|
||||||
<FriendListFriendsView online={ true } />
|
return (<NitroCardTabsItemView key={ index } isActive={ currentTab === index } onClick={ () => setCurrentTab(index) }>
|
||||||
<div className="text-black fw-bold">{ LocalizeText('friendlist.friends.offlinecaption') }</div>
|
{ LocalizeText(tab) }
|
||||||
<FriendListFriendsView online={ false } />
|
</NitroCardTabsItemView>);
|
||||||
|
}) }
|
||||||
|
</NitroCardTabsView>
|
||||||
|
<div className="text-black">
|
||||||
|
{ currentTab === 0 && <NitroCardAccordionView>
|
||||||
|
<NitroCardAccordionItemView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` }>
|
||||||
|
<FriendListFriendsView list={ onlineFriends } />
|
||||||
|
</NitroCardAccordionItemView>
|
||||||
|
<NitroCardAccordionItemView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
|
||||||
|
<FriendListFriendsView list={ offlineFriends } />
|
||||||
|
</NitroCardAccordionItemView>
|
||||||
|
{ requests.length > 0 && <NitroCardAccordionItemView headerText={ LocalizeText('friendlist.tab.friendrequests') + ` (${requests.length})` }>
|
||||||
|
<FriendListRequestsView list={ requests } />
|
||||||
|
</NitroCardAccordionItemView> }
|
||||||
|
</NitroCardAccordionView> }
|
||||||
|
</div>
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView> }
|
</NitroCardView> }
|
||||||
</FriendListContextProvider>
|
</FriendListContextProvider>
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
|
import { FriendParser } from '@nitrots/nitro-renderer';
|
||||||
|
|
||||||
export class MessengerFriend
|
export class MessengerFriend
|
||||||
{
|
{
|
||||||
|
public static RELATIONSHIP_NONE: number = 0;
|
||||||
|
public static RELATIONSHIP_HEART: number = 1;
|
||||||
|
public static RELATIONSHIP_SMILE: number = 2;
|
||||||
|
public static RELATIONSHIP_BOBBA: number = 3;
|
||||||
|
|
||||||
public id: number = -1;
|
public id: number = -1;
|
||||||
public name: string = null;
|
public name: string = null;
|
||||||
public gender: number = 0;
|
public gender: number = 0;
|
||||||
@ -15,4 +22,22 @@ export class MessengerFriend
|
|||||||
public pocketHabboUser: boolean = false;
|
public pocketHabboUser: boolean = false;
|
||||||
public relationshipStatus: number = -1;
|
public relationshipStatus: number = -1;
|
||||||
public unread: number = 0;
|
public unread: number = 0;
|
||||||
|
|
||||||
|
public populate(parser: FriendParser): void
|
||||||
|
{
|
||||||
|
this.id = parser.id;
|
||||||
|
this.name = parser.name;
|
||||||
|
this.gender = parser.gender;
|
||||||
|
this.online = parser.online;
|
||||||
|
this.followingAllowed = parser.followingAllowed;
|
||||||
|
this.figure = parser.figure;
|
||||||
|
this.categoryId = parser.categoryId;
|
||||||
|
this.motto = parser.motto;
|
||||||
|
this.realName = parser.realName;
|
||||||
|
this.lastAccess = parser.lastAccess;
|
||||||
|
this.persistedMessageUser = parser.persistedMessageUser;
|
||||||
|
this.vipMember = parser.vipMember;
|
||||||
|
this.pocketHabboUser = parser.pocketHabboUser;
|
||||||
|
this.relationshipStatus = parser.relationshipStatus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
41
src/views/friend-list/common/MessengerRequest.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { FriendRequestData } from '@nitrots/nitro-renderer';
|
||||||
|
|
||||||
|
export class MessengerRequest
|
||||||
|
{
|
||||||
|
private _id: number;
|
||||||
|
private _name: string;
|
||||||
|
private _requesterUserId: number;
|
||||||
|
private _figureString: string;
|
||||||
|
|
||||||
|
public populate(data: FriendRequestData): boolean
|
||||||
|
{
|
||||||
|
if(!data) return false;
|
||||||
|
|
||||||
|
this._id = data.requestId;
|
||||||
|
this._name = data.requesterName;
|
||||||
|
this._figureString = data.figureString;
|
||||||
|
this._requesterUserId = data.requesterUserId;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): number
|
||||||
|
{
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string
|
||||||
|
{
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get requesterUserId(): number
|
||||||
|
{
|
||||||
|
return this._requesterUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get figureString(): string
|
||||||
|
{
|
||||||
|
return this._figureString;
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,21 @@
|
|||||||
import { FriendListUpdateParser, FriendParser } from '@nitrots/nitro-renderer';
|
import { FriendListUpdateParser, FriendParser, FriendRequestData } from '@nitrots/nitro-renderer';
|
||||||
import { Reducer } from 'react';
|
import { Reducer } from 'react';
|
||||||
import { MessengerFriend } from '../common/MessengerFriend';
|
import { MessengerFriend } from '../common/MessengerFriend';
|
||||||
|
import { MessengerRequest } from '../common/MessengerRequest';
|
||||||
import { MessengerSettings } from '../common/MessengerSettings';
|
import { MessengerSettings } from '../common/MessengerSettings';
|
||||||
|
|
||||||
|
function compareName(a, b)
|
||||||
|
{
|
||||||
|
if( a.name < b.name ) return -1;
|
||||||
|
if( a.name > b.name ) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFriendListState
|
export interface IFriendListState
|
||||||
{
|
{
|
||||||
settings: MessengerSettings;
|
settings: MessengerSettings;
|
||||||
friends: MessengerFriend[];
|
friends: MessengerFriend[];
|
||||||
|
requests: MessengerRequest[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFriendListAction
|
export interface IFriendListAction
|
||||||
@ -16,6 +25,7 @@ export interface IFriendListAction
|
|||||||
settings?: MessengerSettings;
|
settings?: MessengerSettings;
|
||||||
fragment?: FriendParser[];
|
fragment?: FriendParser[];
|
||||||
update?: FriendListUpdateParser;
|
update?: FriendListUpdateParser;
|
||||||
|
requests?: FriendRequestData[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,11 +35,13 @@ export class FriendListActions
|
|||||||
public static UPDATE_SETTINGS: string = 'FLA_UPDATE_SETTINGS';
|
public static UPDATE_SETTINGS: string = 'FLA_UPDATE_SETTINGS';
|
||||||
public static PROCESS_FRAGMENT: string = 'FLA_PROCESS_FRAGMENT';
|
public static PROCESS_FRAGMENT: string = 'FLA_PROCESS_FRAGMENT';
|
||||||
public static PROCESS_UPDATE: string = 'FLA_PROCESS_UPDATE';
|
public static PROCESS_UPDATE: string = 'FLA_PROCESS_UPDATE';
|
||||||
|
public static PROCESS_REQUESTS: string = 'FLA_PROCESS_REQUESTS';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialFriendList: IFriendListState = {
|
export const initialFriendList: IFriendListState = {
|
||||||
settings: null,
|
settings: null,
|
||||||
friends: []
|
friends: [],
|
||||||
|
requests: []
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FriendListReducer: Reducer<IFriendListState, IFriendListAction> = (state, action) =>
|
export const FriendListReducer: Reducer<IFriendListState, IFriendListAction> = (state, action) =>
|
||||||
@ -69,6 +81,8 @@ export const FriendListReducer: Reducer<IFriendListState, IFriendListAction> = (
|
|||||||
else friends.push(newFriend);
|
else friends.push(newFriend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friends.sort(compareName);
|
||||||
|
|
||||||
return { ...state, friends };
|
return { ...state, friends };
|
||||||
}
|
}
|
||||||
case FriendListActions.PROCESS_UPDATE: {
|
case FriendListActions.PROCESS_UPDATE: {
|
||||||
@ -80,22 +94,9 @@ export const FriendListReducer: Reducer<IFriendListState, IFriendListAction> = (
|
|||||||
const processUpdate = (friend: FriendParser) =>
|
const processUpdate = (friend: FriendParser) =>
|
||||||
{
|
{
|
||||||
const index = friends.findIndex(existingFriend => (existingFriend.id === friend.id));
|
const index = friends.findIndex(existingFriend => (existingFriend.id === friend.id));
|
||||||
const newFriend = new MessengerFriend();
|
|
||||||
|
|
||||||
newFriend.id = friend.id;
|
const newFriend = new MessengerFriend();
|
||||||
newFriend.name = friend.name;
|
newFriend.populate(friend);
|
||||||
newFriend.gender = friend.gender;
|
|
||||||
newFriend.online = friend.online;
|
|
||||||
newFriend.followingAllowed = friend.followingAllowed;
|
|
||||||
newFriend.figure = friend.figure;
|
|
||||||
newFriend.categoryId = friend.categoryId;
|
|
||||||
newFriend.motto = friend.motto;
|
|
||||||
newFriend.realName = friend.realName;
|
|
||||||
newFriend.lastAccess = friend.lastAccess;
|
|
||||||
newFriend.persistedMessageUser = friend.persistedMessageUser;
|
|
||||||
newFriend.vipMember = friend.vipMember;
|
|
||||||
newFriend.pocketHabboUser = friend.pocketHabboUser;
|
|
||||||
newFriend.relationshipStatus = friend.relationshipStatus;
|
|
||||||
|
|
||||||
if(index > -1) friends[index] = newFriend;
|
if(index > -1) friends[index] = newFriend;
|
||||||
else friends.unshift(newFriend);
|
else friends.unshift(newFriend);
|
||||||
@ -112,9 +113,26 @@ export const FriendListReducer: Reducer<IFriendListState, IFriendListAction> = (
|
|||||||
if(index > -1) friends.splice(index);
|
if(index > -1) friends.splice(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friends.sort(compareName);
|
||||||
|
|
||||||
return { ...state, friends };
|
return { ...state, friends };
|
||||||
}
|
}
|
||||||
|
case FriendListActions.PROCESS_REQUESTS: {
|
||||||
|
const newRequests = (action.payload.requests || null);
|
||||||
|
let requests = [ ...state.requests ];
|
||||||
|
|
||||||
|
for(const request of newRequests)
|
||||||
|
{
|
||||||
|
const newRequest = new MessengerRequest();
|
||||||
|
newRequest.populate(request);
|
||||||
|
requests.push(newRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
requests.sort(compareName);
|
||||||
|
|
||||||
|
return { ...state, requests };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,63 @@
|
|||||||
import { FC } from 'react';
|
import { FollowFriendComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
|
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView';
|
||||||
|
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||||
import { FriendListFriendsItemViewProps } from './FriendListFriendsItemView.types';
|
import { FriendListFriendsItemViewProps } from './FriendListFriendsItemView.types';
|
||||||
|
|
||||||
export const FriendListFriendsItemView: FC<FriendListFriendsItemViewProps> = props =>
|
export const FriendListFriendsItemView: FC<FriendListFriendsItemViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { friend = null } = props;
|
const { friend = null } = props;
|
||||||
|
|
||||||
|
const [ isExpanded, setIsExpanded ] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const followFriend = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!friend) return;
|
||||||
|
|
||||||
|
SendMessageHook(new FollowFriendComposer(friend.id));
|
||||||
|
}, [ friend ]);
|
||||||
|
|
||||||
|
const getCurrentRelationshipName = useCallback(() =>
|
||||||
|
{
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
}, [ friend ]);
|
||||||
|
|
||||||
|
const updateRelationship = useCallback((type: number) =>
|
||||||
|
{
|
||||||
|
if(type !== friend.relationshipStatus) SendMessageHook(new SetRelationshipStatusComposer(friend.id, type));
|
||||||
|
|
||||||
|
setIsExpanded(false);
|
||||||
|
}, [ friend ]);
|
||||||
|
|
||||||
|
if(!friend) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-flex">
|
<div className="px-2 py-1 d-flex gap-1 align-items-center">
|
||||||
<div className="text-black">{ friend.name }</div>
|
<UserProfileIconView userId={ friend.id } />
|
||||||
|
<div>{ friend.name }</div>
|
||||||
|
<div className="ms-auto d-flex align-items-center gap-1">
|
||||||
|
{ !isExpanded && <>
|
||||||
|
{ friend.followingAllowed && <i onClick={ followFriend } className="icon icon-friendlist-follow cursor-pointer" title={ LocalizeText('friendlist.tip.follow') } /> }
|
||||||
|
{ friend.online && <i className="icon icon-friendlist-chat cursor-pointer" title={ LocalizeText('friendlist.tip.im') } /> }
|
||||||
|
<i className={ 'icon cursor-pointer icon-relationship-' + getCurrentRelationshipName() } onClick={ () => setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } />
|
||||||
|
</> }
|
||||||
|
{ isExpanded && <>
|
||||||
|
<i className="icon icon-relationship-heart cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } />
|
||||||
|
<i className="icon icon-relationship-smile cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } />
|
||||||
|
<i className="icon icon-relationship-bobba cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } />
|
||||||
|
<i className="icon icon-relationship-none cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } />
|
||||||
|
</> }
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,17 @@
|
|||||||
import { FC, useMemo } from 'react';
|
import { FC } from 'react';
|
||||||
import { useFriendListContext } from '../../context/FriendListContext';
|
|
||||||
import { FriendListFriendsItemView } from '../friends-item/FriendListFriendsItemView';
|
import { FriendListFriendsItemView } from '../friends-item/FriendListFriendsItemView';
|
||||||
import { FriendListFriendsViewProps } from './FriendListFriendsView.types';
|
import { FriendListFriendsViewProps } from './FriendListFriendsView.types';
|
||||||
|
|
||||||
export const FriendListFriendsView: FC<FriendListFriendsViewProps> = props =>
|
export const FriendListFriendsView: FC<FriendListFriendsViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { online = true } = props;
|
const { list = null } = props;
|
||||||
const { friendListState = null } = useFriendListContext();
|
|
||||||
const { friends = null } = friendListState;
|
|
||||||
|
|
||||||
const getFriendElements = useMemo(() =>
|
if(!list) return null;
|
||||||
{
|
|
||||||
if(!friends || !friends.length) return null;
|
|
||||||
|
|
||||||
const elements: JSX.Element[] = [];
|
return (<>
|
||||||
|
{ list.map((friend, index) =>
|
||||||
for(const friend of friends)
|
|
||||||
{
|
{
|
||||||
if(!friend || (friend.online !== online)) continue;
|
return <FriendListFriendsItemView key={ index } friend={ friend } />
|
||||||
|
}) }
|
||||||
elements.push(<FriendListFriendsItemView key={ friend.id } friend={ friend } />)
|
</>);
|
||||||
}
|
|
||||||
|
|
||||||
console.log(elements);
|
|
||||||
|
|
||||||
return elements;
|
|
||||||
}, [ friends, online ]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="d-flex flex-column">
|
|
||||||
{ getFriendElements }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
|
import { MessengerFriend } from './../../common/MessengerFriend';
|
||||||
export interface FriendListFriendsViewProps
|
export interface FriendListFriendsViewProps
|
||||||
{
|
{
|
||||||
online?: boolean;
|
list: MessengerFriend[];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import { AcceptFriendComposer, DeclineFriendComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { SendMessageHook } from '../../../../hooks/messages/message-event';
|
||||||
|
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView';
|
||||||
|
import { FriendListRequestsItemViewProps } from './FriendListRequestsItemView.types';
|
||||||
|
|
||||||
|
export const FriendListRequestsItemView: FC<FriendListRequestsItemViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { request = null } = props;
|
||||||
|
|
||||||
|
const accept = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!request) return;
|
||||||
|
|
||||||
|
SendMessageHook(new AcceptFriendComposer(request.id));
|
||||||
|
}, [ request ]);
|
||||||
|
|
||||||
|
const decline = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!request) return;
|
||||||
|
|
||||||
|
SendMessageHook(new DeclineFriendComposer(false, request.id));
|
||||||
|
}, [ request ]);
|
||||||
|
|
||||||
|
if(!request) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="px-2 py-1 d-flex gap-1 align-items-center">
|
||||||
|
<UserProfileIconView userId={ request.id } />
|
||||||
|
<div>{ request.name }</div>
|
||||||
|
<div className="ms-auto d-flex align-items-center gap-1">
|
||||||
|
<i className="icon icon-accept cursor-pointer" onClick={ accept } />
|
||||||
|
<i className="icon icon-deny cursor-pointer" onClick={ decline } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
import { MessengerRequest } from './../../common/MessengerRequest';
|
||||||
|
|
||||||
|
export interface FriendListRequestsItemViewProps
|
||||||
|
{
|
||||||
|
request: MessengerRequest;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { FriendListRequestsItemView } from '../requests-item/FriendListRequestsItemView';
|
||||||
|
import { FriendListRequestsViewProps } from './FriendListRequestsView.types';
|
||||||
|
|
||||||
|
export const FriendListRequestsView: FC<FriendListRequestsViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { list = null } = props;
|
||||||
|
|
||||||
|
if(!list) return null;
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
{ list.map((request, index) =>
|
||||||
|
{
|
||||||
|
return <FriendListRequestsItemView key={ index } request={ request } />
|
||||||
|
}) }
|
||||||
|
</>);
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
import { MessengerRequest } from './../../common/MessengerRequest';
|
||||||
|
|
||||||
|
export interface FriendListRequestsViewProps
|
||||||
|
{
|
||||||
|
list: MessengerRequest[];
|
||||||
|
}
|
@ -177,10 +177,10 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
<i className={ 'icon icon-group-small-' + classNames({ 'owner': member.rank === GroupRank.OWNER, 'admin': member.rank === GroupRank.ADMIN, 'not-admin': member.rank === GroupRank.MEMBER, 'cursor-pointer': pageData.admin }) } title={ LocalizeText(getRankDescription(member)) } onClick={ () => toggleAdmin(member) } />
|
<i className={ 'icon icon-group-small-' + classNames({ 'owner': member.rank === GroupRank.OWNER, 'admin': member.rank === GroupRank.ADMIN, 'not-admin': member.rank === GroupRank.MEMBER, 'cursor-pointer': pageData.admin }) } title={ LocalizeText(getRankDescription(member)) } onClick={ () => toggleAdmin(member) } />
|
||||||
</div>
|
</div>
|
||||||
{ member.rank === GroupRank.REQUESTED && <div className="d-flex align-items-center">
|
{ member.rank === GroupRank.REQUESTED && <div className="d-flex align-items-center">
|
||||||
<i className="icon cursor-pointer icon-group-accept-member" title={ LocalizeText('group.members.accept') } onClick={ () => acceptMembership(member) } />
|
<i className="icon cursor-pointer icon-accept" title={ LocalizeText('group.members.accept') } onClick={ () => acceptMembership(member) } />
|
||||||
</div> }
|
</div> }
|
||||||
{ member.rank !== GroupRank.OWNER && pageData.admin && member.id !== GetSessionDataManager().userId &&<div className="d-flex align-items-center mt-1">
|
{ member.rank !== GroupRank.OWNER && pageData.admin && member.id !== GetSessionDataManager().userId &&<div className="d-flex align-items-center mt-1">
|
||||||
<i className="icon cursor-pointer icon-group-remove-member" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) } />
|
<i className="icon cursor-pointer icon-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) } />
|
||||||
</div> }
|
</div> }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|