GroupMembers updates

This commit is contained in:
MyNameIsBatman 2021-09-06 02:53:24 -03:00
parent 8b6f08b989
commit 3cd6d56b3e
14 changed files with 117 additions and 38 deletions

View File

@ -1,6 +1,7 @@
import { CreateLinkEvent } from '..';
export function GetGroupMembers(groupId: number): void
export function GetGroupMembers(groupId: number, levelId?: number): void
{
CreateLinkEvent(`groups/members/${groupId}`);
if(!levelId) CreateLinkEvent(`groups/members/${groupId}`);
else CreateLinkEvent(`groups/members/${groupId}/${levelId}`);
}

View File

@ -2,4 +2,5 @@ export * from './core';
export * from './groups';
export * from './navigator';
export * from './nitro';
export * from './user';
export * from './utils';

View File

@ -0,0 +1,7 @@
import { UserProfileComposer } from '@nitrots/nitro-renderer';
import { SendMessageHook } from '../../hooks';
export function GetUserProfile(userId: number): void
{
SendMessageHook(new UserProfileComposer(userId));
}

1
src/api/user/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './GetUserProfile';

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 173 B

View File

@ -621,6 +621,12 @@
height: 14px;
}
&.icon-group-accept-member {
background: url('../images/groups/icons/group_icon_accept_member.png');
width: 13px;
height: 14px;
}
&.icon-group-small-owner {
background: url('../images/groups/icons/group_icon_small_owner.png');
width: 13px;

View File

@ -114,8 +114,8 @@ export const GroupsMessageHandler: FC<{}> = props =>
parser.badgeParts.forEach((part, id) =>
{
groupBadgeParts.push(new GroupBadgePart(
part.isBase ? GroupBadgePart.BASE : part.key >= 100 ? GroupBadgePart.SYMBOL_ALT : GroupBadgePart.SYMBOL,
part.key >= 100 ? part.key - 100 : part.key,
part.isBase ? GroupBadgePart.BASE : GroupBadgePart.SYMBOL,
part.key,
part.color,
part.position
));

View File

@ -2,7 +2,6 @@ export class GroupBadgePart
{
public static BASE: string = 'b';
public static SYMBOL: string = 's';
public static SYMBOL_ALT: string = 't';
public type: string;
public key: number;
@ -26,6 +25,6 @@ export class GroupBadgePart
public static getCode(type: string, key: number, color: number, position: number): string
{
return (type === GroupBadgePart.BASE ? type : key >= 100 ? GroupBadgePart.SYMBOL_ALT : GroupBadgePart.SYMBOL) + (key < 10 ? '0' : '') + (type === GroupBadgePart.BASE ? key : key >= 100 ? key - 100 : key) + (color < 10 ? '0' : '') + color + position;
return type + (key < 10 ? '0' : '') + key + (color < 10 ? '0' : '') + color + position;
}
}

View File

@ -24,8 +24,11 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
const leaveGroup = useCallback(() =>
{
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
if(onClose) onClose();
if(window.confirm(LocalizeText('group.leaveconfirm.desc')))
{
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
if(onClose) onClose();
}
}, [ groupInformation, onClose ]);
const isRealOwner = useCallback(() =>
@ -80,6 +83,9 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
case 'members':
GetGroupMembers(groupInformation.id);
break;
case 'members_pending':
GetGroupMembers(groupInformation.id, 2);
break;
case 'manage':
GetGroupManager(groupInformation.id);
break;
@ -104,8 +110,8 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
<div className="mt-3 cursor-pointer" onClick={ () => handleAction('members') }>
{ LocalizeText('group.membercount', ['totalMembers'], [groupInformation.membersCount.toString()]) }
</div>
{ groupInformation.pendingRequestsCount > 0 && <div className="cursor-pointer">
{ LocalizeText('group.pendingmembercount', ['totalMembers'], [groupInformation.pendingRequestsCount.toString()]) }
{ groupInformation.pendingRequestsCount > 0 && <div className="cursor-pointer" onClick={ () => handleAction('members_pending') }>
{ LocalizeText('group.pendingmembercount', ['amount'], [groupInformation.pendingRequestsCount.toString()]) }
</div> }
{ groupInformation.isOwner && <div className="cursor-pointer" onClick={ () => handleAction('manage') }>
{ LocalizeText('group.manage') }

View File

@ -16,12 +16,12 @@
position: relative;
overflow: hidden;
width: 40px;
height: 40px;
height: 50px;
.avatar-image {
position: absolute;
left: -25px;
top: -25px;
top: -20px;
}
}
}

View File

@ -1,7 +1,7 @@
import { GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembersParser, GroupRank } from '@nitrots/nitro-renderer';
import { GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames';
import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { GetSessionDataManager, LocalizeText } from '../../../../api';
import { GetSessionDataManager, GetUserProfile, LocalizeText } from '../../../../api';
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
@ -16,23 +16,40 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
const [ searchQuery, setSearchQuery ] = useState<string>('');
const [ searchLevelId, setSearchLevelId ] = useState<number>(3);
const [ totalPages, setTotalPages ] = useState<number>(0);
const [ removingMemberName, setRemovingMemberName ] = useState<string>(null);
const searchMembers = useCallback((pageId: number) =>
const searchMembers = useCallback((pageId: number, newLevelId?: number) =>
{
if(!groupId) return;
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, searchLevelId));
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, newLevelId !== null ? newLevelId : searchLevelId));
}, [ groupId, searchQuery, searchLevelId ]);
const onGroupMembersEvent = useCallback((event: GroupMembersEvent) =>
{
const parser = event.getParser();
setPageData(null);
setPageData(parser);
setSearchLevelId(parser.level);
setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize));
}, []);
const onGroupConfirmMemberRemoveEvent = useCallback((event: GroupConfirmMemberRemoveEvent) =>
{
const parser = event.getParser();
if(window.confirm(LocalizeText(parser.furnitureCount > 0 ? 'group.kickconfirm.desc' : 'group.kickconfirm_nofurni.desc', ['user', 'amount'], [removingMemberName, parser.furnitureCount.toString()])))
{
SendMessageHook(new GroupRemoveMemberComposer(pageData.groupId, parser.userId));
searchMembers(pageData.pageIndex);
}
setRemovingMemberName(null);
}, [ pageData, removingMemberName, searchMembers ]);
CreateMessageHook(GroupMembersEvent, onGroupMembersEvent);
CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent);
useEffect(() =>
{
@ -43,13 +60,15 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
if(!groupId) return;
if(levelId !== null) setSearchLevelId(levelId);
searchMembers(0);
searchMembers(0, levelId);
}, [ groupId, levelId ]);
useEffect(() =>
const selectSearchLevelId = useCallback((level: number) =>
{
searchMembers(0);
}, [ searchLevelId ]);
setSearchLevelId(level);
searchMembers(0, level);
}, [ searchMembers ]);
const previousPage = useCallback(() =>
{
@ -82,6 +101,45 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
return '';
}, [ pageData ]);
const toggleAdmin = useCallback((member: GroupMemberParser) =>
{
if(member.rank === GroupRank.OWNER) return;
if(member.rank !== GroupRank.ADMIN)
{
SendMessageHook(new GroupAdminGiveComposer(pageData.groupId, member.id));
}
else
{
SendMessageHook(new GroupAdminTakeComposer(pageData.groupId, member.id));
}
searchMembers(pageData.pageIndex);
}, [ pageData ]);
const acceptMembership = useCallback((member) =>
{
if(member.rank === GroupRank.REQUESTED)
{
SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id));
searchMembers(pageData.pageIndex);
}
}, [ pageData ]);
const removeMemberOrDeclineMembership = useCallback((member) =>
{
if(member.rank === GroupRank.REQUESTED)
{
SendMessageHook(new GroupMembershipDeclineComposer(pageData.groupId, member.id));
searchMembers(pageData.pageIndex);
}
else
{
setRemovingMemberName(member.name);
SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id));
}
}, [ pageData ]);
if(!pageData) return null;
return (
@ -94,7 +152,7 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
</div>
<div className="w-100">
<input type="text" className="form-control form-control-sm w-100 mb-1" placeholder={ LocalizeText('group.members.searchinfo') } value={ searchQuery } onChange={ (e) => setSearchQuery(e.target.value) } onBlur={ () => searchMembers(pageData.pageIndex) } onKeyDown={ onKeyDown } />
<select className="form-select form-select-sm w-100" value={ searchLevelId } onChange={ (e) => setSearchLevelId(Number(e.target.value)) }>
<select className="form-select form-select-sm w-100" value={ searchLevelId } onChange={ (e) => selectSearchLevelId(Number(e.target.value)) }>
<option value="0">{ LocalizeText('group.members.search.all') }</option>
<option value="1">{ LocalizeText('group.members.search.admins') }</option>
<option value="2">{ LocalizeText('group.members.search.pending') }</option>
@ -107,19 +165,22 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
return (
<div key={ index } className={ 'col pb-1' + classNames({ ' pe-1': index % 2 === 0 }) }>
<div className="list-member bg-white rounded d-flex text-black">
<div className="avatar-head flex-shrink-0">
<div className="avatar-head flex-shrink-0 cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>
<AvatarImageView figure={ member.figure } headOnly={ true } direction={ 2 } />
</div>
<div className="p-1 w-100">
<div className="fw-bold small">{ member.name }</div>
<div className="text-muted fst-italic small">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</div>
<div className="p-1 w-100 d-flex flex-column justify-content-center">
<div className="fw-bold small cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>{ member.name }</div>
{ member.rank !== GroupRank.REQUESTED && <div className="text-muted fst-italic small">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</div> }
</div>
<div className="d-flex flex-column pe-2 align-items-center justify-content-center">
<div className="d-flex align-items-center">
<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)) } />
<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>
{ 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) } />
</div> }
{ 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') } />
<i className="icon cursor-pointer icon-group-remove-member" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) } />
</div> }
</div>
</div>

View File

@ -65,8 +65,11 @@ export const GroupRoomInformationView: FC<{}> = props =>
const tryLeaveGroup = useCallback(() =>
{
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
SendMessageHook(new GroupInformationComposer(groupInformation.id, false));
if(window.confirm(LocalizeText('group.leaveconfirm.desc')))
{
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
SendMessageHook(new GroupInformationComposer(groupInformation.id, false));
}
}, [ groupInformation ]);
const getButtonText = useCallback(() =>

View File

@ -1,18 +1,12 @@
import { UserProfileComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { SendMessageHook } from '../../../hooks';
import { FC } from 'react';
import { GetUserProfile } from '../../../api';
import { UserProfileIconViewProps } from './UserProfileIconView.types';
export const UserProfileIconView: FC<UserProfileIconViewProps> = props =>
{
const { userId = 0, userName = null } = props;
const visitProfile = useCallback(() =>
{
if(userId) SendMessageHook(new UserProfileComposer(userId));
}, [ userId ]);
return (<>
<i className="icon icon-user-profile me-1 cursor-pointer" onClick={ visitProfile } />
<i className="icon icon-user-profile me-1 cursor-pointer" onClick={ () => GetUserProfile(userId) } />
</>);
}