⚠️ WiP Group badges

This commit is contained in:
duckietm 2025-05-02 16:54:02 +02:00
parent 313c814b1e
commit 2269b20f35
8 changed files with 148 additions and 57 deletions

View File

@ -1,4 +1,3 @@
export class GroupBadgePart export class GroupBadgePart
{ {
public static BASE: string = 'b'; public static BASE: string = 'b';

View File

@ -47,15 +47,10 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
newStyle.backgroundImage = `url(${ badgeUrl })`; newStyle.backgroundImage = `url(${ badgeUrl })`;
// Remove inline width and height to let SCSS control the size
// newStyle.width = imageElement.width;
// newStyle.height = imageElement.height;
if(scale !== 1) if(scale !== 1)
{ {
newStyle.transform = `scale(${ scale })`; newStyle.transform = `scale(${ scale })`;
if(!(scale % 1)) newStyle.imageRendering = 'pixelated'; if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
// If scaling, adjust the dimensions in SCSS instead
} }
} }
@ -111,6 +106,8 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
// Fallback: Try fetching directly from session data // Fallback: Try fetching directly from session data
let texture = isGroup ? GetSessionDataManager().getGroupBadgeImage(badgeCode) : GetSessionDataManager().getBadgeImage(badgeCode); let texture = isGroup ? GetSessionDataManager().getGroupBadgeImage(badgeCode) : GetSessionDataManager().getBadgeImage(badgeCode);
console.log('LayoutBadgeImageView: getGroupBadgeImage result', { badgeCode, texture: texture ? 'exists' : 'null', isGroup });
if(texture) if(texture)
{ {
const sprite = new NitroSprite(texture); const sprite = new NitroSprite(texture);
@ -162,6 +159,12 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
return null; // Optionally render a loading placeholder return null; // Optionally render a loading placeholder
} }
if(!imageElement)
{
console.log('LayoutBadgeImageView: Rendering fallback placeholder', { badgeCode, isGroup });
return <Base classNames={ getClassNames } style={ { ...style, backgroundColor: '#d3d3d3' } } { ...rest } />; // Gray square as fallback
}
console.log('LayoutBadgeImageView: Rendering badge', { badgeCode, hasImage: !!imageElement, isGroup }); console.log('LayoutBadgeImageView: Rendering badge', { badgeCode, hasImage: !!imageElement, isGroup });
return ( return (

View File

@ -1,4 +1,4 @@
import { GroupPurchasedEvent, GroupSettingsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; import { GroupPurchasedEvent, ILinkEventTracker } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { AddEventLinkTracker, RemoveLinkEventTracker, SendMessageComposer, TryVisitRoom } from '../../api'; import { AddEventLinkTracker, RemoveLinkEventTracker, SendMessageComposer, TryVisitRoom } from '../../api';
import { useGroup, useMessageEvent } from '../../hooks'; import { useGroup, useMessageEvent } from '../../hooks';
@ -10,8 +10,15 @@ import { GroupMembersView } from './views/GroupMembersView';
export const GroupsView: FC<{}> = props => export const GroupsView: FC<{}> = props =>
{ {
const [ isCreatorVisible, setCreatorVisible ] = useState<boolean>(false); const [ isCreatorVisible, setCreatorVisible ] = useState<boolean>(false);
const [ currentBadgeCode, setCurrentBadgeCode ] = useState<string>(''); // State to hold the current badge code
const {} = useGroup(); const {} = useGroup();
// Callback to receive the updated badge code from GroupCreatorView
const handleBadgeCodeUpdate = (badgeCode: string) => {
console.log('GroupsView: Received updated badge code', { badgeCode });
setCurrentBadgeCode(badgeCode);
};
useMessageEvent<GroupPurchasedEvent>(GroupPurchasedEvent, event => useMessageEvent<GroupPurchasedEvent>(GroupPurchasedEvent, event =>
{ {
const parser = event.getParser(); const parser = event.getParser();
@ -53,11 +60,11 @@ export const GroupsView: FC<{}> = props =>
return ( return (
<> <>
{ isCreatorVisible && { isCreatorVisible &&
<GroupCreatorView onClose={ () => setCreatorVisible(false) } /> } <GroupCreatorView onClose={ () => setCreatorVisible(false) } onBadgeCodeUpdate={ handleBadgeCodeUpdate } /> }
{ !isCreatorVisible && { !isCreatorVisible &&
<GroupManagerView /> } <GroupManagerView /> }
<GroupMembersView /> <GroupMembersView />
<GroupInformationStandaloneView /> <GroupInformationStandaloneView badgeCode={ currentBadgeCode } isCreating={ isCreatorVisible } />
</> </>
); );
}; };

View File

@ -1,4 +1,4 @@
import { Dispatch, FC, SetStateAction, useState } from 'react'; import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react';
import { FaPlus, FaTimes } from 'react-icons/fa'; import { FaPlus, FaTimes } from 'react-icons/fa';
import { GroupBadgePart } from '../../../api'; import { GroupBadgePart } from '../../../api';
import { Base, Column, Flex, Grid, LayoutBadgeImageView } from '../../../common'; import { Base, Column, Flex, Grid, LayoutBadgeImageView } from '../../../common';
@ -6,15 +6,16 @@ import { useGroup } from '../../../hooks';
interface GroupBadgeCreatorViewProps interface GroupBadgeCreatorViewProps
{ {
badgeParts: GroupBadgePart[]; badgeParts: GroupBadgePart[] | null;
setBadgeParts: Dispatch<SetStateAction<GroupBadgePart[]>>; setBadgeParts: Dispatch<SetStateAction<GroupBadgePart[]>>;
onBadgeCodeUpdate?: (badgeCode: string) => void;
} }
const POSITIONS: number[] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]; const POSITIONS: number[] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props => export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props =>
{ {
const { badgeParts = [], setBadgeParts = null } = props; const { badgeParts = [], setBadgeParts = null, onBadgeCodeUpdate = null } = props;
const [ selectedIndex, setSelectedIndex ] = useState<number>(-1); const [ selectedIndex, setSelectedIndex ] = useState<number>(-1);
const { groupCustomize = null } = useGroup(); const { groupCustomize = null } = useGroup();
@ -27,23 +28,61 @@ export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props =>
setBadgeParts(newBadgeParts); setBadgeParts(newBadgeParts);
if(property === 'key') setSelectedIndex(-1); if(property === 'key') setSelectedIndex(-1);
} };
if(!badgeParts || !badgeParts.length) return null; // Helper function to generate a full group badge code with all parts
const getFullBadgeCode = (index: number): string => {
if (!badgeParts) return '';
let badgeCode = '';
badgeParts.forEach(part => {
if (part.code && part.code.length > 0) {
badgeCode += part.code;
}
// Exclude unset symbols (key: 0) from the badge code
});
console.log('GroupBadgeCreatorView: Computed full badge code', { badgeCode });
return badgeCode;
};
// Debug the contents of badgeBases and badgeSymbols
console.log('GroupBadgeCreatorView: badgeBases', groupCustomize?.badgeBases);
console.log('GroupBadgeCreatorView: badgeSymbols', groupCustomize?.badgeSymbols);
// Compute the full badge code
const fullBadgeCode = getFullBadgeCode(0);
useEffect(() => {
console.log('GroupBadgeCreatorView: useEffect triggered', { fullBadgeCode, hasOnBadgeCodeUpdate: !!onBadgeCodeUpdate });
if (onBadgeCodeUpdate && fullBadgeCode) {
console.log('GroupBadgeCreatorView: Propagating badge code to parent', { fullBadgeCode });
onBadgeCodeUpdate(fullBadgeCode);
}
}, [fullBadgeCode, onBadgeCodeUpdate]);
// Early return after all hooks are called
if (!badgeParts || !badgeParts.length) return null;
return ( return (
<> <>
{ ((selectedIndex < 0) && badgeParts && (badgeParts.length > 0)) && badgeParts.map((part, index) => { ((selectedIndex < 0) && badgeParts && (badgeParts.length > 0)) && badgeParts.map((part, index) =>
{ {
const badgeCode = badgeParts[index].code;
console.log('GroupBadgeCreatorView: Rendering badge part', { index, badgeCode, fullBadgeCode, part });
return ( return (
<Flex key={ index } alignItems="center" justifyContent="between" gap={ 2 } className="bg-muted rounded px-2 py-1"> <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) }> <Flex pointer center className="bg-muted rounded p-1" onClick={ event => setSelectedIndex(index) }>
{ (badgeParts[index].code && (badgeParts[index].code.length > 0)) && { (badgeCode && badgeCode.length > 0) ? (
<LayoutBadgeImageView badgeCode={ badgeParts[index].code } isGroup={ true } /> } <LayoutBadgeImageView badgeCode={ badgeCode } isGroup={ true } />
{ (!badgeParts[index].code || !badgeParts[index].code.length) && ) : (
<Flex center className="badge-image group-badge"> <Flex center className="badge-image group-badge">
<FaPlus className="fa-icon" /> <FaPlus className="fa-icon" />
</Flex> } </Flex>
)}
</Flex> </Flex>
{ (part.type !== GroupBadgePart.BASE) && { (part.type !== GroupBadgePart.BASE) &&
<Grid gap={ 1 } columnCount={ 3 }> <Grid gap={ 1 } columnCount={ 3 }>
@ -71,13 +110,16 @@ export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props =>
</Column> } </Column> }
{ ((badgeParts[selectedIndex].type === GroupBadgePart.BASE) ? groupCustomize.badgeBases : groupCustomize.badgeSymbols).map((item, index) => { ((badgeParts[selectedIndex].type === GroupBadgePart.BASE) ? groupCustomize.badgeBases : groupCustomize.badgeSymbols).map((item, index) =>
{ {
const badgeCode = GroupBadgePart.getCode(badgeParts[selectedIndex].type, item.id, badgeParts[selectedIndex].color, 4);
console.log('GroupBadgeCreatorView: Rendering template badge', { type: badgeParts[selectedIndex].type, id: item.id, badgeCode, color: badgeParts[selectedIndex].color, position: badgeParts[selectedIndex].position, item });
return ( return (
<Column key={ index } pointer center className="bg-muted rounded p-1" onClick={ event => setPartProperty(selectedIndex, 'key', item.id) }> <Column key={ index } pointer center className="bg-muted rounded p-1" onClick={ event => setPartProperty(selectedIndex, 'key', item.id) }>
<LayoutBadgeImageView badgeCode={ GroupBadgePart.getCode(badgeParts[selectedIndex].type, item.id, badgeParts[selectedIndex].color, 4) } isGroup={ true } /> <LayoutBadgeImageView badgeCode={ badgeCode } isGroup={ true } />
</Column> </Column>
); );
}) } }) }
</Grid> } </Grid> }
</> </>
); );
} };

View File

@ -11,18 +11,20 @@ import { GroupTabIdentityView } from './tabs/GroupTabIdentityView';
interface GroupCreatorViewProps interface GroupCreatorViewProps
{ {
onClose: () => void; onClose: () => void;
onBadgeCodeUpdate?: (badgeCode: string) => void; // Callback to propagate badge code to parent
} }
const TABS: number[] = [ 1, 2, 3, 4 ]; const TABS: number[] = [ 1, 2, 3, 4 ];
export const GroupCreatorView: FC<GroupCreatorViewProps> = props => export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
{ {
const { onClose = null } = props; const { onClose = null, onBadgeCodeUpdate = null } = props;
const [ currentTab, setCurrentTab ] = useState<number>(1); const [ currentTab, setCurrentTab ] = useState<number>(1);
const [ closeAction, setCloseAction ] = useState<{ action: () => boolean }>(null); const [ closeAction, setCloseAction ] = useState<{ action: () => boolean }>(null);
const [ groupData, setGroupData ] = useState<IGroupData>(null); const [ groupData, setGroupData ] = useState<IGroupData>(null);
const [ availableRooms, setAvailableRooms ] = useState<{ id: number, name: string }[]>(null); const [ availableRooms, setAvailableRooms ] = useState<{ id: number, name: string }[]>(null);
const [ purchaseCost, setPurchaseCost ] = useState<number>(0); const [ purchaseCost, setPurchaseCost ] = useState<number>(0);
const [ currentBadgeCode, setCurrentBadgeCode ] = useState<string>(''); // State to hold the current badge code
const onCloseClose = () => const onCloseClose = () =>
{ {
@ -85,6 +87,16 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
setCurrentTab(value => (value === 4 ? value : value + 1)); setCurrentTab(value => (value === 4 ? value : value + 1));
} }
// Callback to receive the updated badge code from GroupTabBadgeView
const handleBadgeCodeUpdate = (badgeCode: string) => {
console.log('GroupCreatorView: Received updated badge code', { badgeCode });
setCurrentBadgeCode(badgeCode);
// Propagate the badge code to the parent
if (onBadgeCodeUpdate) {
onBadgeCodeUpdate(badgeCode);
}
};
useMessageEvent<GroupBuyDataEvent>(GroupBuyDataEvent, event => useMessageEvent<GroupBuyDataEvent>(GroupBuyDataEvent, event =>
{ {
const parser = event.getParser(); const parser = event.getParser();
@ -143,7 +155,7 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
{ (currentTab === 1) && { (currentTab === 1) &&
<GroupTabIdentityView groupData={ groupData } setGroupData={ setGroupData } setCloseAction={ setCloseAction } onClose={ null } isCreator={ true } availableRooms={ availableRooms } /> } <GroupTabIdentityView groupData={ groupData } setGroupData={ setGroupData } setCloseAction={ setCloseAction } onClose={ null } isCreator={ true } availableRooms={ availableRooms } /> }
{ (currentTab === 2) && { (currentTab === 2) &&
<GroupTabBadgeView groupData={ groupData } setGroupData={ setGroupData } setCloseAction={ setCloseAction } /> } <GroupTabBadgeView groupData={ groupData } setGroupData={ setGroupData } setCloseAction={ setCloseAction } onBadgeCodeUpdate={ handleBadgeCodeUpdate } /> }
{ (currentTab === 3) && { (currentTab === 3) &&
<GroupTabColorsView groupData={ groupData } setGroupData={ setGroupData } setCloseAction={ setCloseAction } /> } <GroupTabColorsView groupData={ groupData } setGroupData={ setGroupData } setCloseAction={ setCloseAction } /> }
{ (currentTab === 4) && { (currentTab === 4) &&
@ -161,4 +173,4 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );
}; };

View File

@ -5,25 +5,35 @@ import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../
import { useMessageEvent } from '../../../hooks'; import { useMessageEvent } from '../../../hooks';
import { GroupInformationView } from './GroupInformationView'; import { GroupInformationView } from './GroupInformationView';
export const GroupInformationStandaloneView: FC<{}> = props => interface GroupInformationStandaloneViewProps
{ {
badgeCode?: string;
isCreating?: boolean;
}
export const GroupInformationStandaloneView: FC<GroupInformationStandaloneViewProps> = props =>
{
const { badgeCode = null, isCreating = false } = props;
const [ groupInformation, setGroupInformation ] = useState<GroupInformationParser>(null); const [ groupInformation, setGroupInformation ] = useState<GroupInformationParser>(null);
useMessageEvent<GroupInformationEvent>(GroupInformationEvent, event => useMessageEvent<GroupInformationEvent>(GroupInformationEvent, event =>
{ {
// Skip updates during group creation
if (isCreating) return;
const parser = event.getParser(); const parser = event.getParser();
if((groupInformation && (groupInformation.id === parser.id)) || parser.flag) setGroupInformation(parser); if((groupInformation && (groupInformation.id === parser.id)) || parser.flag) setGroupInformation(parser);
}); });
if(!groupInformation) return null; if(!groupInformation && !isCreating) return null;
return ( return (
<NitroCardView className="nitro-group-information-standalone" theme="primary-slim"> <NitroCardView className="nitro-group-information-standalone" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('group.window.title') } onCloseClick={ event => setGroupInformation(null) } /> <NitroCardHeaderView headerText={ LocalizeText('group.window.title') } onCloseClick={ event => setGroupInformation(null) } />
<NitroCardContentView> <NitroCardContentView>
<GroupInformationView groupInformation={ groupInformation } onClose={ () => setGroupInformation(null) } /> <GroupInformationView groupInformation={ groupInformation } onClose={ () => setGroupInformation(null) } badgeCode={ badgeCode } />
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );
}; };

View File

@ -11,11 +11,12 @@ interface GroupInformationViewProps extends GridProps
groupInformation: GroupInformationParser; groupInformation: GroupInformationParser;
onJoin?: () => void; onJoin?: () => void;
onClose?: () => void; onClose?: () => void;
badgeCode?: string; // Optional badge code prop
} }
export const GroupInformationView: FC<GroupInformationViewProps> = props => export const GroupInformationView: FC<GroupInformationViewProps> = props =>
{ {
const { groupInformation = null, onClose = null, overflow = 'hidden', ...rest } = props; const { groupInformation = null, onClose = null, badgeCode = null, overflow = 'hidden', ...rest } = props;
const { showConfirm = null } = useNotification(); const { showConfirm = null } = useNotification();
const isRealOwner = (groupInformation && (groupInformation.ownerName === GetSessionDataManager().userName)); const isRealOwner = (groupInformation && (groupInformation.ownerName === GetSessionDataManager().userName));
@ -99,11 +100,16 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
if(!groupInformation) return null; if(!groupInformation) return null;
// Use badgeCode if provided (during group creation), otherwise fall back to groupInformation.badge
const displayBadgeCode = badgeCode || groupInformation.badge;
return ( return (
<Grid overflow={ overflow } { ...rest }> <Grid overflow={ overflow } { ...rest }>
<Column center size={ 3 } overflow="hidden"> <Column center size={ 3 } overflow="hidden">
<Flex alignItems="center" overflow="hidden" className="group-badge"> <Flex alignItems="center" overflow="hidden" className="group-badge">
<LayoutBadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } scale={ 2 } /> { displayBadgeCode && (
<LayoutBadgeImageView badgeCode={ displayBadgeCode } isGroup={ true } scale={ 2 } />
)}
</Flex> </Flex>
<Column alignItems="center" gap={ 1 }> <Column alignItems="center" gap={ 1 }>
<Text small underline pointer onClick={ () => handleAction('members') }>{ LocalizeText('group.membercount', [ 'totalMembers' ], [ groupInformation.membersCount.toString() ]) }</Text> <Text small underline pointer onClick={ () => handleAction('members') }>{ LocalizeText('group.membercount', [ 'totalMembers' ], [ groupInformation.membersCount.toString() ]) }</Text>
@ -143,4 +149,4 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
</Column> </Column>
</Grid> </Grid>
); );
}; };

View File

@ -11,32 +11,40 @@ interface GroupTabBadgeViewProps
setCloseAction: Dispatch<SetStateAction<{ action: () => boolean }>>; setCloseAction: Dispatch<SetStateAction<{ action: () => boolean }>>;
groupData: IGroupData; groupData: IGroupData;
setGroupData: Dispatch<SetStateAction<IGroupData>>; setGroupData: Dispatch<SetStateAction<IGroupData>>;
onBadgeCodeUpdate?: (badgeCode: string) => void;
} }
export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props => export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props =>
{ {
const { groupData = null, setGroupData = null, setCloseAction = null, skipDefault = null } = props; const { groupData = null, setGroupData = null, setCloseAction = null, skipDefault = null, onBadgeCodeUpdate = null } = props;
const [ badgeParts, setBadgeParts ] = useState<GroupBadgePart[]>(null);
const { groupCustomize = null } = useGroup(); const { groupCustomize = null } = useGroup();
const [ badgeParts, setBadgeParts ] = useState<GroupBadgePart[]>(groupData?.groupBadgeParts || []);
const getModifiedBadgeCode = () => const getModifiedBadgeCode = () =>
{ {
if(!badgeParts || !badgeParts.length) return ''; if (!badgeParts || !badgeParts.length) return '';
let badgeCode = ''; let badgeCode = '';
badgeParts.forEach(part => (part.code && (badgeCode += part.code))); badgeParts.forEach(part => {
if (part.code && part.code.length > 0) {
badgeCode += part.code;
}
// Exclude unset symbols (key: 0) from the badge code
});
console.log('GroupTabBadgeView: Computed badge code', { badgeCode, badgeParts });
return badgeCode; return badgeCode;
} };
const saveBadge = useCallback(() => const saveBadge = useCallback(() =>
{ {
if(!groupData || !badgeParts || !badgeParts.length) return false; if (!groupData || !badgeParts || !badgeParts.length) return false;
if((groupData.groupBadgeParts === badgeParts)) return true; if ((groupData.groupBadgeParts === badgeParts)) return true;
if(groupData.groupId <= 0) if (groupData.groupId <= 0)
{ {
setGroupData(prevValue => setGroupData(prevValue =>
{ {
@ -54,13 +62,13 @@ export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props =>
badgeParts.forEach(part => badgeParts.forEach(part =>
{ {
if(!part.code) return; if (!part.code) return;
badge.push(part.key); badge.push(part.key);
badge.push(part.color); badge.push(part.color);
badge.push(part.position); badge.push(part.position);
}); });
SendMessageComposer(new GroupSaveBadgeComposer(groupData.groupId, badge)); SendMessageComposer(new GroupSaveBadgeComposer(groupData.groupId, badge));
return true; return true;
@ -68,8 +76,8 @@ export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props =>
useEffect(() => useEffect(() =>
{ {
if(groupData.groupBadgeParts) return; if (groupData.groupBadgeParts) return;
const badgeParts = [ const badgeParts = [
new GroupBadgePart(GroupBadgePart.BASE, groupCustomize.badgeBases[0].id, groupCustomize.badgePartColors[0].id), new GroupBadgePart(GroupBadgePart.BASE, groupCustomize.badgeBases[0].id, groupCustomize.badgePartColors[0].id),
new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id), new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id),
@ -88,14 +96,14 @@ export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props =>
useEffect(() => useEffect(() =>
{ {
if(groupData.groupId <= 0) console.log('GroupTabBadgeView: Setting badgeParts', { groupData, groupBadgeParts: groupData?.groupBadgeParts });
if (groupData.groupId <= 0)
{ {
setBadgeParts(groupData.groupBadgeParts ? [ ...groupData.groupBadgeParts ] : null); setBadgeParts(groupData.groupBadgeParts ? [ ...groupData.groupBadgeParts ] : []);
return; return;
} }
setBadgeParts(groupData.groupBadgeParts); setBadgeParts(groupData.groupBadgeParts || []);
}, [ groupData ]); }, [ groupData ]);
useEffect(() => useEffect(() =>
@ -104,17 +112,21 @@ export const GroupTabBadgeView: FC<GroupTabBadgeViewProps> = props =>
return () => setCloseAction(null); return () => setCloseAction(null);
}, [ setCloseAction, saveBadge ]); }, [ setCloseAction, saveBadge ]);
return ( return (
<Grid overflow="hidden" gap={ 1 }> <Grid gap={ 1 } overflow="hidden">
<Column size={ 2 }> <Column size={ 2 }>
<Flex center className="bg-muted rounded p-1"> <Flex center className="bg-muted rounded p-1">
<LayoutBadgeImageView badgeCode={ getModifiedBadgeCode() } isGroup={ true } /> { getModifiedBadgeCode() ? (
<LayoutBadgeImageView badgeCode={ getModifiedBadgeCode() } isGroup={ true } />
) : (
<div style={{ width: 45, height: 45 }} /> // Placeholder to maintain layout
)}
</Flex> </Flex>
</Column> </Column>
<Column size={ 10 } overflow="auto"> <Column overflow="auto" size={ 10 }>
<GroupBadgeCreatorView badgeParts={ badgeParts } setBadgeParts={ setBadgeParts } /> <GroupBadgeCreatorView badgeParts={ badgeParts } setBadgeParts={ setBadgeParts } onBadgeCodeUpdate={ onBadgeCodeUpdate } />
</Column> </Column>
</Grid> </Grid>
); );
}; };