mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Merge branch '@update/groups' into dev
This commit is contained in:
commit
332c9553d5
@ -69,11 +69,17 @@ $camera-checkout-width: 350px;
|
|||||||
|
|
||||||
$room-info-width: 325px;
|
$room-info-width: 325px;
|
||||||
|
|
||||||
|
$nitro-group-creator-width: 383px;
|
||||||
$nitro-mod-tools-width: 175px;
|
$nitro-mod-tools-width: 175px;
|
||||||
|
|
||||||
|
$nitro-group-manager-width: 375px;
|
||||||
|
$nitro-group-manager-height: 355px;
|
||||||
|
|
||||||
$nitro-chooser-width: 200px;
|
$nitro-chooser-width: 200px;
|
||||||
$nitro-chooser-height: 200px;
|
$nitro-chooser-height: 200px;
|
||||||
|
|
||||||
|
$nitro-guide-tool-width: 250px;
|
||||||
|
|
||||||
.nitro-app {
|
.nitro-app {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import { GroupJoinComposer } from '@nitrots/nitro-renderer';
|
import { GroupJoinComposer } from '@nitrots/nitro-renderer';
|
||||||
import { SendMessageHook } from '../../hooks';
|
import { SendMessageHook } from '../../hooks';
|
||||||
|
|
||||||
export function TryJoinGroup(groupId: number): void
|
export const TryJoinGroup = (groupId: number) => SendMessageHook(new GroupJoinComposer(groupId));
|
||||||
{
|
|
||||||
SendMessageHook(new GroupJoinComposer(groupId));
|
|
||||||
}
|
|
||||||
|
38
src/common/HorizontalRule.tsx
Normal file
38
src/common/HorizontalRule.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { CSSProperties, FC, useMemo } from 'react';
|
||||||
|
import { Base, BaseProps } from './Base';
|
||||||
|
import { ColorVariantType } from './types';
|
||||||
|
|
||||||
|
export interface HorizontalRuleProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
variant?: ColorVariantType;
|
||||||
|
height?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HorizontalRule: FC<HorizontalRuleProps> = props =>
|
||||||
|
{
|
||||||
|
const { variant = 'black', height = 1, classNames = [], style = {}, ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [];
|
||||||
|
|
||||||
|
if(variant) newClassNames.push('bg-' + variant);
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ variant, classNames ]);
|
||||||
|
|
||||||
|
const getStyle = useMemo(() =>
|
||||||
|
{
|
||||||
|
let newStyle: CSSProperties = { display: 'list-item' };
|
||||||
|
|
||||||
|
if(height > 0) newStyle.height = height;
|
||||||
|
|
||||||
|
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||||
|
|
||||||
|
return newStyle;
|
||||||
|
}, [ height, style ]);
|
||||||
|
|
||||||
|
return <Base classNames={ getClassNames } style={ getStyle } { ...rest } />;
|
||||||
|
}
|
@ -7,6 +7,7 @@ export * from './Flex';
|
|||||||
export * from './FormGroup';
|
export * from './FormGroup';
|
||||||
export * from './Grid';
|
export * from './Grid';
|
||||||
export * from './GridContext';
|
export * from './GridContext';
|
||||||
|
export * from './HorizontalRule';
|
||||||
export * from './layout';
|
export * from './layout';
|
||||||
export * from './Text';
|
export * from './Text';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
@ -1,54 +1,137 @@
|
|||||||
.nitro-groups {
|
.nitro-group-tab-image {
|
||||||
.tab-image {
|
width: 122px;
|
||||||
width: 122px;
|
height: 68px;
|
||||||
height: 68px;
|
background: url('../../assets/images/groups/creator_images.png') no-repeat;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
div {
|
&.tab-1 {
|
||||||
background-image: url('../../assets/images/groups/creator_images.png');
|
background-position: 0px 0px;
|
||||||
background-repeat: no-repeat;
|
width: 99px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tab-2 {
|
||||||
|
background-position: -99px 0px;
|
||||||
|
width: 98px;
|
||||||
|
height: 62px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tab-3 {
|
||||||
|
background-position: 0px -50px;
|
||||||
|
width: 96px;
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tab-4,
|
||||||
|
&.tab-5 {
|
||||||
|
background-position: 0px -95px;
|
||||||
|
width: 114px;
|
||||||
|
height: 61px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-information {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.group-badge {
|
||||||
|
width: 78px;
|
||||||
|
height: 78px;
|
||||||
|
|
||||||
|
.badge-image {
|
||||||
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.tab-1 {
|
.group-description {
|
||||||
div {
|
height: 55px;
|
||||||
background-position: 0px 0px;
|
}
|
||||||
width: 99px;
|
}
|
||||||
|
|
||||||
|
.nitro-group-information-standalone {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nitro-group-members {
|
||||||
|
width: 400px;
|
||||||
|
|
||||||
|
.nitro-group-members-list-grid {
|
||||||
|
|
||||||
|
.member-list-item {
|
||||||
|
height: 50px;
|
||||||
|
max-height: 50px;
|
||||||
|
|
||||||
|
.avatar-head {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 40px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
|
||||||
}
|
.avatar-image {
|
||||||
|
position: absolute;
|
||||||
&.tab-2 {
|
left: -25px;
|
||||||
div {
|
top: -20px;
|
||||||
background-position: -99px 0px;
|
}
|
||||||
width: 98px;
|
|
||||||
height: 62px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.tab-3 {
|
|
||||||
div {
|
|
||||||
background-position: 0px -50px;
|
|
||||||
width: 96px;
|
|
||||||
height: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.tab-4, &.tab-5 {
|
|
||||||
div {
|
|
||||||
background-position: 0px -95px;
|
|
||||||
width: 114px;
|
|
||||||
height: 61px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.group-badge-preview {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
background-color: $grid-bg-color;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: $grid-active-border-color !important;
|
||||||
|
background-color: $grid-active-bg-color;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
content: ' ';
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 10px solid white;
|
||||||
|
border-left: 10px solid transparent;
|
||||||
|
border-right: 10px solid transparent;
|
||||||
|
bottom: -10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-badge-color-swatch,
|
||||||
|
.group-badge-position-swatch {
|
||||||
|
position: relative;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: $white;
|
||||||
|
border: 2px solid $white;
|
||||||
|
box-shadow: inset 3px 3px rgba(0, 0, 0, .1);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-badge-position-swatch {
|
||||||
|
box-shadow: inset 3px 3px rgba(0, 0, 0, .1);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-badge-color-swatch {
|
||||||
|
box-shadow: inset 2px 2px rgba(0, 0, 0, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-color-swatch {
|
||||||
|
width: 30px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
@import './views/creator/GroupCreatorView';
|
@import './views/creator/GroupCreatorView';
|
||||||
@import './views/information/GroupInformationView';
|
|
||||||
@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/tabs/GroupSharedTabs';
|
||||||
|
@ -12,30 +12,31 @@ import { GroupMembersView } from './views/members/GroupMembersView';
|
|||||||
|
|
||||||
export const GroupsView: FC<{}> = props =>
|
export const GroupsView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ isCreatorVisible, setIsCreatorVisible ] = useState<boolean>(false);
|
const [ currentGroupId, setCurrentGroupId ] = useState<number>(null);
|
||||||
const [ groupMembersId, setGroupMembersId ] = useState<number>(null);
|
const [ currentGroupLevelId, setCurrentGroupLevelId ] = useState<number>(null);
|
||||||
const [ groupMembersLevel, setGroupMembersLevel ] = useState<number>(null);
|
const [ isMembersVisible, setMembersVisible ] = useState<boolean>(false);
|
||||||
|
const [ isCreatorVisible, setCreatorVisible ] = useState<boolean>(false);
|
||||||
const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups);
|
const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups);
|
||||||
|
|
||||||
const onGroupPurchasedEvent = useCallback((event: GroupPurchasedEvent) =>
|
|
||||||
{
|
|
||||||
const parser = event.getParser();
|
|
||||||
|
|
||||||
setIsCreatorVisible(false);
|
|
||||||
TryVisitRoom(parser.roomId);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent);
|
|
||||||
|
|
||||||
const closeMembers = () =>
|
const closeMembers = () =>
|
||||||
{
|
{
|
||||||
BatchUpdates(() =>
|
BatchUpdates(() =>
|
||||||
{
|
{
|
||||||
setGroupMembersId(null);
|
setCurrentGroupId(null);
|
||||||
setGroupMembersLevel(null);
|
setCurrentGroupLevelId(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onGroupPurchasedEvent = useCallback((event: GroupPurchasedEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
setCreatorVisible(false);
|
||||||
|
TryVisitRoom(parser.roomId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent);
|
||||||
|
|
||||||
const linkReceived = useCallback((url: string) =>
|
const linkReceived = useCallback((url: string) =>
|
||||||
{
|
{
|
||||||
const parts = url.split('/');
|
const parts = url.split('/');
|
||||||
@ -45,7 +46,7 @@ export const GroupsView: FC<{}> = props =>
|
|||||||
switch(parts[1])
|
switch(parts[1])
|
||||||
{
|
{
|
||||||
case 'create':
|
case 'create':
|
||||||
setIsCreatorVisible(true);
|
setCreatorVisible(true);
|
||||||
return;
|
return;
|
||||||
case 'manage':
|
case 'manage':
|
||||||
if(!parts[2]) return;
|
if(!parts[2]) return;
|
||||||
@ -55,12 +56,16 @@ export const GroupsView: FC<{}> = props =>
|
|||||||
case 'members':
|
case 'members':
|
||||||
if(!parts[2]) return;
|
if(!parts[2]) return;
|
||||||
|
|
||||||
|
const groupId = (parseInt(parts[2]) || -1);
|
||||||
|
const levelId = (parseInt(parts[3]) || 3);
|
||||||
|
|
||||||
BatchUpdates(() =>
|
BatchUpdates(() =>
|
||||||
{
|
{
|
||||||
setGroupMembersId(Number(parts[2]));
|
setCurrentGroupId(groupId);
|
||||||
|
setCurrentGroupLevelId(levelId);
|
||||||
if(parts[3]) setGroupMembersLevel(Number(parts[3]));
|
setMembersVisible(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
@ -86,9 +91,10 @@ export const GroupsView: FC<{}> = props =>
|
|||||||
<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={ () => setCreatorVisible(false) } />
|
||||||
<GroupManagerView />
|
<GroupManagerView />
|
||||||
<GroupMembersView groupId={ groupMembersId } levelId={ groupMembersLevel } onClose={ closeMembers } />
|
{ isMembersVisible &&
|
||||||
|
<GroupMembersView groupId={ currentGroupId } levelId={ currentGroupLevelId } setLevelId={ setCurrentGroupLevelId } onClose={ closeMembers } /> }
|
||||||
<GroupInformationStandaloneView />
|
<GroupInformationStandaloneView />
|
||||||
</div>
|
</div>
|
||||||
</GroupsContextProvider>
|
</GroupsContextProvider>
|
||||||
|
107
src/components/groups/views/GroupBadgeCreatorView.tsx
Normal file
107
src/components/groups/views/GroupBadgeCreatorView.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { Dispatch, FC, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||||
|
import { Base, Column, Flex, Grid } from '../../../common';
|
||||||
|
import { BatchUpdates } from '../../../hooks';
|
||||||
|
import { BadgeImageView } from '../../../views/shared/badge-image/BadgeImageView';
|
||||||
|
import { GroupBadgePart } from '../common/GroupBadgePart';
|
||||||
|
import { useGroupsContext } from '../GroupsContext';
|
||||||
|
|
||||||
|
interface GroupBadgeCreatorViewProps
|
||||||
|
{
|
||||||
|
badgeParts: GroupBadgePart[];
|
||||||
|
setBadgeParts: Dispatch<SetStateAction<GroupBadgePart[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const POSITIONS: number[] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||||
|
|
||||||
|
export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { badgeParts = [], setBadgeParts = null } = props;
|
||||||
|
const [ selectedIndex, setSelectedIndex ] = useState<number>(-1);
|
||||||
|
const [ copiedBadgeParts, setCopiedBadgeParts ] = useState<GroupBadgePart[]>(null);
|
||||||
|
const { groupsState = null } = useGroupsContext();
|
||||||
|
const { badgeBases = null, badgeSymbols = null, badgePartColors = null } = groupsState;
|
||||||
|
const willUnmount = useRef(false);
|
||||||
|
|
||||||
|
const setPartProperty = (partIndex: number, property: string, value: number) =>
|
||||||
|
{
|
||||||
|
const newBadgeParts = [ ...copiedBadgeParts ];
|
||||||
|
|
||||||
|
newBadgeParts[partIndex][property] = value;
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setCopiedBadgeParts(newBadgeParts);
|
||||||
|
|
||||||
|
if(property === 'key') setSelectedIndex(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setCopiedBadgeParts(badgeParts);
|
||||||
|
setSelectedIndex(-1);
|
||||||
|
});
|
||||||
|
}, [ badgeParts ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!copiedBadgeParts || (copiedBadgeParts === badgeParts)) return;
|
||||||
|
|
||||||
|
setBadgeParts(copiedBadgeParts);
|
||||||
|
}, [ copiedBadgeParts, badgeParts, setBadgeParts ]);
|
||||||
|
|
||||||
|
if(!copiedBadgeParts || !copiedBadgeParts.length) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ ((selectedIndex < 0) && copiedBadgeParts && (copiedBadgeParts.length > 0)) && copiedBadgeParts.map((part, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<Flex key={ index } alignItems="center" justifyContent="between" gap={ 2 } className="bg-muted rounded px-2 py-1">
|
||||||
|
<Flex pointer center className="bg-muted rounded p-1" onClick={ event => setSelectedIndex(index) }>
|
||||||
|
{ (copiedBadgeParts[index].code && (copiedBadgeParts[index].code.length > 0)) &&
|
||||||
|
<BadgeImageView badgeCode={ copiedBadgeParts[index].code } isGroup={ true } /> }
|
||||||
|
{ (!copiedBadgeParts[index].code || !copiedBadgeParts[index].code.length) &&
|
||||||
|
<Flex center className="badge-image group-badge">
|
||||||
|
<FontAwesomeIcon icon="plus" />
|
||||||
|
</Flex> }
|
||||||
|
</Flex>
|
||||||
|
{ (part.type !== GroupBadgePart.BASE) &&
|
||||||
|
<Grid gap={ 1 } columnCount={ 3 }>
|
||||||
|
{ POSITIONS.map((position, posIndex) =>
|
||||||
|
{
|
||||||
|
return <Base key={ posIndex } pointer className={ `group-badge-position-swatch ${ (copiedBadgeParts[index].position === position) ? 'active' : '' }` } onClick={ event => setPartProperty(index, 'position', position) }></Base>
|
||||||
|
}) }
|
||||||
|
</Grid> }
|
||||||
|
<Grid gap={ 1 } columnCount={ 8 }>
|
||||||
|
{ (badgePartColors.length > 0) && badgePartColors.map((item, colorIndex) =>
|
||||||
|
{
|
||||||
|
return <Base key={ colorIndex } pointer className={ `group-badge-color-swatch ${ (copiedBadgeParts[index].color === (colorIndex + 1)) ? 'active' : '' }` } style={{ backgroundColor: '#' + item.color }} onClick={ event => setPartProperty(index, 'color', (colorIndex + 1)) }></Base>
|
||||||
|
}) }
|
||||||
|
</Grid>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
{ (selectedIndex >= 0) &&
|
||||||
|
<Grid gap={ 1 } columnCount={ 5 }>
|
||||||
|
{ (copiedBadgeParts[selectedIndex].type === GroupBadgePart.SYMBOL) &&
|
||||||
|
<Column pointer center className="bg-muted rounded p-1" onClick={ event => setPartProperty(selectedIndex, 'key', 0) }>
|
||||||
|
<Flex center className="badge-image group-badge">
|
||||||
|
<FontAwesomeIcon icon="times" />
|
||||||
|
</Flex>
|
||||||
|
</Column> }
|
||||||
|
{ ((copiedBadgeParts[selectedIndex].type === GroupBadgePart.BASE) ? badgeBases : badgeSymbols).map((item, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<Column key={ index } pointer center className="bg-muted rounded p-1" onClick={ event => setPartProperty(selectedIndex, 'key', item.id) }>
|
||||||
|
<BadgeImageView badgeCode={ GroupBadgePart.getCode(copiedBadgeParts[selectedIndex].type, item.id, copiedBadgeParts[selectedIndex].color, 4) } isGroup={ true } />
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</Grid> }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
.nitro-group-creator {
|
.nitro-group-creator {
|
||||||
width: 433px;
|
width: $nitro-group-creator-width;
|
||||||
|
|
||||||
.creator-tabs {
|
.creator-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -11,14 +11,8 @@
|
|||||||
background-image: url('../../../../assets/images/groups/creator_tabs.png');
|
background-image: url('../../../../assets/images/groups/creator_tabs.png');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
div {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: 0px;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-blue-flat {
|
&.tab-blue-flat {
|
||||||
@ -26,10 +20,6 @@
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
background-position: 0px 0px;
|
background-position: 0px 0px;
|
||||||
|
|
||||||
div {
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
background-position: 0px -24px;
|
background-position: 0px -24px;
|
||||||
@ -41,10 +31,6 @@
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
background-position: 0px -52px;
|
background-position: 0px -52px;
|
||||||
|
|
||||||
div {
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
background-position: 0px -76px;
|
background-position: 0px -76px;
|
||||||
@ -56,10 +42,6 @@
|
|||||||
height: 28px;
|
height: 28px;
|
||||||
background-position: 0px -104px;
|
background-position: 0px -104px;
|
||||||
|
|
||||||
div {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
height: 33px;
|
height: 33px;
|
||||||
background-position: 0px -132px;
|
background-position: 0px -132px;
|
||||||
@ -67,10 +49,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.creator-tab {
|
|
||||||
height: 260px;
|
|
||||||
min-height: 260px;
|
|
||||||
max-height: 260px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { GroupBuyComposer, GroupBuyDataComposer } from '@nitrots/nitro-renderer';
|
import { GroupBuyComposer, GroupBuyDataComposer } from '@nitrots/nitro-renderer';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { Button } from 'react-bootstrap';
|
|
||||||
import { HasHabboClub, LocalizeText } from '../../../../api';
|
import { HasHabboClub, LocalizeText } from '../../../../api';
|
||||||
|
import { Base, Button, Column, Flex, Text } from '../../../../common';
|
||||||
import { SendMessageHook } from '../../../../hooks';
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
|
import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { useGroupsContext } from '../../GroupsContext';
|
import { useGroupsContext } from '../../GroupsContext';
|
||||||
import { GroupsActions } from '../../reducers/GroupsReducer';
|
import { GroupsActions } from '../../reducers/GroupsReducer';
|
||||||
import { GroupSharedTabBadgeView } from '../shared-tabs/GroupSharedTabBadgeView';
|
import { GroupTabBadgeView } from '../tabs/GroupTabBadgeView';
|
||||||
import { GroupSharedTabColorsView } from '../shared-tabs/GroupSharedTabColorsView';
|
import { GroupTabColorsView } from '../tabs/GroupTabColorsView';
|
||||||
import { GroupSharedTabIdentityView } from '../shared-tabs/GroupSharedTabIdentityView';
|
import { GroupTabIdentityView } from '../tabs/GroupTabIdentityView';
|
||||||
import { GroupCreatorTabConfirmationView } from './GroupCreatorTabConfirmationView';
|
import { GroupCreatorTabConfirmationView } from './GroupCreatorTabConfirmationView';
|
||||||
|
|
||||||
const TABS: number[] = [1, 2, 3, 4];
|
const TABS: number[] = [1, 2, 3, 4];
|
||||||
@ -22,34 +22,17 @@ interface GroupCreatorViewProps
|
|||||||
|
|
||||||
export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||||
{
|
{
|
||||||
|
const { isVisible = false, onClose = null } = props;
|
||||||
|
const [ currentTab, setCurrentTab ] = useState<number>(1);
|
||||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||||
const { groupName = null, groupDescription = null, groupHomeroomId = null, groupColors = null, groupBadgeParts = null } = groupsState;
|
const { groupName = null, groupDescription = null, groupHomeroomId = null, groupColors = null, groupBadgeParts = null } = groupsState;
|
||||||
|
|
||||||
const { isVisible = false, onClose = null } = props;
|
|
||||||
|
|
||||||
const [ currentTab, setCurrentTab ] = useState<number>(1);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!isVisible)
|
|
||||||
{
|
|
||||||
setCurrentTab(1);
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.RESET_GROUP_SETTINGS
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SendMessageHook(new GroupBuyDataComposer());
|
|
||||||
}
|
|
||||||
}, [ isVisible ]);
|
|
||||||
|
|
||||||
const buyGroup = useCallback(() =>
|
const buyGroup = useCallback(() =>
|
||||||
{
|
{
|
||||||
const badge = [];
|
|
||||||
|
|
||||||
if(!groupBadgeParts) return;
|
if(!groupBadgeParts) return;
|
||||||
|
|
||||||
|
const badge = [];
|
||||||
|
|
||||||
groupBadgeParts.forEach((part) =>
|
groupBadgeParts.forEach((part) =>
|
||||||
{
|
{
|
||||||
if(part.code)
|
if(part.code)
|
||||||
@ -77,56 +60,78 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
|||||||
case 1: {
|
case 1: {
|
||||||
if(!groupName || groupName.length === 0 || !groupHomeroomId)
|
if(!groupName || groupName.length === 0 || !groupHomeroomId)
|
||||||
{
|
{
|
||||||
alert(LocalizeText('group.edit.error.no.name.or.room.selected'));
|
NotificationUtilities.simpleAlert(LocalizeText('group.edit.error.no.name.or.room.selected'));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4:
|
||||||
buyGroup();
|
buyGroup();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentTab(value =>
|
setCurrentTab(value => (value === 4 ? value : value + 1));
|
||||||
{
|
}, [ currentTab, groupName, groupHomeroomId, buyGroup ]);
|
||||||
return (value === 4 ? value : value + 1)
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!isVisible)
|
||||||
|
{
|
||||||
|
setCurrentTab(1);
|
||||||
|
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.RESET_GROUP_SETTINGS
|
||||||
});
|
});
|
||||||
}, [ currentTab, groupName, groupHomeroomId ]);
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendMessageHook(new GroupBuyDataComposer());
|
||||||
|
}, [ isVisible, dispatchGroupsState ]);
|
||||||
|
|
||||||
if(!isVisible) return null;
|
if(!isVisible) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView className="nitro-group-creator" simple={ true }>
|
<NitroCardView className="nitro-group-creator" simple={ true }>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('group.create.title') } onCloseClick={ onClose } />
|
<NitroCardHeaderView headerText={ LocalizeText('group.create.title') } onCloseClick={ onClose } />
|
||||||
<NitroCardContentView className="pb-2">
|
<NitroCardContentView>
|
||||||
<div className="creator-tabs mb-2">
|
<Flex className="creator-tabs">
|
||||||
{ TABS.map((tab, index) =>
|
{ TABS.map((tab, index) =>
|
||||||
{
|
{
|
||||||
return (<div key={ index } className={ 'tab tab-' + (tab === 1 ? 'blue-flat' : tab === 4 ? 'yellow' : 'blue-arrow') + classNames({ ' active': currentTab === tab }) }>
|
return (
|
||||||
<div>{ LocalizeText('group.create.steplabel.' + tab) }</div>
|
<Flex center key={ index } className={ `tab tab-${ ((tab === 1) ? 'blue-flat' : (tab === 4) ? 'yellow' : 'blue-arrow') } ${ (currentTab === tab) ? 'active' : '' }` }>
|
||||||
</div>);
|
<Text variant="white">{ LocalizeText(`group.create.steplabel.${ tab }`) }</Text>
|
||||||
}) }
|
</Flex>
|
||||||
</div>
|
);
|
||||||
<div className="d-flex align-items-center mb-2">
|
}) }
|
||||||
<div className={ 'flex-shrink-0 tab-image tab-' + currentTab }>
|
</Flex>
|
||||||
<div></div>
|
<Flex alignItems="center" gap={ 2 }>
|
||||||
</div>
|
<Base className={ `nitro-group-tab-image tab-${ currentTab }`} />
|
||||||
<div className="w-100 text-black ms-2">
|
<Column grow gap={ 0 }>
|
||||||
<div className="fw-bold h4 m-0">{ LocalizeText('group.create.stepcaption.' + currentTab) }</div>
|
<Text bold fontSize={ 4 }>{ LocalizeText(`group.create.stepcaption.${ currentTab }`) }</Text>
|
||||||
<div>{ LocalizeText('group.create.stepdesc.' + currentTab) }</div>
|
<Text>{ LocalizeText(`group.create.stepdesc.${ currentTab }`) }</Text>
|
||||||
</div>
|
</Column>
|
||||||
</div>
|
</Flex>
|
||||||
<div className="text-black creator-tab">
|
<Column>
|
||||||
{ currentTab === 1 && <GroupSharedTabIdentityView isCreator={ true } /> }
|
{ (currentTab === 1) &&
|
||||||
{ currentTab === 2 && <GroupSharedTabBadgeView /> }
|
<GroupTabIdentityView isCreator={ true } /> }
|
||||||
{ currentTab === 3 && <GroupSharedTabColorsView /> }
|
{ (currentTab === 2) &&
|
||||||
{ currentTab === 4 && <GroupCreatorTabConfirmationView /> }
|
<GroupTabBadgeView /> }
|
||||||
</div>
|
{ (currentTab === 3) &&
|
||||||
<div className="d-flex justify-content-between mt-2">
|
<GroupTabColorsView /> }
|
||||||
<Button variant="link" className="text-black" onClick={ previousStep }>{ LocalizeText(currentTab === 1 ? 'generic.cancel' : 'group.create.previousstep') }</Button>
|
{ (currentTab === 4) &&
|
||||||
<button disabled={ currentTab === 4 && !HasHabboClub() } className={ 'btn ' + (currentTab === 4 ? HasHabboClub() ? 'btn-success' : 'btn-danger' : 'btn-primary') } onClick={ nextStep }>{ LocalizeText(currentTab === 4 ? HasHabboClub() ? 'group.create.confirm.buy' : 'group.create.confirm.viprequired' : 'group.create.nextstep') }</button>
|
<GroupCreatorTabConfirmationView /> }
|
||||||
</div>
|
</Column>
|
||||||
|
<Flex justifyContent="between">
|
||||||
|
<Button variant="link" className="text-black" onClick={ previousStep }>
|
||||||
|
{ LocalizeText(currentTab === 1 ? 'generic.cancel' : 'group.create.previousstep') }
|
||||||
|
</Button>
|
||||||
|
<Button disabled={ ((currentTab === 4) && !HasHabboClub()) } variant={ ((currentTab === 4) ? HasHabboClub() ? 'success' : 'danger' : 'primary') } onClick={ nextStep }>
|
||||||
|
{ LocalizeText((currentTab === 4) ? HasHabboClub() ? 'group.create.confirm.buy' : 'group.create.confirm.viprequired' : 'group.create.nextstep') }
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView>
|
</NitroCardView>
|
||||||
);
|
);
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.nitro-group-information-standalone {
|
|
||||||
width: 500px;
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import { GroupInformationEvent, GroupInformationParser } from '@nitrots/nitro-renderer';
|
import { GroupInformationEvent, GroupInformationParser } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../api';
|
import { LocalizeText } from '../../../../api';
|
||||||
import { CreateMessageHook } from '../../../../hooks';
|
import { BatchUpdates, CreateMessageHook } from '../../../../hooks';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
import { GroupInformationView } from '../information/GroupInformationView';
|
import { GroupInformationView } from '../information/GroupInformationView';
|
||||||
|
|
||||||
@ -15,8 +15,11 @@ export const GroupInformationStandaloneView: FC<{}> = props =>
|
|||||||
|
|
||||||
if(!parser.flag) return;
|
if(!parser.flag) return;
|
||||||
|
|
||||||
setGroupInformation(null);
|
BatchUpdates(() =>
|
||||||
setGroupInformation(parser);
|
{
|
||||||
|
setGroupInformation(null);
|
||||||
|
setGroupInformation(parser);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
CreateMessageHook(GroupInformationEvent, onGroupInformationEvent);
|
CreateMessageHook(GroupInformationEvent, onGroupInformationEvent);
|
||||||
@ -24,9 +27,9 @@ export const GroupInformationStandaloneView: FC<{}> = props =>
|
|||||||
if(!groupInformation) return null;
|
if(!groupInformation) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView className="nitro-group-information-standalone" simple={ true }>
|
<NitroCardView className="nitro-group-information-standalone" simple>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('group.window.title') } onCloseClick={ () => setGroupInformation(null) } />
|
<NitroCardHeaderView headerText={ LocalizeText('group.window.title') } onCloseClick={ () => setGroupInformation(null) } />
|
||||||
<NitroCardContentView className="pb-2">
|
<NitroCardContentView>
|
||||||
<GroupInformationView groupInformation={ groupInformation } onClose={ () => setGroupInformation(null) } />
|
<GroupInformationView groupInformation={ groupInformation } onClose={ () => setGroupInformation(null) } />
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView>
|
</NitroCardView>
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
.group-information {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.group-badge {
|
|
||||||
width: 78px;
|
|
||||||
height: 78px;
|
|
||||||
|
|
||||||
.badge-image {
|
|
||||||
background-size: contain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-description {
|
|
||||||
height: 55px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,9 @@ import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } fr
|
|||||||
import { GetGroupManager } from '../../../../api/groups/GetGroupManager';
|
import { GetGroupManager } from '../../../../api/groups/GetGroupManager';
|
||||||
import { GetGroupMembers } from '../../../../api/groups/GetGroupMembers';
|
import { GetGroupMembers } from '../../../../api/groups/GetGroupMembers';
|
||||||
import { TryJoinGroup } from '../../../../api/groups/TryJoinGroup';
|
import { TryJoinGroup } from '../../../../api/groups/TryJoinGroup';
|
||||||
|
import { Button, Column, Flex, Grid, Text } from '../../../../common';
|
||||||
import { SendMessageHook } from '../../../../hooks';
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
|
import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
||||||
import { CatalogPageName } from '../../../catalog/common/CatalogPageName';
|
import { CatalogPageName } from '../../../catalog/common/CatalogPageName';
|
||||||
import { GroupMembershipType } from '../../common/GroupMembershipType';
|
import { GroupMembershipType } from '../../common/GroupMembershipType';
|
||||||
@ -21,30 +23,21 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
{
|
{
|
||||||
const { groupInformation = null, onClose = null } = props;
|
const { groupInformation = null, onClose = null } = props;
|
||||||
|
|
||||||
const joinGroup = useCallback(() =>
|
const isRealOwner = () => (groupInformation && (groupInformation.ownerName === GetSessionDataManager().userName));
|
||||||
|
|
||||||
|
const joinGroup = () => (groupInformation && TryJoinGroup(groupInformation.id));
|
||||||
|
|
||||||
|
const leaveGroup = () =>
|
||||||
{
|
{
|
||||||
if(!groupInformation) return;
|
NotificationUtilities.confirm(LocalizeText('group.leaveconfirm.desc'), () =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
|
||||||
|
|
||||||
TryJoinGroup(groupInformation.id);
|
if(onClose) onClose();
|
||||||
}, [ groupInformation ]);
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
const leaveGroup = useCallback(() =>
|
const getRoleIcon = () =>
|
||||||
{
|
|
||||||
if(window.confirm(LocalizeText('group.leaveconfirm.desc')))
|
|
||||||
{
|
|
||||||
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
|
|
||||||
if(onClose) onClose();
|
|
||||||
}
|
|
||||||
}, [ groupInformation, onClose ]);
|
|
||||||
|
|
||||||
const isRealOwner = useCallback(() =>
|
|
||||||
{
|
|
||||||
if(!groupInformation) return false;
|
|
||||||
|
|
||||||
return (groupInformation.ownerName === GetSessionDataManager().userName);
|
|
||||||
}, [ groupInformation ]);
|
|
||||||
|
|
||||||
const getRoleIcon = useCallback(() =>
|
|
||||||
{
|
{
|
||||||
if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER || groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return null;
|
if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER || groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return null;
|
||||||
|
|
||||||
@ -53,9 +46,9 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
if(groupInformation.isAdmin) return <i className="icon icon-group-admin" title={ LocalizeText('group.youareadmin') } />;
|
if(groupInformation.isAdmin) return <i className="icon icon-group-admin" title={ LocalizeText('group.youareadmin') } />;
|
||||||
|
|
||||||
return <i className="icon icon-group-member" title={ LocalizeText('group.youaremember') } />;
|
return <i className="icon icon-group-member" title={ LocalizeText('group.youaremember') } />;
|
||||||
}, [ groupInformation, isRealOwner ]);
|
}
|
||||||
|
|
||||||
const getButtonText = useCallback(() =>
|
const getButtonText = () =>
|
||||||
{
|
{
|
||||||
if(groupInformation.type === GroupType.PRIVATE) return '';
|
if(groupInformation.type === GroupType.PRIVATE) return '';
|
||||||
|
|
||||||
@ -71,16 +64,21 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
|
|
||||||
if(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return 'group.membershippending';
|
if(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return 'group.membershippending';
|
||||||
}
|
}
|
||||||
}, [ groupInformation, isRealOwner ]);
|
}
|
||||||
|
|
||||||
const handleButtonClick = useCallback(() =>
|
const handleButtonClick = () =>
|
||||||
{
|
{
|
||||||
if(groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return;
|
if((groupInformation.type === GroupType.PRIVATE) && (groupInformation.membershipType === GroupMembershipType.NOT_MEMBER)) return;
|
||||||
|
|
||||||
if(groupInformation.membershipType === GroupMembershipType.MEMBER) return leaveGroup();
|
if(groupInformation.membershipType === GroupMembershipType.MEMBER)
|
||||||
|
{
|
||||||
|
leaveGroup();
|
||||||
|
|
||||||
return joinGroup();
|
return;
|
||||||
}, [ groupInformation, leaveGroup, joinGroup ]);
|
}
|
||||||
|
|
||||||
|
joinGroup();
|
||||||
|
}
|
||||||
|
|
||||||
const handleAction = useCallback((action: string) =>
|
const handleAction = useCallback((action: string) =>
|
||||||
{
|
{
|
||||||
@ -102,56 +100,52 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
|||||||
CreateLinkEvent('catalog/open/' + CatalogPageName.GUILD_CUSTOM_FURNI);
|
CreateLinkEvent('catalog/open/' + CatalogPageName.GUILD_CUSTOM_FURNI);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}, [ groupInformation, onClose ]);
|
}, [ groupInformation ]);
|
||||||
|
|
||||||
if(!groupInformation) return null;
|
if(!groupInformation) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group-information text-black p-2">
|
<Grid overflow="hidden">
|
||||||
<div>
|
<Column center size={ 3 } overflow="hidden">
|
||||||
<div className="text-center d-flex flex-column h-100 small text-black text-decoration-underline">
|
<Flex alignItems="center" overflow="hidden" className="group-badge">
|
||||||
<div className="group-badge d-flex align-items-center">
|
<BadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } scale={ 2 } />
|
||||||
<BadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } className="mx-auto d-block"/>
|
</Flex>
|
||||||
</div>
|
<Column alignItems="center" gap={ 1 }>
|
||||||
<div className="mt-3 cursor-pointer" onClick={ () => handleAction('members') }>
|
<Text small underline pointer onClick={ () => handleAction('members') }>{ LocalizeText('group.membercount', [ 'totalMembers' ], [ groupInformation.membersCount.toString() ]) }</Text>
|
||||||
{ LocalizeText('group.membercount', ['totalMembers'], [groupInformation.membersCount.toString()]) }
|
{ (groupInformation.pendingRequestsCount > 0) &&
|
||||||
</div>
|
<Text small underline pointer onClick={ () => handleAction('members_pending') }>{ LocalizeText('group.pendingmembercount', [ 'amount' ], [ groupInformation.pendingRequestsCount.toString() ]) }</Text> }
|
||||||
{ groupInformation.pendingRequestsCount > 0 && <div className="cursor-pointer" onClick={ () => handleAction('members_pending') }>
|
{ groupInformation.isOwner &&
|
||||||
{ LocalizeText('group.pendingmembercount', ['amount'], [groupInformation.pendingRequestsCount.toString()]) }
|
<Text small underline pointer onClick={ () => handleAction('manage') }>{ LocalizeText('group.manage') }</Text> }
|
||||||
</div> }
|
</Column>
|
||||||
{ groupInformation.isOwner && <div className="cursor-pointer" onClick={ () => handleAction('manage') }>
|
{ getRoleIcon() }
|
||||||
{ LocalizeText('group.manage') }
|
</Column>
|
||||||
</div> }
|
<Column size={ 9 } justifyContent="between" overflow="auto">
|
||||||
<div className="mt-auto mb-1">
|
<Column overflow="hidden">
|
||||||
{ getRoleIcon() }
|
<Column gap={ 1 }>
|
||||||
</div>
|
<Flex alignItems="center" gap={ 2 }>
|
||||||
</div>
|
<Text bold>{ groupInformation.title }</Text>
|
||||||
</div>
|
<Flex gap={ 1 }>
|
||||||
<div className="ms-2 w-100">
|
<i className={ 'icon icon-group-type-' + groupInformation.type } />
|
||||||
<div className="fw-bold d-flex align-items-center">
|
{ groupInformation.canMembersDecorate &&
|
||||||
<i className={ 'icon icon-group-type-' + groupInformation.type } />
|
<i className="icon icon-group-decorate" /> }
|
||||||
{ groupInformation.canMembersDecorate && <i className="icon icon-group-decorate ms-1" /> }
|
</Flex>
|
||||||
<div className="ms-1">{ groupInformation.title }</div>
|
</Flex>
|
||||||
</div>
|
<Text small>{ LocalizeText('group.created', ['date', 'owner'], [groupInformation.createdAt, groupInformation.ownerName]) }</Text>
|
||||||
<div>{ LocalizeText('group.created', ['date', 'owner'], [groupInformation.createdAt, groupInformation.ownerName]) }</div>
|
</Column>
|
||||||
<div className="group-description small overflow-auto">{ groupInformation.description }</div>
|
<Text small overflow="auto" className="group-description">{ groupInformation.description }</Text>
|
||||||
<div className="small text-black text-decoration-underline">
|
</Column>
|
||||||
<div className="cursor-pointer" onClick={ () => handleAction('homeroom') }>
|
<Column>
|
||||||
{ LocalizeText('group.linktobase') }
|
<Column gap={ 1 }>
|
||||||
</div>
|
<Text small underline pointer onClick={ () => handleAction('homeroom') }>{ LocalizeText('group.linktobase') }</Text>
|
||||||
<div className="cursor-pointer" onClick={ () => handleAction('furniture') }>
|
<Text small underline pointer onClick={ () => handleAction('furniture') }>{ LocalizeText('group.buyfurni') }</Text>
|
||||||
{ LocalizeText('group.buyfurni') }
|
<Text small underline pointer>{ LocalizeText('group.showgroups') }</Text>
|
||||||
</div>
|
</Column>
|
||||||
<div className="cursor-pointer">
|
{ (groupInformation.type !== GroupType.PRIVATE) &&
|
||||||
{ LocalizeText('group.showgroups') }
|
<Button disabled={ (groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) || isRealOwner() } onClick={ handleButtonClick }>
|
||||||
</div>
|
{ LocalizeText(getButtonText()) }
|
||||||
</div>
|
</Button> }
|
||||||
{ groupInformation.type !== GroupType.PRIVATE &&
|
</Column>
|
||||||
<button className="btn btn-primary w-100 mt-2" disabled={ groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING || isRealOwner() } onClick={ handleButtonClick }>
|
</Column>
|
||||||
{ LocalizeText(getButtonText()) }
|
</Grid>
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
.nitro-group-manager {
|
.nitro-group-manager {
|
||||||
width: 385px;
|
height: $nitro-group-manager-height;
|
||||||
|
width: $nitro-group-manager-width;
|
||||||
.manager-tab {
|
|
||||||
height: 260px;
|
|
||||||
min-height: 260px;
|
|
||||||
max-height: 260px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { GroupDeleteComposer, GroupSaveBadgeComposer, GroupSaveColorsComposer, GroupSaveInformationComposer, GroupSavePreferencesComposer } from '@nitrots/nitro-renderer';
|
import { GroupDeleteComposer, GroupSaveColorsComposer, GroupSaveInformationComposer, GroupSavePreferencesComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../api';
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Base, Button, Column, Flex, Text } from '../../../../common';
|
||||||
import { SendMessageHook } from '../../../../hooks';
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
|
||||||
import { useGroupsContext } from '../../GroupsContext';
|
import { useGroupsContext } from '../../GroupsContext';
|
||||||
import { GroupsActions } from '../../reducers/GroupsReducer';
|
import { GroupsActions } from '../../reducers/GroupsReducer';
|
||||||
import { GroupSharedTabBadgeView } from '../shared-tabs/GroupSharedTabBadgeView';
|
import { GroupTabBadgeView } from '../tabs/GroupTabBadgeView';
|
||||||
import { GroupSharedTabColorsView } from '../shared-tabs/GroupSharedTabColorsView';
|
import { GroupTabColorsView } from '../tabs/GroupTabColorsView';
|
||||||
import { GroupSharedTabIdentityView } from '../shared-tabs/GroupSharedTabIdentityView';
|
import { GroupTabIdentityView } from '../tabs/GroupTabIdentityView';
|
||||||
import { GroupManagerTabSettingsView } from './tab-settings/GroupManagerTabSettingsView';
|
import { GroupTabSettingsView } from '../tabs/GroupTabSettingsView';
|
||||||
|
|
||||||
const TABS: number[] = [1, 2, 3, 5];
|
const TABS: number[] = [1, 2, 3, 5];
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ export const GroupManagerView: FC<{}> = props =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
SendMessageHook(new GroupSaveInformationComposer(groupId, groupName, groupDescription));
|
SendMessageHook(new GroupSaveInformationComposer(groupId, groupName, groupDescription));
|
||||||
SendMessageHook(new GroupSaveBadgeComposer(groupId, badge));
|
//SendMessageHook(new GroupSaveBadgeComposer(groupId, badge));
|
||||||
SendMessageHook(new GroupSaveColorsComposer(groupId, groupColors[0], groupColors[1]));
|
SendMessageHook(new GroupSaveColorsComposer(groupId, groupColors[0], groupColors[1]));
|
||||||
SendMessageHook(new GroupSavePreferencesComposer(groupId, groupState, groupCanMembersDecorate ? 0 : 1));
|
SendMessageHook(new GroupSavePreferencesComposer(groupId, groupState, groupCanMembersDecorate ? 0 : 1));
|
||||||
|
|
||||||
@ -64,36 +65,36 @@ export const GroupManagerView: FC<{}> = props =>
|
|||||||
return (
|
return (
|
||||||
<NitroCardView className="nitro-group-manager" simple={ false }>
|
<NitroCardView className="nitro-group-manager" simple={ false }>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('group.window.title') } onCloseClick={ onClose } />
|
<NitroCardHeaderView headerText={ LocalizeText('group.window.title') } onCloseClick={ onClose } />
|
||||||
<NitroCardContentView className="p-0">
|
<NitroCardTabsView>
|
||||||
<NitroCardTabsView>
|
{ TABS.map(tab =>
|
||||||
{ TABS.map(tab =>
|
{
|
||||||
{
|
return (<NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ () => setCurrentTab(tab) }>
|
||||||
return (<NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ () => setCurrentTab(tab) }>
|
{ LocalizeText(`group.edit.tab.${tab}`) }
|
||||||
{ LocalizeText(`group.edit.tab.${tab}`) }
|
</NitroCardTabsItemView>);
|
||||||
</NitroCardTabsItemView>);
|
}) }
|
||||||
}) }
|
</NitroCardTabsView>
|
||||||
</NitroCardTabsView>
|
<NitroCardContentView>
|
||||||
<div className="p-2">
|
<Flex alignItems="center" gap={ 2 }>
|
||||||
<div className="d-flex align-items-center mb-2">
|
<Base className={ `nitro-group-tab-image tab-${ currentTab }`} />
|
||||||
<div className={ 'flex-shrink-0 tab-image tab-' + currentTab }>
|
<Column grow gap={ 0 }>
|
||||||
<div></div>
|
<Text bold fontSize={ 4 }>{ LocalizeText(`group.edit.tabcaption.${ currentTab }`) }</Text>
|
||||||
</div>
|
<Text>{ LocalizeText(`group.edit.tabdesc.${ currentTab }`) }</Text>
|
||||||
<div className="w-100 text-black ms-2">
|
</Column>
|
||||||
<div className="fw-bold h4 m-0">{ LocalizeText('group.edit.tabcaption.' + currentTab) }</div>
|
</Flex>
|
||||||
<div>{ LocalizeText('group.edit.tabdesc.' + currentTab) }</div>
|
<Column grow overflow="hidden">
|
||||||
</div>
|
{ currentTab === 1 &&
|
||||||
</div>
|
<GroupTabIdentityView /> }
|
||||||
<div className="text-black manager-tab">
|
{ currentTab === 2 &&
|
||||||
{ currentTab === 1 && <GroupSharedTabIdentityView /> }
|
<GroupTabBadgeView skipDefault={ true } /> }
|
||||||
{ currentTab === 2 && <GroupSharedTabBadgeView skipDefault={ true } /> }
|
{ currentTab === 3 &&
|
||||||
{ currentTab === 3 && <GroupSharedTabColorsView /> }
|
<GroupTabColorsView /> }
|
||||||
{ currentTab === 5 && <GroupManagerTabSettingsView /> }
|
{ currentTab === 5 &&
|
||||||
</div>
|
<GroupTabSettingsView /> }
|
||||||
<div className="d-flex justify-content-between mt-2">
|
</Column>
|
||||||
<button className="btn btn-danger" onClick={ deleteGroup }>{ LocalizeText('group.delete') }</button>
|
<Flex justifyContent="between">
|
||||||
<button className="btn btn-success" onClick={ saveGroup }>{ LocalizeText('save') }</button>
|
<Button variant="danger" onClick={ deleteGroup }>{ LocalizeText('group.delete') }</Button>
|
||||||
</div>
|
<Button variant="success" onClick={ saveGroup }>{ LocalizeText('save') }</Button>
|
||||||
</div>
|
</Flex>
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView>
|
</NitroCardView>
|
||||||
);
|
);
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import { FC, useCallback } from 'react';
|
|
||||||
import { LocalizeText } from '../../../../../api/utils/LocalizeText';
|
|
||||||
import { useGroupsContext } from '../../../GroupsContext';
|
|
||||||
import { GroupsActions } from '../../../reducers/GroupsReducer';
|
|
||||||
|
|
||||||
const STATES: string[] = ['regular', 'exclusive', 'private'];
|
|
||||||
|
|
||||||
export const GroupManagerTabSettingsView: FC<{}> = props =>
|
|
||||||
{
|
|
||||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
|
||||||
const { groupState = null, groupCanMembersDecorate = false } = groupsState;
|
|
||||||
|
|
||||||
const setState = useCallback((state: number) =>
|
|
||||||
{
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_STATE,
|
|
||||||
payload: {
|
|
||||||
numberValues: [ state ]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [ dispatchGroupsState ]);
|
|
||||||
|
|
||||||
const toggleCanMembersDecorate = useCallback(() =>
|
|
||||||
{
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_CAN_MEMBERS_DECORATE,
|
|
||||||
payload: {
|
|
||||||
boolValues: [ !groupCanMembersDecorate ]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [ dispatchGroupsState, groupCanMembersDecorate ]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{ STATES.map((state, index) =>
|
|
||||||
{
|
|
||||||
return <div key={ index } className="form-check mb-1">
|
|
||||||
<input className="form-check-input" type="radio" name="groupState" id={ `groupState${ index }` } checked={ (groupState === index) } onChange={ () => setState(index) } />
|
|
||||||
<label className="form-check-label d-flex align-items-center gap-1 fw-bold" htmlFor={'groupState' + index}>
|
|
||||||
<i className={ `icon icon-group-type-${index}` } /> { LocalizeText(`group.edit.settings.type.${state}.label`) }
|
|
||||||
</label>
|
|
||||||
<div>{ LocalizeText(`group.edit.settings.type.${state}.help`) }</div>
|
|
||||||
</div>
|
|
||||||
}) }
|
|
||||||
<hr className="bg-dark" />
|
|
||||||
<div className="form-check">
|
|
||||||
<input className="form-check-input" type="checkbox" id="groupCanMembersDecorate" checked={ groupCanMembersDecorate } onChange={() => toggleCanMembersDecorate() } />
|
|
||||||
<label className="form-check-label fw-bold" htmlFor="groupCanMembersDecorate">
|
|
||||||
{ LocalizeText('group.edit.settings.rights.caption') }
|
|
||||||
</label>
|
|
||||||
<div>{ LocalizeText('group.edit.settings.rights.members.help') }</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,27 +0,0 @@
|
|||||||
.nitro-group-members {
|
|
||||||
width: 400px;
|
|
||||||
|
|
||||||
.group-badge {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members-list {
|
|
||||||
.list-member {
|
|
||||||
border: 1px solid #ced4da;
|
|
||||||
|
|
||||||
.avatar-head {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 40px;
|
|
||||||
height: 50px;
|
|
||||||
|
|
||||||
.avatar-image {
|
|
||||||
position: absolute;
|
|
||||||
left: -25px;
|
|
||||||
top: -20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,12 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer } 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 { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react';
|
||||||
import { GetSessionDataManager, GetUserProfile, LocalizeText } from '../../../../api';
|
import { GetSessionDataManager, GetUserProfile, LocalizeText } from '../../../../api';
|
||||||
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
import { Base, Button, Column, Flex, Grid, Text } from '../../../../common';
|
||||||
|
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
|
import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { AvatarImageView } from '../../../../views/shared/avatar-image/AvatarImageView';
|
import { AvatarImageView } from '../../../../views/shared/avatar-image/AvatarImageView';
|
||||||
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
||||||
|
|
||||||
@ -11,90 +14,31 @@ interface GroupMembersViewProps
|
|||||||
{
|
{
|
||||||
groupId: number;
|
groupId: number;
|
||||||
levelId: number;
|
levelId: number;
|
||||||
|
setLevelId: Dispatch<SetStateAction<number>>;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { groupId = null, levelId = null, onClose = null } = props;
|
const { groupId = -1, levelId = -1, setLevelId = null, onClose = null } = props;
|
||||||
|
|
||||||
const [ pageData, setPageData ] = useState<GroupMembersParser>(null);
|
const [ pageData, setPageData ] = useState<GroupMembersParser>(null);
|
||||||
|
const [ pageId, setPageId ] = useState<number>(-1);
|
||||||
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
||||||
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 [ removingMemberName, setRemovingMemberName ] = useState<string>(null);
|
||||||
|
|
||||||
const searchMembers = useCallback((pageId: number, newLevelId?: number) =>
|
const refreshMembers = useCallback(() =>
|
||||||
{
|
{
|
||||||
if(!groupId) return;
|
if((groupId === -1) || (levelId === -1) || (pageId === -1)) return;
|
||||||
|
|
||||||
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, newLevelId !== undefined ? newLevelId : searchLevelId));
|
|
||||||
}, [ groupId, searchQuery, searchLevelId ]);
|
|
||||||
|
|
||||||
const onGroupMembersEvent = useCallback((event: GroupMembersEvent) =>
|
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId));
|
||||||
{
|
}, [ groupId, levelId, pageId, searchQuery ]);
|
||||||
const parser = event.getParser();
|
|
||||||
|
|
||||||
setPageData(null);
|
|
||||||
setPageData(parser);
|
|
||||||
setSearchLevelId(parser.level);
|
|
||||||
setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onGroupConfirmMemberRemoveEvent = useCallback((event: GroupConfirmMemberRemoveEvent) =>
|
const previousPage = () => setPageId(prevValue => (prevValue - 1));
|
||||||
{
|
|
||||||
const parser = event.getParser();
|
|
||||||
|
|
||||||
if(window.confirm(LocalizeText(parser.furnitureCount > 0 ? 'group.kickconfirm.desc' : 'group.kickconfirm_nofurni.desc', ['user', 'amount'], [removingMemberName, parser.furnitureCount.toString()])))
|
const nextPage = () => setPageId(prevValue => (prevValue + 1));
|
||||||
{
|
|
||||||
SendMessageHook(new GroupRemoveMemberComposer(pageData.groupId, parser.userId));
|
|
||||||
searchMembers(pageData.pageIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
setRemovingMemberName(null);
|
const getRankDescription = (member: GroupMemberParser) =>
|
||||||
}, [ pageData, removingMemberName, searchMembers ]);
|
|
||||||
|
|
||||||
CreateMessageHook(GroupMembersEvent, onGroupMembersEvent);
|
|
||||||
CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
setPageData(null);
|
|
||||||
setSearchQuery('');
|
|
||||||
setSearchLevelId(0);
|
|
||||||
|
|
||||||
if(!groupId) return;
|
|
||||||
|
|
||||||
if(levelId !== null) setSearchLevelId(levelId);
|
|
||||||
|
|
||||||
searchMembers(0, levelId);
|
|
||||||
}, [ groupId, levelId ]);
|
|
||||||
|
|
||||||
const selectSearchLevelId = useCallback((level: number) =>
|
|
||||||
{
|
|
||||||
setSearchLevelId(level);
|
|
||||||
searchMembers(0, level);
|
|
||||||
}, [ searchMembers ]);
|
|
||||||
|
|
||||||
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(member.rank === GroupRank.OWNER) return 'group.members.owner';
|
||||||
|
|
||||||
@ -106,107 +50,151 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}, [ pageData ]);
|
}
|
||||||
|
|
||||||
const toggleAdmin = useCallback((member: GroupMemberParser) =>
|
const toggleAdmin = (member: GroupMemberParser) =>
|
||||||
{
|
{
|
||||||
if(member.rank === GroupRank.OWNER) return;
|
if(member.rank === GroupRank.OWNER) return;
|
||||||
|
|
||||||
if(member.rank !== GroupRank.ADMIN)
|
if(member.rank !== GroupRank.ADMIN) SendMessageHook(new GroupAdminGiveComposer(pageData.groupId, member.id));
|
||||||
{
|
else SendMessageHook(new GroupAdminTakeComposer(pageData.groupId, member.id));
|
||||||
SendMessageHook(new GroupAdminGiveComposer(pageData.groupId, member.id));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SendMessageHook(new GroupAdminTakeComposer(pageData.groupId, member.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
searchMembers(pageData.pageIndex);
|
refreshMembers();
|
||||||
}, [ pageData, searchMembers ]);
|
}
|
||||||
|
|
||||||
const acceptMembership = useCallback((member) =>
|
const acceptMembership = (member: GroupMemberParser) =>
|
||||||
{
|
{
|
||||||
if(member.rank === GroupRank.REQUESTED)
|
if(member.rank !== GroupRank.REQUESTED) return;
|
||||||
{
|
|
||||||
SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id));
|
SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id));
|
||||||
searchMembers(pageData.pageIndex);
|
|
||||||
}
|
|
||||||
}, [ pageData, searchMembers ]);
|
|
||||||
|
|
||||||
const removeMemberOrDeclineMembership = useCallback((member) =>
|
refreshMembers();
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeMemberOrDeclineMembership = (member: GroupMemberParser) =>
|
||||||
{
|
{
|
||||||
if(member.rank === GroupRank.REQUESTED)
|
if(member.rank === GroupRank.REQUESTED)
|
||||||
{
|
{
|
||||||
SendMessageHook(new GroupMembershipDeclineComposer(pageData.groupId, member.id));
|
SendMessageHook(new GroupMembershipDeclineComposer(pageData.groupId, member.id));
|
||||||
searchMembers(pageData.pageIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setRemovingMemberName(member.name);
|
|
||||||
SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id));
|
|
||||||
}
|
|
||||||
}, [ pageData, searchMembers ]);
|
|
||||||
|
|
||||||
if(!groupId) return null;
|
refreshMembers();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRemovingMemberName(member.name);
|
||||||
|
SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
const onGroupMembersEvent = useCallback((event: GroupMembersEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setPageData(parser);
|
||||||
|
//setSearchLevelId(parser.level);
|
||||||
|
setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(GroupMembersEvent, onGroupMembersEvent);
|
||||||
|
|
||||||
|
const onGroupConfirmMemberRemoveEvent = useCallback((event: GroupConfirmMemberRemoveEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
NotificationUtilities.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));
|
||||||
|
|
||||||
|
refreshMembers();
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
setRemovingMemberName(null);
|
||||||
|
}, [ pageData, removingMemberName, refreshMembers ]);
|
||||||
|
|
||||||
|
CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setPageId(0);
|
||||||
|
}, [ groupId, levelId, searchQuery ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if((groupId === -1) || (levelId === -1) || (pageId === -1)) return;
|
||||||
|
|
||||||
|
SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId));
|
||||||
|
}, [ groupId, levelId, pageId, searchQuery ]);
|
||||||
|
|
||||||
|
if(!groupId || !pageData) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView className="nitro-group-members" simple={ true }>
|
<NitroCardView className="nitro-group-members" simple>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('group.members.title', ['groupName'], [ pageData ? pageData.groupTitle : '' ]) } onCloseClick={ onClose } />
|
<NitroCardHeaderView headerText={ LocalizeText('group.members.title', ['groupName'], [ pageData ? pageData.groupTitle : '' ]) } onCloseClick={ onClose } />
|
||||||
{ pageData && <>
|
<NitroCardContentView overflow="hidden">
|
||||||
<NitroCardContentView className="pb-2">
|
<Flex gap={ 2 }>
|
||||||
<div className="d-flex gap-2 align-items-center mb-2">
|
<Flex center className="group-badge">
|
||||||
<div className="group-badge">
|
<BadgeImageView badgeCode={ pageData.badge } isGroup={ true } className="mx-auto d-block"/>
|
||||||
<BadgeImageView badgeCode={ pageData.badge } isGroup={ true } />
|
</Flex>
|
||||||
</div>
|
<Column fullWidth gap={ 1 }>
|
||||||
<div className="w-100">
|
<input type="text" className="form-control form-control-sm w-100" placeholder={ LocalizeText('group.members.searchinfo') } value={ searchQuery } onChange={ event => setSearchQuery(event.target.value) } />
|
||||||
<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={ levelId } onChange={ event => setLevelId(parseInt(event.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>
|
</select>
|
||||||
</select>
|
</Column>
|
||||||
</div>
|
</Flex>
|
||||||
</div>
|
<Grid columnCount={ 2 } overflow="auto" className="nitro-group-members-list-grid">
|
||||||
<div className="row row-cols-2 align-content-start g-0 w-100 members-list overflow-auto">
|
{ pageData.result.map((member, index) =>
|
||||||
{ pageData.result.map((member, index) =>
|
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<div key={ index } className={ 'col pb-1' + classNames({ ' pe-1': index % 2 === 0 }) }>
|
<Flex key={ index } gap={ 2 } alignItems="center" overflow="hidden" className="member-list-item bg-white rounded p-2">
|
||||||
<div className="list-member bg-white rounded d-flex text-black">
|
<div className="avatar-head cursor-pointer" onClick={ () => GetUserProfile(member.id) }>
|
||||||
<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 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)) } onClick={ () => toggleAdmin(member) } />
|
|
||||||
</div>
|
|
||||||
{ member.rank === GroupRank.REQUESTED && <div className="d-flex align-items-center">
|
|
||||||
<i className="icon cursor-pointer icon-accept" 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-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) } />
|
|
||||||
</div> }
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Column grow gap={ 1 }>
|
||||||
|
<Text bold small pointer onClick={ () => GetUserProfile(member.id) }>{ member.name }</Text>
|
||||||
|
{ (member.rank !== GroupRank.REQUESTED) &&
|
||||||
|
<Text small italics variant="muted">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</Text> }
|
||||||
|
</Column>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
{ (member.rank !== GroupRank.REQUESTED) &&
|
||||||
|
<Flex 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)) } onClick={ () => toggleAdmin(member) } />
|
||||||
|
</Flex> }
|
||||||
|
{ (member.rank === GroupRank.REQUESTED) &&
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Base pointer className="nitro-friends-spritesheet icon-accept" title={ LocalizeText('group.members.accept') } onClick={ () => acceptMembership(member) }></Base>
|
||||||
|
</Flex> }
|
||||||
|
{ (member.rank !== GroupRank.OWNER) && pageData.admin && (member.id !== GetSessionDataManager().userId) &&
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Base pointer className="nitro-friends-spritesheet icon-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) }></Base>
|
||||||
|
</Flex> }
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</Grid>
|
||||||
<div className="d-flex w-100 align-items-center">
|
<Flex gap={ 1 } justifyContent="between" alignItems="center">
|
||||||
<div>
|
<Button disabled={ (pageData.pageIndex === 0) } onClick={ previousPage }>
|
||||||
<button disabled={ pageData.pageIndex === 0 } className="btn btn-primary" onClick={ previousPage }><i className="fas fa-chevron-left" /></button>
|
<FontAwesomeIcon icon="chevron-left" />
|
||||||
</div>
|
</Button>
|
||||||
<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>
|
<Text small>
|
||||||
<div>
|
{ LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) }
|
||||||
<button disabled={ pageData.pageIndex === totalPages - 1 } className="btn btn-primary" onClick={ nextPage }><i className="fas fa-chevron-right" /></button>
|
</Text>
|
||||||
</div>
|
<Button disabled={ (pageData.pageIndex === (totalPages - 1)) } onClick={ nextPage }>
|
||||||
</div>
|
<FontAwesomeIcon icon="chevron-right" />
|
||||||
</NitroCardContentView>
|
</Button>
|
||||||
</> }
|
</Flex>
|
||||||
|
</NitroCardContentView>
|
||||||
</NitroCardView>
|
</NitroCardView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
|
||||||
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
|
||||||
import { GroupBadgePart } from '../../common/GroupBadgePart';
|
|
||||||
import { useGroupsContext } from '../../GroupsContext';
|
|
||||||
import { GroupsActions } from '../../reducers/GroupsReducer';
|
|
||||||
|
|
||||||
const POSITIONS: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
|
|
||||||
interface GroupSharedTabBadgeViewProps
|
|
||||||
{
|
|
||||||
skipDefault?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GroupSharedTabBadgeView: FC<GroupSharedTabBadgeViewProps> = props =>
|
|
||||||
{
|
|
||||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
|
||||||
const { badgeBases = null, badgeSymbols = null, badgePartColors = null, groupBadgeParts = null } = groupsState;
|
|
||||||
|
|
||||||
const { skipDefault = null } = props;
|
|
||||||
|
|
||||||
const [ editingIndex, setEditingIndex ] = useState<number>(0);
|
|
||||||
const [ isSelectingModel, setIsSelectingModel ] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(skipDefault || !badgeBases || !badgePartColors || groupBadgeParts) return;
|
|
||||||
|
|
||||||
const badgeParts: GroupBadgePart[] = [
|
|
||||||
new GroupBadgePart(GroupBadgePart.BASE, badgeBases[0].id, badgePartColors[0].id),
|
|
||||||
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id),
|
|
||||||
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id),
|
|
||||||
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id),
|
|
||||||
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id)
|
|
||||||
];
|
|
||||||
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_BADGE_PARTS,
|
|
||||||
payload: { badgeParts }
|
|
||||||
});
|
|
||||||
|
|
||||||
}, [ badgeBases, badgePartColors, groupBadgeParts ]);
|
|
||||||
|
|
||||||
const switchIndex = useCallback((index: number) =>
|
|
||||||
{
|
|
||||||
setIsSelectingModel(false);
|
|
||||||
setEditingIndex(index);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const selectPartProperty = useCallback((property: string, key: number) =>
|
|
||||||
{
|
|
||||||
const clonedBadgeParts = Array.from(groupBadgeParts);
|
|
||||||
|
|
||||||
clonedBadgeParts[editingIndex][property] = key;
|
|
||||||
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_BADGE_PARTS,
|
|
||||||
payload: {
|
|
||||||
badgeParts: clonedBadgeParts
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(property === 'key') setIsSelectingModel(false);
|
|
||||||
}, [ editingIndex, groupBadgeParts, dispatchGroupsState ]);
|
|
||||||
|
|
||||||
const getCompleteBadgeCode = useCallback(() =>
|
|
||||||
{
|
|
||||||
let code = '';
|
|
||||||
|
|
||||||
if(!groupBadgeParts) return code;
|
|
||||||
|
|
||||||
groupBadgeParts.forEach((badgePart) =>
|
|
||||||
{
|
|
||||||
if(badgePart.code) code = code + badgePart.code;
|
|
||||||
});
|
|
||||||
|
|
||||||
return code;
|
|
||||||
}, [ groupBadgeParts ]);
|
|
||||||
|
|
||||||
const getCurrentPart = useCallback((property: string) =>
|
|
||||||
{
|
|
||||||
return groupBadgeParts[editingIndex][property];
|
|
||||||
}, [ groupBadgeParts, editingIndex ]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="shared-tab-badge">
|
|
||||||
<div className="d-flex gap-2">
|
|
||||||
<div className="badge-preview flex-shrink-0 me-3">
|
|
||||||
<BadgeImageView badgeCode={ getCompleteBadgeCode() } isGroup={ true } />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="d-flex gap-2 align-items-center">
|
|
||||||
{ groupBadgeParts && groupBadgeParts.map((badgePart, partIndex) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<div key={ partIndex } className={ 'badge-preview flex-shrink-0 d-flex align-items-center justify-content-center cursor-pointer' + classNames({ ' active': editingIndex === partIndex }) } onClick={ () => switchIndex(partIndex) }>
|
|
||||||
{ badgePart.code && <BadgeImageView badgeCode={ badgePart.code } isGroup={ true } /> }
|
|
||||||
{ !badgePart.code && <i className="fas fa-plus text-primary h4 m-0" /> }
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
{ !isSelectingModel && groupBadgeParts && <div className="d-flex gap-2 mt-3">
|
|
||||||
<div className="badge-preview d-flex align-items-center justify-content-center flex-shrink-0 cursor-pointer" onClick={ () => setIsSelectingModel(true) }>
|
|
||||||
{ getCurrentPart('code') && <BadgeImageView badgeCode={ getCurrentPart('code') } isGroup={ true } /> }
|
|
||||||
{ !getCurrentPart('code') && <i className="fas fa-plus text-primary h4 m-0" /> }
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="row row-cols-3 g-0 gap-1 w-100 h-100 overflow-auto">
|
|
||||||
{ POSITIONS.map((position) =>
|
|
||||||
{
|
|
||||||
return <div key={ position } className={ 'position-swatch cursor-pointer' + classNames({ ' active': getCurrentPart('position') === position }) } onClick={ () => selectPartProperty('position', position) }></div>
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="row row-cols-8 g-0 gap-1 w-100 h-100 overflow-auto">
|
|
||||||
{ badgePartColors && badgePartColors.map((item, index) =>
|
|
||||||
{
|
|
||||||
return <div key={ index } className={ 'color-swatch cursor-pointer' + classNames({ ' active': item.id === getCurrentPart('color') }) } style={{ backgroundColor: '#' + item.color }} onClick={ () => selectPartProperty('color', item.id) }></div>
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> }
|
|
||||||
{ isSelectingModel && <div className="selection-list row row-cols-8 g-0 align-content-start gap-1 h-100 overflow-auto mt-3">
|
|
||||||
{ groupBadgeParts[editingIndex].type !== GroupBadgePart.BASE && <>
|
|
||||||
<div className="cursor-pointer badge-preview d-flex align-items-center justify-content-center" onClick={ () => selectPartProperty('key', 0) }>
|
|
||||||
<i className="fas fa-times text-primary h4 m-0" />
|
|
||||||
</div>
|
|
||||||
</> }
|
|
||||||
{ (groupBadgeParts[editingIndex].type === GroupBadgePart.BASE ? badgeBases : badgeSymbols).map((item, index) =>
|
|
||||||
{
|
|
||||||
return <div key={ index } className={ 'cursor-pointer badge-preview' + classNames({ ' bg-primary': groupBadgeParts[editingIndex].key === item.id }) } onClick={ () => selectPartProperty('key', item.id) }>
|
|
||||||
<BadgeImageView badgeCode={ GroupBadgePart.getCode(groupBadgeParts[editingIndex].type, item.id, groupBadgeParts[editingIndex].color, 4) } isGroup={ true } />
|
|
||||||
</div>
|
|
||||||
}) }
|
|
||||||
</div>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
};
|
|
@ -1,88 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
|
||||||
import { LocalizeText } from '../../../../api';
|
|
||||||
import { useGroupsContext } from '../../GroupsContext';
|
|
||||||
import { GroupsActions } from '../../reducers/GroupsReducer';
|
|
||||||
|
|
||||||
export const GroupSharedTabColorsView: FC<{}> = props =>
|
|
||||||
{
|
|
||||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
|
||||||
const { groupColors = null, groupColorsA = null, groupColorsB = null } = groupsState;
|
|
||||||
|
|
||||||
const [ selectingColorIndex, setSelectingColorIndex ] = useState<number>(0);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!groupColorsA || !groupColorsB || groupColors) return;
|
|
||||||
|
|
||||||
const colors: number[] = [
|
|
||||||
groupColorsA[0].id,
|
|
||||||
groupColorsB[0].id
|
|
||||||
];
|
|
||||||
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_COLORS,
|
|
||||||
payload: {
|
|
||||||
objectValues: colors
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}, [dispatchGroupsState, groupColors, groupColorsA, groupColorsB]);
|
|
||||||
|
|
||||||
const getGroupColor = useCallback((colorIndex: number) =>
|
|
||||||
{
|
|
||||||
if(colorIndex === 0) return groupColorsA.find(c => c.id === groupColors[colorIndex]).color;
|
|
||||||
|
|
||||||
return groupColorsB.find(c => c.id === groupColors[colorIndex]).color;
|
|
||||||
}, [ groupColors, groupColorsA, groupColorsB ]);
|
|
||||||
|
|
||||||
const selectColor = useCallback((colorId: number) =>
|
|
||||||
{
|
|
||||||
const clonedGroupColors = Array.from(groupColors);
|
|
||||||
|
|
||||||
clonedGroupColors[selectingColorIndex] = colorId;
|
|
||||||
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_COLORS,
|
|
||||||
payload: {
|
|
||||||
objectValues: clonedGroupColors
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [ selectingColorIndex, groupColors, dispatchGroupsState ]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="shared-tab-colors d-flex flex-column gap-2">
|
|
||||||
<div className="d-flex flex-column align-items-center">
|
|
||||||
<div className="fw-bold">{ LocalizeText('group.edit.color.guild.color') }</div>
|
|
||||||
{ groupColors && <div className="d-flex rounded border overflow-hidden">
|
|
||||||
<div className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(0) }}></div>
|
|
||||||
<div className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(1) }}></div>
|
|
||||||
</div> }
|
|
||||||
</div>
|
|
||||||
{ selectingColorIndex === 0 && <div>
|
|
||||||
<div className="fw-bold">{ LocalizeText('group.edit.color.primary.color') }</div>
|
|
||||||
<div className="d-flex align-items-center gap-2">
|
|
||||||
<div className="row row-cols-18 g-0 gap-1 w-100 h-100 overflow-auto">
|
|
||||||
{ groupColors && groupColorsA && groupColorsA.map((item, index) =>
|
|
||||||
{
|
|
||||||
return <div key={ index } className={ 'color-swatch cursor-pointer' + classNames({ ' active': groupColors[selectingColorIndex] === item.id }) } style={{ backgroundColor: '#' + item.color }} onClick={ () => selectColor(item.id) }></div>
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
<div><i className="fas fa-chevron-right h2 m-0 text-primary cursor-pointer" onClick={ () => setSelectingColorIndex(1) } /></div>
|
|
||||||
</div>
|
|
||||||
</div> }
|
|
||||||
{ selectingColorIndex === 1 && <div>
|
|
||||||
<div className="fw-bold text-end">{ LocalizeText('group.edit.color.secondary.color') }</div>
|
|
||||||
<div className="d-flex align-items-center gap-2">
|
|
||||||
<div><i className="fas fa-chevron-left h2 m-0 text-primary cursor-pointer" onClick={ () => setSelectingColorIndex(0) } /></div>
|
|
||||||
<div className="row row-cols-18 g-0 gap-1 w-100 h-100 overflow-auto">
|
|
||||||
{ groupColorsB && groupColorsB.map((item, index) =>
|
|
||||||
{
|
|
||||||
return <div key={ index } className={ 'color-swatch cursor-pointer' + classNames({ ' active': groupColors[selectingColorIndex] === item.id }) } style={{ backgroundColor: '#' + item.color }} onClick={ () => selectColor(item.id) }></div>
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,72 +0,0 @@
|
|||||||
import { FC, useCallback } from 'react';
|
|
||||||
import { CreateLinkEvent, LocalizeText } from '../../../../api';
|
|
||||||
import { useGroupsContext } from '../../GroupsContext';
|
|
||||||
import { GroupsActions } from '../../reducers/GroupsReducer';
|
|
||||||
|
|
||||||
interface GroupSharedTabIdentityViewProps
|
|
||||||
{
|
|
||||||
isCreator?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GroupSharedTabIdentityView: FC<GroupSharedTabIdentityViewProps> = props =>
|
|
||||||
{
|
|
||||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
|
||||||
const { groupName = '', groupDescription = '', groupHomeroomId = 0, availableRooms = null } = groupsState;
|
|
||||||
|
|
||||||
const { isCreator = false } = props;
|
|
||||||
|
|
||||||
const setName = useCallback((name: string) =>
|
|
||||||
{
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_NAME,
|
|
||||||
payload: {
|
|
||||||
stringValues: [ name ]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [ dispatchGroupsState ]);
|
|
||||||
|
|
||||||
const setDescription = useCallback((description: string) =>
|
|
||||||
{
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_DESCRIPTION,
|
|
||||||
payload: {
|
|
||||||
stringValues: [ description ]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [ dispatchGroupsState ]);
|
|
||||||
|
|
||||||
const setHomeroomId = useCallback((id: number) =>
|
|
||||||
{
|
|
||||||
dispatchGroupsState({
|
|
||||||
type: GroupsActions.SET_GROUP_HOMEROOM_ID,
|
|
||||||
payload: {
|
|
||||||
numberValues: [ id ]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [ dispatchGroupsState ]);
|
|
||||||
|
|
||||||
return (<div className="d-flex flex-column justify-content-center h-100">
|
|
||||||
<div className="form-group mb-2">
|
|
||||||
<label className="fw-bold">{ LocalizeText('group.edit.name') }</label>
|
|
||||||
<input type="text" className="form-control form-control-sm" value={ groupName } maxLength={ 29 } onChange={ (e) => setName(e.target.value) } />
|
|
||||||
</div>
|
|
||||||
<div className="form-group mb-2">
|
|
||||||
<label className="fw-bold">{ LocalizeText('group.edit.desc') }</label>
|
|
||||||
<input type="text" className="form-control form-control-sm" value={ groupDescription } maxLength={ 254 } onChange={ (e) => setDescription(e.target.value) } />
|
|
||||||
</div>
|
|
||||||
{ isCreator && <>
|
|
||||||
<div className="form-group mb-1">
|
|
||||||
<label className="fw-bold">{ LocalizeText('group.edit.base') }</label>
|
|
||||||
<select className="form-select form-select-sm" value={ groupHomeroomId } onChange={ (e) => setHomeroomId(Number(e.target.value)) }>
|
|
||||||
<option value={ 0 } disabled>{ LocalizeText('group.edit.base.select.room') }</option>
|
|
||||||
{ availableRooms && availableRooms.map((room, index) =>
|
|
||||||
{
|
|
||||||
return <option key={ index } value={ room.id }>{ room.name }</option>;
|
|
||||||
}) }
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="small mb-2">{ LocalizeText('group.edit.base.warning') }</div>
|
|
||||||
<div className="cursor-pointer text-decoration-underline text-center" onClick={ () => CreateLinkEvent('navigator/create') }>{ LocalizeText('group.createroom') }</div>
|
|
||||||
</> }
|
|
||||||
</div>);
|
|
||||||
};
|
|
@ -1,79 +0,0 @@
|
|||||||
.nitro-groups {
|
|
||||||
.badge-preview {
|
|
||||||
position: relative;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
background: $white;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
content: ' ';
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-top: 10px solid white;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
bottom: -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-color-swatch {
|
|
||||||
width: 30px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.shared-tab-badge {
|
|
||||||
.position-swatch {
|
|
||||||
position: relative;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
background: $white;
|
|
||||||
border: 2px solid $white;
|
|
||||||
box-shadow: inset 3px 3px rgba(0, 0, 0, .1);
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background: $primary;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-swatch {
|
|
||||||
position: relative;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border: 2px solid $white;
|
|
||||||
box-shadow: inset 3px 3px rgba(0, 0, 0, .1);
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selection-list {
|
|
||||||
height: 160px;
|
|
||||||
min-height: 160px;
|
|
||||||
max-height: 160px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.shared-tab-colors {
|
|
||||||
|
|
||||||
.color-swatch {
|
|
||||||
position: relative;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border: 2px solid $white;
|
|
||||||
box-shadow: inset 2px 2px rgba(0, 0, 0, .2);
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
7
src/components/groups/views/tabs/GroupSharedTabs.scss
Normal file
7
src/components/groups/views/tabs/GroupSharedTabs.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.nitro-groups {
|
||||||
|
|
||||||
|
.group-color-swatch {
|
||||||
|
width: 30px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
111
src/components/groups/views/tabs/GroupTabBadgeView.tsx
Normal file
111
src/components/groups/views/tabs/GroupTabBadgeView.tsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { GroupSaveBadgeComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { Column, Flex, Grid } from '../../../../common';
|
||||||
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
|
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
||||||
|
import { GroupBadgePart } from '../../common/GroupBadgePart';
|
||||||
|
import { useGroupsContext } from '../../GroupsContext';
|
||||||
|
import { GroupsActions } from '../../reducers/GroupsReducer';
|
||||||
|
import { GroupBadgeCreatorView } from '../GroupBadgeCreatorView';
|
||||||
|
|
||||||
|
const POSITIONS: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
|
||||||
|
interface GroupTabBadgeViewProps
|
||||||
|
{
|
||||||
|
skipDefault?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { skipDefault = null } = props;
|
||||||
|
const [ badgeParts, setBadgeParts ] = useState<GroupBadgePart[]>(null);
|
||||||
|
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||||
|
const { badgeBases = null, badgeSymbols = null, badgePartColors = null, groupId = -1, groupBadgeParts = null } = groupsState;
|
||||||
|
|
||||||
|
const getModifiedBadgeCode = () =>
|
||||||
|
{
|
||||||
|
if(!badgeParts || !badgeParts.length) return '';
|
||||||
|
|
||||||
|
let badgeCode = '';
|
||||||
|
|
||||||
|
badgeParts.forEach(part => (part.code && (badgeCode += part.code)));
|
||||||
|
|
||||||
|
return badgeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(groupBadgeParts && groupBadgeParts.length) return;
|
||||||
|
|
||||||
|
const badgeParts = [
|
||||||
|
new GroupBadgePart(GroupBadgePart.BASE, badgeBases[0].id, badgePartColors[0].id),
|
||||||
|
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id),
|
||||||
|
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id),
|
||||||
|
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id),
|
||||||
|
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id)
|
||||||
|
];
|
||||||
|
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_BADGE_PARTS,
|
||||||
|
payload: { badgeParts }
|
||||||
|
});
|
||||||
|
}, [ groupBadgeParts, badgeBases, badgePartColors, dispatchGroupsState ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setBadgeParts(groupBadgeParts);
|
||||||
|
}, [ groupBadgeParts ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if((groupId <= 0) || !badgeParts || !badgeParts.length || !groupBadgeParts || !groupBadgeParts.length || (badgeParts === groupBadgeParts)) return;
|
||||||
|
|
||||||
|
const badge = [];
|
||||||
|
|
||||||
|
badgeParts.forEach((part) =>
|
||||||
|
{
|
||||||
|
if(!part.code) return;
|
||||||
|
|
||||||
|
badge.push(part.key);
|
||||||
|
badge.push(part.color);
|
||||||
|
badge.push(part.position);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('send')
|
||||||
|
|
||||||
|
SendMessageHook(new GroupSaveBadgeComposer(groupId, badge));
|
||||||
|
}, [ groupId, badgeParts, groupBadgeParts ]);
|
||||||
|
|
||||||
|
// useEffect(() =>
|
||||||
|
// {
|
||||||
|
// if((groupId <= 0) || !badgeParts || !badgeParts.length || (badgeParts === groupBadgeParts)) return;
|
||||||
|
|
||||||
|
// const badge = [];
|
||||||
|
|
||||||
|
// badgeParts.forEach((part) =>
|
||||||
|
// {
|
||||||
|
// if(!part.code) return;
|
||||||
|
|
||||||
|
// badge.push(part.key);
|
||||||
|
// badge.push(part.color);
|
||||||
|
// badge.push(part.position);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// console.log('send')
|
||||||
|
|
||||||
|
// SendMessageHook(new GroupSaveBadgeComposer(groupId, badge));
|
||||||
|
// }, [ groupId, groupBadgeParts, badgeParts ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid overflow="hidden" gap={ 1 }>
|
||||||
|
<Column size={ 2 }>
|
||||||
|
<Flex center className="bg-muted rounded p-1">
|
||||||
|
<BadgeImageView badgeCode={ getModifiedBadgeCode() } isGroup={ true } />
|
||||||
|
</Flex>
|
||||||
|
</Column>
|
||||||
|
<Column size={ 10 } overflow="auto">
|
||||||
|
<GroupBadgeCreatorView badgeParts={ badgeParts } setBadgeParts={ setBadgeParts } />
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
81
src/components/groups/views/tabs/GroupTabColorsView.tsx
Normal file
81
src/components/groups/views/tabs/GroupTabColorsView.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { FC, useEffect } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { AutoGrid, Base, Column, Flex, Grid, Text } from '../../../../common';
|
||||||
|
import { useGroupsContext } from '../../GroupsContext';
|
||||||
|
import { GroupsActions } from '../../reducers/GroupsReducer';
|
||||||
|
|
||||||
|
export const GroupTabColorsView: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||||
|
const { groupColors = null, groupColorsA = null, groupColorsB = null } = groupsState;
|
||||||
|
|
||||||
|
const getGroupColor = (colorIndex: number) =>
|
||||||
|
{
|
||||||
|
if(colorIndex === 0) return groupColorsA.find(color => (color.id === groupColors[colorIndex])).color;
|
||||||
|
|
||||||
|
return groupColorsB.find(color => (color.id === groupColors[colorIndex])).color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectColor = (colorIndex: number, colorId: number) =>
|
||||||
|
{
|
||||||
|
const clonedGroupColors = Array.from(groupColors);
|
||||||
|
|
||||||
|
clonedGroupColors[colorIndex] = colorId;
|
||||||
|
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_COLORS,
|
||||||
|
payload: {
|
||||||
|
objectValues: clonedGroupColors
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!groupColorsA || !groupColorsB || groupColors) return;
|
||||||
|
|
||||||
|
const colors: number[] = [
|
||||||
|
groupColorsA[0].id,
|
||||||
|
groupColorsB[0].id
|
||||||
|
];
|
||||||
|
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_COLORS,
|
||||||
|
payload: {
|
||||||
|
objectValues: colors
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ dispatchGroupsState, groupColors, groupColorsA, groupColorsB ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid overflow="hidden">
|
||||||
|
<Column size={ 2 } gap={ 1 }>
|
||||||
|
<Text bold>{ LocalizeText('group.edit.color.guild.color') }</Text>
|
||||||
|
{ groupColors && (groupColors.length > 0) &&
|
||||||
|
<Flex overflow="hidden" className="rounded border">
|
||||||
|
<Base className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(0) }} />
|
||||||
|
<Base className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(1) }} />
|
||||||
|
</Flex> }
|
||||||
|
</Column>
|
||||||
|
<Column size={ 5 } gap={ 1 } overflow="hidden">
|
||||||
|
<Text bold>{ LocalizeText('group.edit.color.primary.color') }</Text>
|
||||||
|
<AutoGrid gap={ 1 } columnCount={ 7 } columnMinWidth={ 16 } columnMinHeight={ 16 }>
|
||||||
|
{ groupColors && groupColorsA && groupColorsA.map((item, index) =>
|
||||||
|
{
|
||||||
|
return <div key={ index } className={ 'group-badge-color-swatch cursor-pointer' + classNames({ ' active': (groupColors[0] === item.id) }) } style={{ backgroundColor: '#' + item.color }} onClick={ () => selectColor(0, item.id) }></div>
|
||||||
|
}) }
|
||||||
|
</AutoGrid>
|
||||||
|
</Column>
|
||||||
|
<Column size={ 5 } gap={ 1 } overflow="hidden">
|
||||||
|
<Text bold>{ LocalizeText('group.edit.color.secondary.color') }</Text>
|
||||||
|
<AutoGrid gap={ 1 } columnCount={ 7 } columnMinWidth={ 16 } columnMinHeight={ 16 }>
|
||||||
|
{ groupColorsB && groupColorsB.map((item, index) =>
|
||||||
|
{
|
||||||
|
return <div key={ index } className={ 'group-badge-color-swatch cursor-pointer' + classNames({ ' active': (groupColors[1] === item.id) }) } style={{ backgroundColor: '#' + item.color }} onClick={ () => selectColor(1, item.id) }></div>
|
||||||
|
}) }
|
||||||
|
</AutoGrid>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
75
src/components/groups/views/tabs/GroupTabIdentityView.tsx
Normal file
75
src/components/groups/views/tabs/GroupTabIdentityView.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { CreateLinkEvent, LocalizeText } from '../../../../api';
|
||||||
|
import { Column, Flex, Text } from '../../../../common';
|
||||||
|
import { useGroupsContext } from '../../GroupsContext';
|
||||||
|
import { GroupsActions } from '../../reducers/GroupsReducer';
|
||||||
|
|
||||||
|
interface GroupTabIdentityViewProps
|
||||||
|
{
|
||||||
|
isCreator?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GroupTabIdentityView: FC<GroupTabIdentityViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { isCreator = false } = props;
|
||||||
|
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||||
|
const { groupName = '', groupDescription = '', groupHomeroomId = 0, availableRooms = null } = groupsState;
|
||||||
|
|
||||||
|
const setName = (name: string) =>
|
||||||
|
{
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_NAME,
|
||||||
|
payload: {
|
||||||
|
stringValues: [ name ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDescription = (description: string) =>
|
||||||
|
{
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_DESCRIPTION,
|
||||||
|
payload: {
|
||||||
|
stringValues: [ description ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setHomeroomId = (id: number) =>
|
||||||
|
{
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_HOMEROOM_ID,
|
||||||
|
payload: {
|
||||||
|
numberValues: [ id ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
|
<Text className="col-3">{ LocalizeText('group.edit.name') }</Text>
|
||||||
|
<input type="text" className="form-control form-control-sm" value={ groupName } maxLength={ 29 } onChange={ event => setName(event.target.value) } />
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
|
<Text className="col-3">{ LocalizeText('group.edit.desc') }</Text>
|
||||||
|
<textarea className="form-control form-control-sm" value={ groupDescription } maxLength={ 254 } onChange={ event => setDescription(event.target.value) } />
|
||||||
|
</Flex>
|
||||||
|
{ isCreator &&
|
||||||
|
<Flex gap={ 1 }>
|
||||||
|
<Text className="col-3">{ LocalizeText('group.edit.base') }</Text>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<select className="form-select form-select-sm" value={ groupHomeroomId } onChange={ event => setHomeroomId(Number(event.target.value)) }>
|
||||||
|
<option value={ 0 } disabled>{ LocalizeText('group.edit.base.select.room') }</option>
|
||||||
|
{ availableRooms && availableRooms.map((room, index) => <option key={ index } value={ room.id }>{ room.name }</option>) }
|
||||||
|
</select>
|
||||||
|
<Text small>{ LocalizeText('group.edit.base.warning') }</Text>
|
||||||
|
</Column>
|
||||||
|
</Flex> }
|
||||||
|
</Column>
|
||||||
|
{ isCreator &&
|
||||||
|
<Text underline center fullWidth pointer onClick={ event => CreateLinkEvent('navigator/create') }>{ LocalizeText('group.createroom') }</Text> }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
63
src/components/groups/views/tabs/GroupTabSettingsView.tsx
Normal file
63
src/components/groups/views/tabs/GroupTabSettingsView.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api/utils/LocalizeText';
|
||||||
|
import { Column, Flex, HorizontalRule, Text } from '../../../../common';
|
||||||
|
import { useGroupsContext } from '../../GroupsContext';
|
||||||
|
import { GroupsActions } from '../../reducers/GroupsReducer';
|
||||||
|
|
||||||
|
const STATES: string[] = ['regular', 'exclusive', 'private'];
|
||||||
|
|
||||||
|
export const GroupTabSettingsView: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||||
|
const { groupState = null, groupCanMembersDecorate = false } = groupsState;
|
||||||
|
|
||||||
|
const setState = (state: number) =>
|
||||||
|
{
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_STATE,
|
||||||
|
payload: {
|
||||||
|
numberValues: [ state ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleCanMembersDecorate = () =>
|
||||||
|
{
|
||||||
|
dispatchGroupsState({
|
||||||
|
type: GroupsActions.SET_GROUP_CAN_MEMBERS_DECORATE,
|
||||||
|
payload: {
|
||||||
|
boolValues: [ !groupCanMembersDecorate ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column overflow="auto">
|
||||||
|
<Column>
|
||||||
|
{ STATES.map((state, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<Flex key={ index } alignItems="center" gap={ 1 }>
|
||||||
|
<input className="form-check-input" type="radio" name="groupState" checked={ (groupState === index) } onChange={ event => setState(index) } />
|
||||||
|
<Column gap={ 0 }>
|
||||||
|
<Flex gap={ 1 }>
|
||||||
|
<i className={ `icon icon-group-type-${index}` } />
|
||||||
|
<Text bold>{ LocalizeText(`group.edit.settings.type.${state}.label`) }</Text>
|
||||||
|
</Flex>
|
||||||
|
<Text>{ LocalizeText(`group.edit.settings.type.${state}.help`) }</Text>
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</Column>
|
||||||
|
<HorizontalRule />
|
||||||
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
|
<input className="form-check-input" type="checkbox" checked={ groupCanMembersDecorate } onChange={() => toggleCanMembersDecorate() } />
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Text bold>{ LocalizeText('group.edit.settings.rights.caption') }</Text>
|
||||||
|
<Text>{ LocalizeText('group.edit.settings.rights.members.help') }</Text>
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
.nitro-guide-tool {
|
.nitro-guide-tool {
|
||||||
width: 250px;
|
width: $nitro-guide-tool-width;
|
||||||
|
|
||||||
.duty-switch {
|
.duty-switch {
|
||||||
width: 38px;
|
width: 38px;
|
||||||
@ -19,6 +19,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-messages {
|
.chat-messages {
|
||||||
|
height: 200px;
|
||||||
|
|
||||||
.message-avatar {
|
.message-avatar {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -83,7 +83,7 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
|||||||
</Column> }
|
</Column> }
|
||||||
<Button variant="danger" disabled>{ LocalizeText('guide.help.common.report.link') }</Button>
|
<Button variant="danger" disabled>{ LocalizeText('guide.help.common.report.link') }</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Column fullHeight overflow="hidden" gap={ 1 } className="bg-muted rounded chat-messages p-2">
|
<Column overflow="hidden" gap={ 1 } className="bg-muted rounded chat-messages p-2">
|
||||||
<Column overflow="auto">
|
<Column overflow="auto">
|
||||||
{ messageGroups.map((group, index) =>
|
{ messageGroups.map((group, index) =>
|
||||||
{
|
{
|
||||||
|
@ -31,14 +31,17 @@ export const GuideToolUserFeedbackView: FC<GuideToolUserFeedbackViewProps> = pro
|
|||||||
<Text bold>{ LocalizeText('guide.help.request.user.feedback.closed.title') }</Text>
|
<Text bold>{ LocalizeText('guide.help.request.user.feedback.closed.title') }</Text>
|
||||||
<Text>{ LocalizeText('guide.help.request.user.feedback.closed.desc') }</Text>
|
<Text>{ LocalizeText('guide.help.request.user.feedback.closed.desc') }</Text>
|
||||||
</Column>
|
</Column>
|
||||||
<hr className="bg-dark m-0 mt-auto" />
|
{ userName && (userName.length > 0) &&
|
||||||
<Column>
|
<>
|
||||||
<Text center bold>{ LocalizeText('guide.help.request.user.feedback.question') }</Text>
|
<hr className="bg-dark m-0 mt-auto" />
|
||||||
<Flex gap={ 1 }>
|
<Column>
|
||||||
<Button fullWidth variant="success" onClick={ event => giveFeedback(true) }>{ LocalizeText('guide.help.request.user.feedback.positive.button') }</Button>
|
<Text center bold>{ LocalizeText('guide.help.request.user.feedback.question') }</Text>
|
||||||
<Button fullWidth variant="danger" onClick={ event => giveFeedback(false) }>{ LocalizeText('guide.help.request.user.feedback.negative.button') }</Button>
|
<Flex gap={ 1 }>
|
||||||
</Flex>
|
<Button fullWidth variant="success" onClick={ event => giveFeedback(true) }>{ LocalizeText('guide.help.request.user.feedback.positive.button') }</Button>
|
||||||
</Column>
|
<Button fullWidth variant="danger" onClick={ event => giveFeedback(false) }>{ LocalizeText('guide.help.request.user.feedback.negative.button') }</Button>
|
||||||
|
</Flex>
|
||||||
|
</Column>
|
||||||
|
</> }
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createContext, Dispatch, FC, ProviderProps, useContext } from 'react';
|
import { createContext, Dispatch, FC, ProviderProps, useContext } from 'react';
|
||||||
import { INavigatorAction, INavigatorState } from '../reducers/NavigatorReducer';
|
import { INavigatorAction, INavigatorState } from './reducers/NavigatorReducer';
|
||||||
|
|
||||||
export interface INavigatorContext
|
interface INavigatorContext
|
||||||
{
|
{
|
||||||
navigatorState: INavigatorState;
|
navigatorState: INavigatorState;
|
||||||
dispatchNavigatorState: Dispatch<INavigatorAction>;
|
dispatchNavigatorState: Dispatch<INavigatorAction>;
|
@ -4,7 +4,7 @@ import { CreateRoomSession, GetSessionDataManager } from '../../api';
|
|||||||
import { UpdateDoorStateEvent } from '../../events';
|
import { UpdateDoorStateEvent } from '../../events';
|
||||||
import { dispatchUiEvent } from '../../hooks';
|
import { dispatchUiEvent } from '../../hooks';
|
||||||
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { useNavigatorContext } from './context/NavigatorContext';
|
import { useNavigatorContext } from './NavigatorContext';
|
||||||
import { NavigatorActions } from './reducers/NavigatorReducer';
|
import { NavigatorActions } from './reducers/NavigatorReducer';
|
||||||
|
|
||||||
export const NavigatorMessageHandler: FC<{}> = props =>
|
export const NavigatorMessageHandler: FC<{}> = props =>
|
||||||
|
@ -9,7 +9,7 @@ import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/roo
|
|||||||
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||||
import { SendMessageHook } from '../../hooks/messages/message-event';
|
import { SendMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
||||||
import { NavigatorContextProvider } from './context/NavigatorContext';
|
import { NavigatorContextProvider } from './NavigatorContext';
|
||||||
import { NavigatorMessageHandler } from './NavigatorMessageHandler';
|
import { NavigatorMessageHandler } from './NavigatorMessageHandler';
|
||||||
import { initialNavigator, NavigatorActions, NavigatorReducer } from './reducers/NavigatorReducer';
|
import { initialNavigator, NavigatorActions, NavigatorReducer } from './reducers/NavigatorReducer';
|
||||||
import { NavigatorRoomCreatorView } from './views/creator/NavigatorRoomCreatorView';
|
import { NavigatorRoomCreatorView } from './views/creator/NavigatorRoomCreatorView';
|
||||||
|
@ -12,7 +12,7 @@ import { BatchUpdates } from '../../../../hooks';
|
|||||||
import { SendMessageHook } from '../../../../hooks/messages';
|
import { SendMessageHook } from '../../../../hooks/messages';
|
||||||
import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon';
|
import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
import { IRoomModel, RoomModels } from '../../common/RoomModels';
|
import { IRoomModel, RoomModels } from '../../common/RoomModels';
|
||||||
import { useNavigatorContext } from '../../context/NavigatorContext';
|
import { useNavigatorContext } from '../../NavigatorContext';
|
||||||
|
|
||||||
export const NavigatorRoomCreatorView: FC<{}> = props =>
|
export const NavigatorRoomCreatorView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ import { GetConfiguration, GetGroupInformation, GetSessionDataManager, LocalizeT
|
|||||||
import { Button } from '../../../../common/Button';
|
import { Button } from '../../../../common/Button';
|
||||||
import { Column } from '../../../../common/Column';
|
import { Column } from '../../../../common/Column';
|
||||||
import { Flex } from '../../../../common/Flex';
|
import { Flex } from '../../../../common/Flex';
|
||||||
import { Grid } from '../../../../common/Grid';
|
|
||||||
import { Text } from '../../../../common/Text';
|
import { Text } from '../../../../common/Text';
|
||||||
import { NavigatorEvent } from '../../../../events';
|
import { NavigatorEvent } from '../../../../events';
|
||||||
import { FloorplanEditorEvent } from '../../../../events/floorplan-editor/FloorplanEditorEvent';
|
import { FloorplanEditorEvent } from '../../../../events/floorplan-editor/FloorplanEditorEvent';
|
||||||
@ -17,7 +16,7 @@ import { SendMessageHook } from '../../../../hooks/messages';
|
|||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, UserProfileIconView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, UserProfileIconView } from '../../../../layout';
|
||||||
import { RoomThumbnailView } from '../../../../layout/room-thumbnail/RoomThumbnailView';
|
import { RoomThumbnailView } from '../../../../layout/room-thumbnail/RoomThumbnailView';
|
||||||
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
||||||
import { useNavigatorContext } from '../../context/NavigatorContext';
|
import { useNavigatorContext } from '../../NavigatorContext';
|
||||||
import { NavigatorActions } from '../../reducers/NavigatorReducer';
|
import { NavigatorActions } from '../../reducers/NavigatorReducer';
|
||||||
|
|
||||||
export class NavigatorRoomInfoViewProps
|
export class NavigatorRoomInfoViewProps
|
||||||
@ -160,15 +159,17 @@ export const NavigatorRoomInfoView: FC<NavigatorRoomInfoViewProps> = props =>
|
|||||||
}) }
|
}) }
|
||||||
</Flex> }
|
</Flex> }
|
||||||
</Column>
|
</Column>
|
||||||
<Grid maxContent gap={ 1 }>
|
<Column gap={ 1 }>
|
||||||
<i onClick={ () => processAction('set_home_room') } className={ 'g-col-6 flex-shrink-0 icon icon-house-small cursor-pointer' + classNames({ ' gray': homeRoomId !== roomInfoData.enteredGuestRoom.roomId }) } />
|
<Flex gap={ 1 }>
|
||||||
<FontAwesomeIcon icon="link" title={ LocalizeText('navigator.embed.caption') } className="cursor-pointer g-col-6" onClick={ event => dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK)) } />
|
<i onClick={ () => processAction('set_home_room') } className={ 'flex-shrink-0 icon icon-house-small cursor-pointer' + classNames({ ' gray': homeRoomId !== roomInfoData.enteredGuestRoom.roomId }) } />
|
||||||
|
<FontAwesomeIcon icon="link" title={ LocalizeText('navigator.embed.caption') } className="cursor-pointer" onClick={ event => dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK)) } />
|
||||||
|
</Flex>
|
||||||
{ hasPermission('settings') &&
|
{ hasPermission('settings') &&
|
||||||
<>
|
<Flex gap={ 1 }>
|
||||||
<FontAwesomeIcon icon="cogs" title={ LocalizeText('navigator.room.popup.info.room.settings') } className="g-col-6 cursor-pointer" onClick={ event => processAction('open_room_settings') } />
|
<FontAwesomeIcon icon="cogs" title={ LocalizeText('navigator.room.popup.info.room.settings') } className="cursor-pointer" onClick={ event => processAction('open_room_settings') } />
|
||||||
<FontAwesomeIcon icon="tools" title={ LocalizeText('open.floor.plan.editor') } className="g-col-6 cursor-pointer" onClick={ event => processAction('open_floorplan_editor') } />
|
<FontAwesomeIcon icon="tools" title={ LocalizeText('open.floor.plan.editor') } className="cursor-pointer" onClick={ event => processAction('open_floorplan_editor') } />
|
||||||
</> }
|
</Flex> }
|
||||||
</Grid>
|
</Column>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text overflow="auto">{ roomInfoData.enteredGuestRoom.description }</Text>
|
<Text overflow="auto">{ roomInfoData.enteredGuestRoom.description }</Text>
|
||||||
{ (roomInfoData.enteredGuestRoom.habboGroupId > 0) &&
|
{ (roomInfoData.enteredGuestRoom.habboGroupId > 0) &&
|
||||||
|
@ -5,7 +5,7 @@ import { Flex } from '../../../../common/Flex';
|
|||||||
import { Text } from '../../../../common/Text';
|
import { Text } from '../../../../common/Text';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
import { RoomThumbnailView } from '../../../../layout/room-thumbnail/RoomThumbnailView';
|
import { RoomThumbnailView } from '../../../../layout/room-thumbnail/RoomThumbnailView';
|
||||||
import { useNavigatorContext } from '../../context/NavigatorContext';
|
import { useNavigatorContext } from '../../NavigatorContext';
|
||||||
|
|
||||||
export class NavigatorRoomLinkViewProps
|
export class NavigatorRoomLinkViewProps
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { RoomDeleteComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../../api';
|
import { LocalizeText } from '../../../../../api';
|
||||||
import { Base } from '../../../../../common/Base';
|
import { Base } from '../../../../../common/Base';
|
||||||
import { Flex } from '../../../../../common/Flex';
|
import { Flex } from '../../../../../common/Flex';
|
||||||
import { Text } from '../../../../../common/Text';
|
import { Text } from '../../../../../common/Text';
|
||||||
|
import { SendMessageHook } from '../../../../../hooks';
|
||||||
|
import { NotificationUtilities } from '../../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { GetMaxVisitorsList } from '../../../common/RoomSettingsUtils';
|
import { GetMaxVisitorsList } from '../../../common/RoomSettingsUtils';
|
||||||
import { useNavigatorContext } from '../../../context/NavigatorContext';
|
import { useNavigatorContext } from '../../../NavigatorContext';
|
||||||
import { NavigatorRoomSettingsTabViewProps } from './NavigatorRoomSettingsTabViewProps.types';
|
import { NavigatorRoomSettingsTabViewProps } from './NavigatorRoomSettingsTabViewProps.types';
|
||||||
|
|
||||||
const DESC_MAX_LENGTH = 255;
|
const DESC_MAX_LENGTH = 255;
|
||||||
@ -17,6 +20,15 @@ export const NavigatorRoomSettingsBasicTabView: FC<NavigatorRoomSettingsTabViewP
|
|||||||
const { navigatorState = null } = useNavigatorContext();
|
const { navigatorState = null } = useNavigatorContext();
|
||||||
const { categories = null } = navigatorState;
|
const { categories = null } = navigatorState;
|
||||||
|
|
||||||
|
const deleteRoom = () =>
|
||||||
|
{
|
||||||
|
NotificationUtilities.confirm(LocalizeText('navigator.roomsettings.deleteroom.confirm.message'), () =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new RoomDeleteComposer(roomSettingsData.roomId));
|
||||||
|
},
|
||||||
|
null, null, null, LocalizeText('navigator.roomsettings.deleteroom.confirm.title'));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex alignItems="center" gap={ 1 }>
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
@ -58,7 +70,7 @@ export const NavigatorRoomSettingsBasicTabView: FC<NavigatorRoomSettingsTabViewP
|
|||||||
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowWalkthrough } onChange={ event => handleChange('allow_walkthrough', event.target.checked) } />
|
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowWalkthrough } onChange={ event => handleChange('allow_walkthrough', event.target.checked) } />
|
||||||
<Text>{ LocalizeText('navigator.roomsettings.allow_walk_through') }</Text>
|
<Text>{ LocalizeText('navigator.roomsettings.allow_walk_through') }</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text variant="danger" underline bold pointer className="d-flex justify-content-center align-items-center gap-1 mt-2">
|
<Text variant="danger" underline bold pointer className="d-flex justify-content-center align-items-center gap-1" onClick={ deleteRoom }>
|
||||||
<FontAwesomeIcon icon="times" />
|
<FontAwesomeIcon icon="times" />
|
||||||
{ LocalizeText('navigator.roomsettings.delete') }
|
{ LocalizeText('navigator.roomsettings.delete') }
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -4,7 +4,7 @@ import { LocalizeText } from '../../../../api';
|
|||||||
import { Button } from '../../../../common/Button';
|
import { Button } from '../../../../common/Button';
|
||||||
import { Flex } from '../../../../common/Flex';
|
import { Flex } from '../../../../common/Flex';
|
||||||
import { SearchFilterOptions } from '../../common/SearchFilterOptions';
|
import { SearchFilterOptions } from '../../common/SearchFilterOptions';
|
||||||
import { useNavigatorContext } from '../../context/NavigatorContext';
|
import { useNavigatorContext } from '../../NavigatorContext';
|
||||||
|
|
||||||
export interface NavigatorSearchViewProps
|
export interface NavigatorSearchViewProps
|
||||||
{
|
{
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import { FC, useMemo } from 'react';
|
import { FC, useMemo } from 'react';
|
||||||
import { NitroLayoutFlex } from '../..';
|
import { Flex, FlexProps } from '../../../common';
|
||||||
import { NitroCardSubHeaderViewProps } from './NitroCardSubHeaderView.types';
|
|
||||||
|
|
||||||
export const NitroCardSubHeaderView: FC<NitroCardSubHeaderViewProps> = props =>
|
export const NitroCardSubHeaderView: FC<FlexProps> = props =>
|
||||||
{
|
{
|
||||||
const { className = '', ...rest } = props;
|
const { justifyContent = 'center', classNames = [], ...rest } = props;
|
||||||
|
|
||||||
const getClassName = useMemo(() =>
|
const getClassNames = useMemo(() =>
|
||||||
{
|
{
|
||||||
let newClassName = 'container-fluid bg-muted justify-content-center py-1';
|
const newClassNames: string[] = [ 'container-fluid', 'bg-muted', 'p-1' ];
|
||||||
|
|
||||||
if(className && className.length) newClassName += ` ${ className }`;
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
return newClassName;
|
return newClassNames;
|
||||||
}, [ className ]);
|
}, [ classNames ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutFlex className={ getClassName } { ...rest } />
|
<Flex justifyContent={ justifyContent } classNames={ getClassNames } { ...rest } />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,16 @@
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
|
||||||
|
&.group-badge {
|
||||||
|
width: 39px;
|
||||||
|
height: 39px;
|
||||||
|
|
||||||
|
&.scale-2 {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.badge-information {
|
.badge-information {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -18,7 +18,6 @@ export const BadgeImageView: FC<BadgeImageViewProps> = props =>
|
|||||||
{
|
{
|
||||||
const { badgeCode = null, isGroup = false, showInfo = false, customTitle = null, isGrayscale = false, scale = 1, classNames = [], style = {}, children = null, ...rest } = props;
|
const { badgeCode = null, isGroup = false, showInfo = false, customTitle = null, isGrayscale = false, scale = 1, classNames = [], style = {}, children = null, ...rest } = props;
|
||||||
const [ badgeUrl, setBadgeUrl ] = useState<string>('');
|
const [ badgeUrl, setBadgeUrl ] = useState<string>('');
|
||||||
const [ isListening, setIsListening ] = useState<boolean>(true);
|
|
||||||
|
|
||||||
const getScaleClass = useMemo(() =>
|
const getScaleClass = useMemo(() =>
|
||||||
{
|
{
|
||||||
@ -39,6 +38,8 @@ export const BadgeImageView: FC<BadgeImageViewProps> = props =>
|
|||||||
{
|
{
|
||||||
const newClassNames: string[] = [ 'badge-image' ];
|
const newClassNames: string[] = [ 'badge-image' ];
|
||||||
|
|
||||||
|
if(isGroup) newClassNames.push('group-badge');
|
||||||
|
|
||||||
if(isGrayscale) newClassNames.push('grayscale');
|
if(isGrayscale) newClassNames.push('grayscale');
|
||||||
|
|
||||||
if((scale !== 1) && getScaleClass.length) newClassNames.push(getScaleClass);
|
if((scale !== 1) && getScaleClass.length) newClassNames.push(getScaleClass);
|
||||||
@ -46,7 +47,7 @@ export const BadgeImageView: FC<BadgeImageViewProps> = props =>
|
|||||||
if(classNames.length) newClassNames.push(...classNames);
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
return newClassNames;
|
return newClassNames;
|
||||||
}, [ classNames, isGrayscale, scale, getScaleClass ]);
|
}, [ classNames, isGroup, isGrayscale, scale, getScaleClass ]);
|
||||||
|
|
||||||
const getStyle = useMemo(() =>
|
const getStyle = useMemo(() =>
|
||||||
{
|
{
|
||||||
@ -62,42 +63,28 @@ export const BadgeImageView: FC<BadgeImageViewProps> = props =>
|
|||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!badgeCode || !badgeCode.length) return;
|
if(!badgeCode || !badgeCode.length) return;
|
||||||
|
|
||||||
const existing = (isGroup) ? GetSessionDataManager().loadGroupBadgeImage(badgeCode) : GetSessionDataManager().loadBadgeImage(badgeCode);
|
let didSetBadge = false;
|
||||||
|
|
||||||
const onBadgeImageReadyEvent = (event: BadgeImageReadyEvent) =>
|
const onBadgeImageReadyEvent = (event: BadgeImageReadyEvent) =>
|
||||||
{
|
{
|
||||||
if(event.badgeId !== badgeCode) return;
|
if(event.badgeId !== badgeCode) return;
|
||||||
|
|
||||||
const nitroSprite = new NitroSprite(event.image);
|
setBadgeUrl(TextureUtils.generateImageUrl(new NitroSprite(event.image)));
|
||||||
setBadgeUrl(TextureUtils.generateImageUrl(nitroSprite));
|
|
||||||
|
|
||||||
if(isListening)
|
didSetBadge = true;
|
||||||
{
|
|
||||||
GetSessionDataManager().events.removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
GetSessionDataManager().events.removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
||||||
setIsListening(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!existing)
|
GetSessionDataManager().events.addEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
||||||
{
|
|
||||||
GetSessionDataManager().events.addEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const image = (isGroup) ? GetSessionDataManager().getGroupBadgeImage(badgeCode) : GetSessionDataManager().getBadgeImage(badgeCode);
|
|
||||||
const nitroSprite = new NitroSprite(image);
|
|
||||||
setBadgeUrl(TextureUtils.generateImageUrl(nitroSprite));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (() =>
|
const texture = isGroup ? GetSessionDataManager().getGroupBadgeImage(badgeCode) : GetSessionDataManager().getBadgeImage(badgeCode);
|
||||||
{
|
|
||||||
if(isListening)
|
if(texture && !didSetBadge) setBadgeUrl(TextureUtils.generateImageUrl(new NitroSprite(texture)));
|
||||||
{
|
|
||||||
GetSessionDataManager().events.removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
return () => GetSessionDataManager().events.removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
||||||
}
|
}, [ badgeCode, isGroup ]);
|
||||||
});
|
|
||||||
}, [ badgeCode, isGroup, isListening ]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Base classNames={ getClassNames } style={ getStyle } { ...rest }>
|
<Base classNames={ getClassNames } style={ getStyle } { ...rest }>
|
||||||
|
Loading…
Reference in New Issue
Block a user