mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-02-17 01:12:37 +01:00
GroupMembers updates
This commit is contained in:
parent
8b6f08b989
commit
3cd6d56b3e
@ -1,6 +1,7 @@
|
|||||||
import { CreateLinkEvent } from '..';
|
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}`);
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,5 @@ export * from './core';
|
|||||||
export * from './groups';
|
export * from './groups';
|
||||||
export * from './navigator';
|
export * from './navigator';
|
||||||
export * from './nitro';
|
export * from './nitro';
|
||||||
|
export * from './user';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
7
src/api/user/GetUserProfile.ts
Normal file
7
src/api/user/GetUserProfile.ts
Normal 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
1
src/api/user/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './GetUserProfile';
|
BIN
src/assets/images/groups/icons/group_icon_accept_member.png
Normal file
BIN
src/assets/images/groups/icons/group_icon_accept_member.png
Normal file
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 |
@ -621,6 +621,12 @@
|
|||||||
height: 14px;
|
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;
|
||||||
|
@ -114,8 +114,8 @@ export const GroupsMessageHandler: FC<{}> = props =>
|
|||||||
parser.badgeParts.forEach((part, id) =>
|
parser.badgeParts.forEach((part, id) =>
|
||||||
{
|
{
|
||||||
groupBadgeParts.push(new GroupBadgePart(
|
groupBadgeParts.push(new GroupBadgePart(
|
||||||
part.isBase ? GroupBadgePart.BASE : part.key >= 100 ? GroupBadgePart.SYMBOL_ALT : GroupBadgePart.SYMBOL,
|
part.isBase ? GroupBadgePart.BASE : GroupBadgePart.SYMBOL,
|
||||||
part.key >= 100 ? part.key - 100 : part.key,
|
part.key,
|
||||||
part.color,
|
part.color,
|
||||||
part.position
|
part.position
|
||||||
));
|
));
|
||||||
|
@ -2,7 +2,6 @@ export class GroupBadgePart
|
|||||||
{
|
{
|
||||||
public static BASE: string = 'b';
|
public static BASE: string = 'b';
|
||||||
public static SYMBOL: string = 's';
|
public static SYMBOL: string = 's';
|
||||||
public static SYMBOL_ALT: string = 't';
|
|
||||||
|
|
||||||
public type: string;
|
public type: string;
|
||||||
public key: number;
|
public key: number;
|
||||||
@ -26,6 +25,6 @@ export class GroupBadgePart
|
|||||||
|
|
||||||
public static getCode(type: string, key: number, color: number, position: number): string
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,11 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
|
|
||||||
const leaveGroup = useCallback(() =>
|
const leaveGroup = useCallback(() =>
|
||||||
{
|
{
|
||||||
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
|
if(window.confirm(LocalizeText('group.leaveconfirm.desc')))
|
||||||
if(onClose) onClose();
|
{
|
||||||
|
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
|
||||||
|
if(onClose) onClose();
|
||||||
|
}
|
||||||
}, [ groupInformation, onClose ]);
|
}, [ groupInformation, onClose ]);
|
||||||
|
|
||||||
const isRealOwner = useCallback(() =>
|
const isRealOwner = useCallback(() =>
|
||||||
@ -80,6 +83,9 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
case 'members':
|
case 'members':
|
||||||
GetGroupMembers(groupInformation.id);
|
GetGroupMembers(groupInformation.id);
|
||||||
break;
|
break;
|
||||||
|
case 'members_pending':
|
||||||
|
GetGroupMembers(groupInformation.id, 2);
|
||||||
|
break;
|
||||||
case 'manage':
|
case 'manage':
|
||||||
GetGroupManager(groupInformation.id);
|
GetGroupManager(groupInformation.id);
|
||||||
break;
|
break;
|
||||||
@ -104,8 +110,8 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
<div className="mt-3 cursor-pointer" onClick={ () => handleAction('members') }>
|
<div className="mt-3 cursor-pointer" onClick={ () => handleAction('members') }>
|
||||||
{ LocalizeText('group.membercount', ['totalMembers'], [groupInformation.membersCount.toString()]) }
|
{ LocalizeText('group.membercount', ['totalMembers'], [groupInformation.membersCount.toString()]) }
|
||||||
</div>
|
</div>
|
||||||
{ groupInformation.pendingRequestsCount > 0 && <div className="cursor-pointer">
|
{ groupInformation.pendingRequestsCount > 0 && <div className="cursor-pointer" onClick={ () => handleAction('members_pending') }>
|
||||||
{ LocalizeText('group.pendingmembercount', ['totalMembers'], [groupInformation.pendingRequestsCount.toString()]) }
|
{ LocalizeText('group.pendingmembercount', ['amount'], [groupInformation.pendingRequestsCount.toString()]) }
|
||||||
</div> }
|
</div> }
|
||||||
{ groupInformation.isOwner && <div className="cursor-pointer" onClick={ () => handleAction('manage') }>
|
{ groupInformation.isOwner && <div className="cursor-pointer" onClick={ () => handleAction('manage') }>
|
||||||
{ LocalizeText('group.manage') }
|
{ LocalizeText('group.manage') }
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 50px;
|
||||||
|
|
||||||
.avatar-image {
|
.avatar-image {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -25px;
|
left: -25px;
|
||||||
top: -25px;
|
top: -20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 classNames from 'classnames';
|
||||||
import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react';
|
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 { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||||
@ -16,23 +16,40 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
||||||
const [ searchLevelId, setSearchLevelId ] = useState<number>(3);
|
const [ searchLevelId, setSearchLevelId ] = useState<number>(3);
|
||||||
const [ totalPages, setTotalPages ] = useState<number>(0);
|
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;
|
if(!groupId) return;
|
||||||
|
|
||||||
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, searchLevelId));
|
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, newLevelId !== null ? newLevelId : searchLevelId));
|
||||||
}, [ groupId, searchQuery, searchLevelId ]);
|
}, [ groupId, searchQuery, searchLevelId ]);
|
||||||
|
|
||||||
const onGroupMembersEvent = useCallback((event: GroupMembersEvent) =>
|
const onGroupMembersEvent = useCallback((event: GroupMembersEvent) =>
|
||||||
{
|
{
|
||||||
const parser = event.getParser();
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
setPageData(null);
|
||||||
setPageData(parser);
|
setPageData(parser);
|
||||||
|
setSearchLevelId(parser.level);
|
||||||
setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize));
|
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(GroupMembersEvent, onGroupMembersEvent);
|
||||||
|
CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@ -43,13 +60,15 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
if(!groupId) return;
|
if(!groupId) return;
|
||||||
|
|
||||||
if(levelId !== null) setSearchLevelId(levelId);
|
if(levelId !== null) setSearchLevelId(levelId);
|
||||||
searchMembers(0);
|
|
||||||
|
searchMembers(0, levelId);
|
||||||
}, [ groupId, levelId ]);
|
}, [ groupId, levelId ]);
|
||||||
|
|
||||||
useEffect(() =>
|
const selectSearchLevelId = useCallback((level: number) =>
|
||||||
{
|
{
|
||||||
searchMembers(0);
|
setSearchLevelId(level);
|
||||||
}, [ searchLevelId ]);
|
searchMembers(0, level);
|
||||||
|
}, [ searchMembers ]);
|
||||||
|
|
||||||
const previousPage = useCallback(() =>
|
const previousPage = useCallback(() =>
|
||||||
{
|
{
|
||||||
@ -82,6 +101,45 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
return '';
|
return '';
|
||||||
}, [ pageData ]);
|
}, [ 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;
|
if(!pageData) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -94,7 +152,7 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-100">
|
<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 } />
|
<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="0">{ LocalizeText('group.members.search.all') }</option>
|
||||||
<option value="1">{ LocalizeText('group.members.search.admins') }</option>
|
<option value="1">{ LocalizeText('group.members.search.admins') }</option>
|
||||||
<option value="2">{ LocalizeText('group.members.search.pending') }</option>
|
<option value="2">{ LocalizeText('group.members.search.pending') }</option>
|
||||||
@ -107,19 +165,22 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
return (
|
return (
|
||||||
<div key={ index } className={ 'col pb-1' + classNames({ ' pe-1': index % 2 === 0 }) }>
|
<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="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 } />
|
<AvatarImageView figure={ member.figure } headOnly={ true } direction={ 2 } />
|
||||||
</div>
|
</div>
|
||||||
<div className="p-1 w-100">
|
<div className="p-1 w-100 d-flex flex-column justify-content-center">
|
||||||
<div className="fw-bold small">{ member.name }</div>
|
<div className="fw-bold small cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>{ member.name }</div>
|
||||||
<div className="text-muted fst-italic small">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</div>
|
{ member.rank !== GroupRank.REQUESTED && <div className="text-muted fst-italic small">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</div> }
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex flex-column pe-2 align-items-center justify-content-center">
|
<div className="d-flex flex-column pe-2 align-items-center justify-content-center">
|
||||||
<div className="d-flex align-items-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>
|
</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">
|
{ 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,8 +65,11 @@ export const GroupRoomInformationView: FC<{}> = props =>
|
|||||||
|
|
||||||
const tryLeaveGroup = useCallback(() =>
|
const tryLeaveGroup = useCallback(() =>
|
||||||
{
|
{
|
||||||
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
|
if(window.confirm(LocalizeText('group.leaveconfirm.desc')))
|
||||||
SendMessageHook(new GroupInformationComposer(groupInformation.id, false));
|
{
|
||||||
|
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
|
||||||
|
SendMessageHook(new GroupInformationComposer(groupInformation.id, false));
|
||||||
|
}
|
||||||
}, [ groupInformation ]);
|
}, [ groupInformation ]);
|
||||||
|
|
||||||
const getButtonText = useCallback(() =>
|
const getButtonText = useCallback(() =>
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
import { UserProfileComposer } from '@nitrots/nitro-renderer';
|
import { FC } from 'react';
|
||||||
import { FC, useCallback } from 'react';
|
import { GetUserProfile } from '../../../api';
|
||||||
import { SendMessageHook } from '../../../hooks';
|
|
||||||
import { UserProfileIconViewProps } from './UserProfileIconView.types';
|
import { UserProfileIconViewProps } from './UserProfileIconView.types';
|
||||||
|
|
||||||
export const UserProfileIconView: FC<UserProfileIconViewProps> = props =>
|
export const UserProfileIconView: FC<UserProfileIconViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { userId = 0, userName = null } = props;
|
const { userId = 0, userName = null } = props;
|
||||||
|
|
||||||
const visitProfile = useCallback(() =>
|
|
||||||
{
|
|
||||||
if(userId) SendMessageHook(new UserProfileComposer(userId));
|
|
||||||
}, [ userId ]);
|
|
||||||
|
|
||||||
return (<>
|
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) } />
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user