mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Groups Members update
This commit is contained in:
parent
447fc2afff
commit
ce38e9045c
6
src/api/groups/GetGroupMembers.ts
Normal file
6
src/api/groups/GetGroupMembers.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { CreateLinkEvent } from '..';
|
||||||
|
|
||||||
|
export function GetGroupMembers(groupId: number): void
|
||||||
|
{
|
||||||
|
CreateLinkEvent(`groups/members/${groupId}`);
|
||||||
|
}
|
BIN
src/assets/images/groups/icons/group_icon_admin.png
Normal file
BIN
src/assets/images/groups/icons/group_icon_admin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 203 B |
BIN
src/assets/images/groups/icons/group_icon_not_admin.png
Normal file
BIN
src/assets/images/groups/icons/group_icon_not_admin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 198 B |
BIN
src/assets/images/groups/icons/group_icon_remove_member.png
Normal file
BIN
src/assets/images/groups/icons/group_icon_remove_member.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 B |
BIN
src/assets/images/groups/icons/group_icon_small_owner.png
Normal file
BIN
src/assets/images/groups/icons/group_icon_small_owner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 215 B |
@ -603,6 +603,30 @@
|
|||||||
height: 14px;
|
height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-group-small-admin {
|
||||||
|
background: url('../images/groups/icons/group_icon_admin.png');
|
||||||
|
width: 11px;
|
||||||
|
height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-group-small-not-admin {
|
||||||
|
background: url('../images/groups/icons/group_icon_not_admin.png');
|
||||||
|
width: 11px;
|
||||||
|
height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-group-remove-member {
|
||||||
|
background: url('../images/groups/icons/group_icon_remove_member.png');
|
||||||
|
width: 13px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-group-small-owner {
|
||||||
|
background: url('../images/groups/icons/group_icon_small_owner.png');
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-navigator-info {
|
&.icon-navigator-info {
|
||||||
background: url('../images/navigator/icons/info.png');
|
background: url('../images/navigator/icons/info.png');
|
||||||
width: 18px;
|
width: 18px;
|
||||||
|
@ -49,5 +49,6 @@
|
|||||||
@import './views/information/GroupInformationView';
|
@import './views/information/GroupInformationView';
|
||||||
@import './views/information-standalone/GroupInformationStandaloneView';
|
@import './views/information-standalone/GroupInformationStandaloneView';
|
||||||
@import './views/manager/GroupManagerView';
|
@import './views/manager/GroupManagerView';
|
||||||
|
@import './views/members/GroupMembersView';
|
||||||
@import './views/room-information/GroupRoomInformationView';
|
@import './views/room-information/GroupRoomInformationView';
|
||||||
@import './views/shared-tabs/GroupSharedTabs';
|
@import './views/shared-tabs/GroupSharedTabs';
|
||||||
|
@ -8,12 +8,15 @@ import { GroupsMessageHandler } from './GroupsMessageHandler';
|
|||||||
import { GroupCreatorView } from './views/creator/GroupCreatorView';
|
import { GroupCreatorView } from './views/creator/GroupCreatorView';
|
||||||
import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView';
|
import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView';
|
||||||
import { GroupManagerView } from './views/manager/GroupManagerView';
|
import { GroupManagerView } from './views/manager/GroupManagerView';
|
||||||
|
import { GroupMembersView } from './views/members/GroupMembersView';
|
||||||
|
|
||||||
export const GroupsView: FC<{}> = props =>
|
export const GroupsView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups);
|
const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups);
|
||||||
|
|
||||||
const [ isCreatorVisible, setIsCreatorVisible ] = useState<boolean>(false);
|
const [ isCreatorVisible, setIsCreatorVisible ] = useState<boolean>(false);
|
||||||
|
const [ groupMembersId, setGroupMembersId ] = useState<number>(null);
|
||||||
|
const [ groupMembersLevel, setGroupMembersLevel ] = useState<number>(null);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@ -36,6 +39,13 @@ export const GroupsView: FC<{}> = props =>
|
|||||||
|
|
||||||
SendMessageHook(new GroupSettingsComposer(Number(parts[2])));
|
SendMessageHook(new GroupSettingsComposer(Number(parts[2])));
|
||||||
return;
|
return;
|
||||||
|
case 'members':
|
||||||
|
if(!parts[2]) return;
|
||||||
|
|
||||||
|
setGroupMembersId(Number(parts[2]));
|
||||||
|
|
||||||
|
if(parts[3]) setGroupMembersLevel(Number(parts[3]));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -61,12 +71,19 @@ export const GroupsView: FC<{}> = props =>
|
|||||||
|
|
||||||
CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent);
|
CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent);
|
||||||
|
|
||||||
|
const closeMembers = useCallback(() =>
|
||||||
|
{
|
||||||
|
setGroupMembersId(null);
|
||||||
|
setGroupMembersLevel(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GroupsContextProvider value={ { groupsState, dispatchGroupsState } }>
|
<GroupsContextProvider value={ { groupsState, dispatchGroupsState } }>
|
||||||
<GroupsMessageHandler />
|
<GroupsMessageHandler />
|
||||||
<div className="nitro-groups">
|
<div className="nitro-groups">
|
||||||
<GroupCreatorView isVisible={ isCreatorVisible } onClose={ () => setIsCreatorVisible(false) } />
|
<GroupCreatorView isVisible={ isCreatorVisible } onClose={ () => setIsCreatorVisible(false) } />
|
||||||
<GroupManagerView />
|
<GroupManagerView />
|
||||||
|
<GroupMembersView groupId={ groupMembersId } levelId={ groupMembersLevel } onClose={ closeMembers } />
|
||||||
<GroupInformationStandaloneView />
|
<GroupInformationStandaloneView />
|
||||||
</div>
|
</div>
|
||||||
</GroupsContextProvider>
|
</GroupsContextProvider>
|
||||||
|
@ -2,6 +2,7 @@ import { GroupRemoveMemberComposer } from '@nitrots/nitro-renderer';
|
|||||||
import { FC, useCallback } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../../api';
|
import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../../api';
|
||||||
import { GetGroupManager } from '../../../../api/groups/GetGroupManager';
|
import { GetGroupManager } from '../../../../api/groups/GetGroupManager';
|
||||||
|
import { GetGroupMembers } from '../../../../api/groups/GetGroupMembers';
|
||||||
import { TryJoinGroup } from '../../../../api/groups/TryJoinGroup';
|
import { TryJoinGroup } from '../../../../api/groups/TryJoinGroup';
|
||||||
import { SendMessageHook } from '../../../../hooks';
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
import { CatalogPageName } from '../../../catalog/common/CatalogPageName';
|
import { CatalogPageName } from '../../../catalog/common/CatalogPageName';
|
||||||
@ -76,6 +77,12 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
{
|
{
|
||||||
switch(action)
|
switch(action)
|
||||||
{
|
{
|
||||||
|
case 'members':
|
||||||
|
GetGroupMembers(groupInformation.id);
|
||||||
|
break;
|
||||||
|
case 'manage':
|
||||||
|
GetGroupManager(groupInformation.id);
|
||||||
|
break;
|
||||||
case 'homeroom':
|
case 'homeroom':
|
||||||
TryVisitRoom(groupInformation.roomId);
|
TryVisitRoom(groupInformation.roomId);
|
||||||
break;
|
break;
|
||||||
@ -94,13 +101,13 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
<div className="group-badge">
|
<div className="group-badge">
|
||||||
<BadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } />
|
<BadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } />
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 cursor-pointer">
|
<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">
|
||||||
{ LocalizeText('group.pendingmembercount', ['totalMembers'], [groupInformation.pendingRequestsCount.toString()]) }
|
{ LocalizeText('group.pendingmembercount', ['totalMembers'], [groupInformation.pendingRequestsCount.toString()]) }
|
||||||
</div> }
|
</div> }
|
||||||
{ groupInformation.isOwner && <div className="cursor-pointer" onClick={ () => GetGroupManager(groupInformation.id) }>
|
{ groupInformation.isOwner && <div className="cursor-pointer" onClick={ () => handleAction('manage') }>
|
||||||
{ LocalizeText('group.manage') }
|
{ LocalizeText('group.manage') }
|
||||||
</div> }
|
</div> }
|
||||||
<div className="mt-auto mb-1">
|
<div className="mt-auto mb-1">
|
||||||
|
29
src/views/groups/views/members/GroupMembersView.scss
Normal file
29
src/views/groups/views/members/GroupMembersView.scss
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
.nitro-group-members {
|
||||||
|
width: 400px;
|
||||||
|
|
||||||
|
.group-badge {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.members-list {
|
||||||
|
height: 300px;
|
||||||
|
|
||||||
|
.list-member {
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
|
||||||
|
.avatar-head {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
.avatar-image {
|
||||||
|
position: absolute;
|
||||||
|
left: -25px;
|
||||||
|
top: -25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
src/views/groups/views/members/GroupMembersView.tsx
Normal file
142
src/views/groups/views/members/GroupMembersView.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembersParser, GroupRank } from '@nitrots/nitro-renderer';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||||
|
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
|
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||||
|
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
|
||||||
|
import { GroupMembersViewProps } from './GroupMembersView.types';
|
||||||
|
|
||||||
|
export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { groupId = null, levelId = null, onClose = null } = props;
|
||||||
|
|
||||||
|
const [ pageData, setPageData ] = useState<GroupMembersParser>(null);
|
||||||
|
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
||||||
|
const [ searchLevelId, setSearchLevelId ] = useState<number>(3);
|
||||||
|
const [ totalPages, setTotalPages ] = useState<number>(0);
|
||||||
|
|
||||||
|
const searchMembers = useCallback((pageId: number) =>
|
||||||
|
{
|
||||||
|
if(!groupId) return;
|
||||||
|
|
||||||
|
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, searchLevelId));
|
||||||
|
}, [ groupId, searchQuery, searchLevelId ]);
|
||||||
|
|
||||||
|
const onGroupMembersEvent = useCallback((event: GroupMembersEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
setPageData(parser);
|
||||||
|
setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(GroupMembersEvent, onGroupMembersEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setPageData(null);
|
||||||
|
setSearchQuery('');
|
||||||
|
setSearchLevelId(0);
|
||||||
|
|
||||||
|
if(!groupId) return;
|
||||||
|
|
||||||
|
if(levelId !== null) setSearchLevelId(levelId);
|
||||||
|
searchMembers(0);
|
||||||
|
}, [ groupId, levelId ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
searchMembers(0);
|
||||||
|
}, [ searchLevelId ]);
|
||||||
|
|
||||||
|
const previousPage = useCallback(() =>
|
||||||
|
{
|
||||||
|
searchMembers(pageData.pageIndex - 1);
|
||||||
|
}, [ searchMembers, pageData ]);
|
||||||
|
|
||||||
|
const nextPage = useCallback(() =>
|
||||||
|
{
|
||||||
|
searchMembers(pageData.pageIndex + 1);
|
||||||
|
}, [ searchMembers, pageData ]);
|
||||||
|
|
||||||
|
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
|
||||||
|
{
|
||||||
|
if(event.key !== 'Enter') return;
|
||||||
|
|
||||||
|
searchMembers(pageData.pageIndex);
|
||||||
|
}, [ searchMembers, pageData ]);
|
||||||
|
|
||||||
|
const getRankDescription = useCallback((member: GroupMemberParser) =>
|
||||||
|
{
|
||||||
|
if(member.rank === GroupRank.OWNER) return 'group.members.owner';
|
||||||
|
|
||||||
|
if(pageData.admin)
|
||||||
|
{
|
||||||
|
if(member.rank === GroupRank.ADMIN) return 'group.members.removerights';
|
||||||
|
|
||||||
|
if(member.rank === GroupRank.MEMBER) return 'group.members.giverights';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}, [ pageData ]);
|
||||||
|
|
||||||
|
if(!pageData) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView className="nitro-group-members" simple={ true }>
|
||||||
|
<NitroCardHeaderView headerText={ LocalizeText('group.members.title', ['groupName'], [ pageData.groupTitle ]) } onCloseClick={ onClose } />
|
||||||
|
<NitroCardContentView className="pb-2">
|
||||||
|
<div className="d-flex gap-2 align-items-center mb-2">
|
||||||
|
<div className="group-badge">
|
||||||
|
<BadgeImageView badgeCode={ pageData.badge } isGroup={ true } />
|
||||||
|
</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)) }>
|
||||||
|
<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>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row row-cols-2 align-content-start g-0 w-100 members-list overflow-auto">
|
||||||
|
{ pageData.result.map((member, index) =>
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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)) } />
|
||||||
|
</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') } />
|
||||||
|
</div> }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
<div className="d-flex w-100 align-items-center">
|
||||||
|
<div>
|
||||||
|
<button disabled={ pageData.pageIndex === 0 } className="btn btn-primary" onClick={ previousPage }><i className="fas fa-chevron-left" /></button>
|
||||||
|
</div>
|
||||||
|
<div className="text-center text-black w-100">{ LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) }</div>
|
||||||
|
<div>
|
||||||
|
<button disabled={ pageData.pageIndex === totalPages - 1 } className="btn btn-primary" onClick={ nextPage }><i className="fas fa-chevron-right" /></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
);
|
||||||
|
};
|
6
src/views/groups/views/members/GroupMembersView.types.ts
Normal file
6
src/views/groups/views/members/GroupMembersView.types.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface GroupMembersViewProps
|
||||||
|
{
|
||||||
|
groupId: number;
|
||||||
|
levelId: number;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user