From 2269b20f35a5a1e9d84d1c2e698a5da483816641 Mon Sep 17 00:00:00 2001 From: duckietm Date: Fri, 2 May 2025 16:54:02 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20WiP=20Group=20badges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/groups/GroupBadgePart.ts | 1 - src/common/layout/LayoutBadgeImageView.tsx | 13 ++-- src/components/groups/GroupsView.tsx | 15 ++-- .../groups/views/GroupBadgeCreatorView.tsx | 68 +++++++++++++++---- .../groups/views/GroupCreatorView.tsx | 18 ++++- .../views/GroupInformationStandaloneView.tsx | 18 +++-- .../groups/views/GroupInformationView.tsx | 12 +++- .../groups/views/tabs/GroupTabBadgeView.tsx | 60 +++++++++------- 8 files changed, 148 insertions(+), 57 deletions(-) diff --git a/src/api/groups/GroupBadgePart.ts b/src/api/groups/GroupBadgePart.ts index 3b74875..09c137c 100644 --- a/src/api/groups/GroupBadgePart.ts +++ b/src/api/groups/GroupBadgePart.ts @@ -1,4 +1,3 @@ - export class GroupBadgePart { public static BASE: string = 'b'; diff --git a/src/common/layout/LayoutBadgeImageView.tsx b/src/common/layout/LayoutBadgeImageView.tsx index 5f53e15..a7845dc 100644 --- a/src/common/layout/LayoutBadgeImageView.tsx +++ b/src/common/layout/LayoutBadgeImageView.tsx @@ -47,15 +47,10 @@ export const LayoutBadgeImageView: FC = props => 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) { newStyle.transform = `scale(${ scale })`; if(!(scale % 1)) newStyle.imageRendering = 'pixelated'; - // If scaling, adjust the dimensions in SCSS instead } } @@ -111,6 +106,8 @@ export const LayoutBadgeImageView: FC = props => // Fallback: Try fetching directly from session data let texture = isGroup ? GetSessionDataManager().getGroupBadgeImage(badgeCode) : GetSessionDataManager().getBadgeImage(badgeCode); + console.log('LayoutBadgeImageView: getGroupBadgeImage result', { badgeCode, texture: texture ? 'exists' : 'null', isGroup }); + if(texture) { const sprite = new NitroSprite(texture); @@ -162,6 +159,12 @@ export const LayoutBadgeImageView: FC = props => return null; // Optionally render a loading placeholder } + if(!imageElement) + { + console.log('LayoutBadgeImageView: Rendering fallback placeholder', { badgeCode, isGroup }); + return ; // Gray square as fallback + } + console.log('LayoutBadgeImageView: Rendering badge', { badgeCode, hasImage: !!imageElement, isGroup }); return ( diff --git a/src/components/groups/GroupsView.tsx b/src/components/groups/GroupsView.tsx index bbaee28..f6e11e2 100644 --- a/src/components/groups/GroupsView.tsx +++ b/src/components/groups/GroupsView.tsx @@ -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 { AddEventLinkTracker, RemoveLinkEventTracker, SendMessageComposer, TryVisitRoom } from '../../api'; import { useGroup, useMessageEvent } from '../../hooks'; @@ -10,8 +10,15 @@ import { GroupMembersView } from './views/GroupMembersView'; export const GroupsView: FC<{}> = props => { const [ isCreatorVisible, setCreatorVisible ] = useState(false); + const [ currentBadgeCode, setCurrentBadgeCode ] = useState(''); // State to hold the current badge code 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, event => { const parser = event.getParser(); @@ -53,11 +60,11 @@ export const GroupsView: FC<{}> = props => return ( <> { isCreatorVisible && - setCreatorVisible(false) } /> } + setCreatorVisible(false) } onBadgeCodeUpdate={ handleBadgeCodeUpdate } /> } { !isCreatorVisible && } - + ); -}; +}; \ No newline at end of file diff --git a/src/components/groups/views/GroupBadgeCreatorView.tsx b/src/components/groups/views/GroupBadgeCreatorView.tsx index 8e32c92..9a807f2 100644 --- a/src/components/groups/views/GroupBadgeCreatorView.tsx +++ b/src/components/groups/views/GroupBadgeCreatorView.tsx @@ -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 { GroupBadgePart } from '../../../api'; import { Base, Column, Flex, Grid, LayoutBadgeImageView } from '../../../common'; @@ -6,15 +6,16 @@ import { useGroup } from '../../../hooks'; interface GroupBadgeCreatorViewProps { - badgeParts: GroupBadgePart[]; + badgeParts: GroupBadgePart[] | null; setBadgeParts: Dispatch>; + onBadgeCodeUpdate?: (badgeCode: string) => void; } const POSITIONS: number[] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]; export const GroupBadgeCreatorView: FC = props => { - const { badgeParts = [], setBadgeParts = null } = props; + const { badgeParts = [], setBadgeParts = null, onBadgeCodeUpdate = null } = props; const [ selectedIndex, setSelectedIndex ] = useState(-1); const { groupCustomize = null } = useGroup(); @@ -27,23 +28,61 @@ export const GroupBadgeCreatorView: FC = props => setBadgeParts(newBadgeParts); 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 ( <> { ((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 ( setSelectedIndex(index) }> - { (badgeParts[index].code && (badgeParts[index].code.length > 0)) && - } - { (!badgeParts[index].code || !badgeParts[index].code.length) && - - - } + { (badgeCode && badgeCode.length > 0) ? ( + + ) : ( + + + + )} { (part.type !== GroupBadgePart.BASE) && @@ -71,13 +110,16 @@ export const GroupBadgeCreatorView: FC = props => } { ((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 ( setPartProperty(selectedIndex, 'key', item.id) }> - + ); }) } } ); -} +}; \ No newline at end of file diff --git a/src/components/groups/views/GroupCreatorView.tsx b/src/components/groups/views/GroupCreatorView.tsx index cd64ef3..ed024f4 100644 --- a/src/components/groups/views/GroupCreatorView.tsx +++ b/src/components/groups/views/GroupCreatorView.tsx @@ -11,18 +11,20 @@ import { GroupTabIdentityView } from './tabs/GroupTabIdentityView'; interface GroupCreatorViewProps { onClose: () => void; + onBadgeCodeUpdate?: (badgeCode: string) => void; // Callback to propagate badge code to parent } const TABS: number[] = [ 1, 2, 3, 4 ]; export const GroupCreatorView: FC = props => { - const { onClose = null } = props; + const { onClose = null, onBadgeCodeUpdate = null } = props; const [ currentTab, setCurrentTab ] = useState(1); const [ closeAction, setCloseAction ] = useState<{ action: () => boolean }>(null); const [ groupData, setGroupData ] = useState(null); const [ availableRooms, setAvailableRooms ] = useState<{ id: number, name: string }[]>(null); const [ purchaseCost, setPurchaseCost ] = useState(0); + const [ currentBadgeCode, setCurrentBadgeCode ] = useState(''); // State to hold the current badge code const onCloseClose = () => { @@ -85,6 +87,16 @@ export const GroupCreatorView: FC = props => 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, event => { const parser = event.getParser(); @@ -143,7 +155,7 @@ export const GroupCreatorView: FC = props => { (currentTab === 1) && } { (currentTab === 2) && - } + } { (currentTab === 3) && } { (currentTab === 4) && @@ -161,4 +173,4 @@ export const GroupCreatorView: FC = props => ); -}; +}; \ No newline at end of file diff --git a/src/components/groups/views/GroupInformationStandaloneView.tsx b/src/components/groups/views/GroupInformationStandaloneView.tsx index d4206d7..c185233 100644 --- a/src/components/groups/views/GroupInformationStandaloneView.tsx +++ b/src/components/groups/views/GroupInformationStandaloneView.tsx @@ -5,25 +5,35 @@ import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../ import { useMessageEvent } from '../../../hooks'; import { GroupInformationView } from './GroupInformationView'; -export const GroupInformationStandaloneView: FC<{}> = props => +interface GroupInformationStandaloneViewProps { + badgeCode?: string; + isCreating?: boolean; +} + +export const GroupInformationStandaloneView: FC = props => +{ + const { badgeCode = null, isCreating = false } = props; const [ groupInformation, setGroupInformation ] = useState(null); useMessageEvent(GroupInformationEvent, event => { + // Skip updates during group creation + if (isCreating) return; + const parser = event.getParser(); if((groupInformation && (groupInformation.id === parser.id)) || parser.flag) setGroupInformation(parser); }); - if(!groupInformation) return null; + if(!groupInformation && !isCreating) return null; return ( setGroupInformation(null) } /> - setGroupInformation(null) } /> + setGroupInformation(null) } badgeCode={ badgeCode } /> ); -}; +}; \ No newline at end of file diff --git a/src/components/groups/views/GroupInformationView.tsx b/src/components/groups/views/GroupInformationView.tsx index da6bdc8..da058b2 100644 --- a/src/components/groups/views/GroupInformationView.tsx +++ b/src/components/groups/views/GroupInformationView.tsx @@ -11,11 +11,12 @@ interface GroupInformationViewProps extends GridProps groupInformation: GroupInformationParser; onJoin?: () => void; onClose?: () => void; + badgeCode?: string; // Optional badge code prop } export const GroupInformationView: FC = 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 isRealOwner = (groupInformation && (groupInformation.ownerName === GetSessionDataManager().userName)); @@ -99,11 +100,16 @@ export const GroupInformationView: FC = props => if(!groupInformation) return null; + // Use badgeCode if provided (during group creation), otherwise fall back to groupInformation.badge + const displayBadgeCode = badgeCode || groupInformation.badge; + return ( - + { displayBadgeCode && ( + + )} handleAction('members') }>{ LocalizeText('group.membercount', [ 'totalMembers' ], [ groupInformation.membersCount.toString() ]) } @@ -143,4 +149,4 @@ export const GroupInformationView: FC = props => ); -}; +}; \ No newline at end of file diff --git a/src/components/groups/views/tabs/GroupTabBadgeView.tsx b/src/components/groups/views/tabs/GroupTabBadgeView.tsx index 73d65c0..112127e 100644 --- a/src/components/groups/views/tabs/GroupTabBadgeView.tsx +++ b/src/components/groups/views/tabs/GroupTabBadgeView.tsx @@ -11,32 +11,40 @@ interface GroupTabBadgeViewProps setCloseAction: Dispatch boolean }>>; groupData: IGroupData; setGroupData: Dispatch>; + onBadgeCodeUpdate?: (badgeCode: string) => void; } export const GroupTabBadgeView: FC = props => { - const { groupData = null, setGroupData = null, setCloseAction = null, skipDefault = null } = props; - const [ badgeParts, setBadgeParts ] = useState(null); + const { groupData = null, setGroupData = null, setCloseAction = null, skipDefault = null, onBadgeCodeUpdate = null } = props; const { groupCustomize = null } = useGroup(); + const [ badgeParts, setBadgeParts ] = useState(groupData?.groupBadgeParts || []); const getModifiedBadgeCode = () => { - if(!badgeParts || !badgeParts.length) return ''; + if (!badgeParts || !badgeParts.length) return ''; 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; - } + }; 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 => { @@ -54,13 +62,13 @@ export const GroupTabBadgeView: FC = props => badgeParts.forEach(part => { - if(!part.code) return; - + if (!part.code) return; + badge.push(part.key); badge.push(part.color); badge.push(part.position); }); - + SendMessageComposer(new GroupSaveBadgeComposer(groupData.groupId, badge)); return true; @@ -68,8 +76,8 @@ export const GroupTabBadgeView: FC = props => useEffect(() => { - if(groupData.groupBadgeParts) return; - + if (groupData.groupBadgeParts) return; + const badgeParts = [ new GroupBadgePart(GroupBadgePart.BASE, groupCustomize.badgeBases[0].id, groupCustomize.badgePartColors[0].id), new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id), @@ -88,14 +96,14 @@ export const GroupTabBadgeView: FC = props => 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; } - - setBadgeParts(groupData.groupBadgeParts); + + setBadgeParts(groupData.groupBadgeParts || []); }, [ groupData ]); useEffect(() => @@ -104,17 +112,21 @@ export const GroupTabBadgeView: FC = props => return () => setCloseAction(null); }, [ setCloseAction, saveBadge ]); - + return ( - + - + { getModifiedBadgeCode() ? ( + + ) : ( +
// Placeholder to maintain layout + )} - - + + ); -}; +}; \ No newline at end of file