Files
Nitro-V3/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx
T
simoleo89 9ef6983632 post cherry-pick: restore useEffectEvent wrapper + fix configuration import
Two typecheck regressions surfaced after pulling duckietm PR #126 onto
this branch:

- NotificationBadgeReceivedBubbleView lost its `useEffectEvent` wrapper
  during conflict resolution. The PR rewrote the effect to use a plain
  `useEffect(..., [activeBadgeCodes.length])`; reintroduce the
  `requestBadgesIfEmpty = useEffectEvent(...)` shape that the rest of
  this branch uses, applied to the renamed activeBadgeCodes selector.
- `src/bootstrap.ts` was importing `GetConfiguration` from the package
  alias `@nitrots/configuration`, which Vite resolves via filesystem
  alias but tsgo does not. Swap to the monolithic
  `@nitrots/nitro-renderer` re-export, matching how App.tsx already
  imports the same symbol.

Both fixes get `yarn typecheck` green again and all 113 Vitest cases
still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:16:52 +02:00

80 lines
3.3 KiB
TypeScript

import { RequestBadgesComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useEffectEvent } from 'react';
import { LocalizeText, NotificationBubbleItem, SendMessageComposer } from '../../../../api';
import { Flex, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common';
import { useInventoryBadges } from '../../../../hooks';
export interface NotificationBadgeReceivedBubbleViewProps extends LayoutNotificationBubbleViewProps
{
item: NotificationBubbleItem;
}
export const NotificationBadgeReceivedBubbleView: FC<NotificationBadgeReceivedBubbleViewProps> = props =>
{
const { item = null, onClose = null, ...rest } = props;
const { activeBadgeCodes = [], toggleBadge = null, isWearingBadge = null, canWearBadges = null } = useInventoryBadges();
const requestBadgesIfEmpty = useEffectEvent(() =>
{
if(activeBadgeCodes.length === 0) SendMessageComposer(new RequestBadgesComposer());
});
useEffect(() =>
{
requestBadgesIfEmpty();
}, []);
const badgeCode = item?.linkUrl ?? null;
const isLoaded = activeBadgeCodes.length > 0;
const alreadyWearing = !!badgeCode && !!isWearingBadge && isWearingBadge(badgeCode);
const slotsAvailable = !!canWearBadges && canWearBadges();
const canShowWearButton = !!badgeCode && isLoaded && !alreadyWearing && slotsAvailable;
const handleWear = (event: React.MouseEvent) =>
{
event.stopPropagation();
if(canShowWearButton && toggleBadge) toggleBadge(badgeCode);
if(onClose) onClose();
};
const handleDismiss = (event: React.MouseEvent) =>
{
event.stopPropagation();
if(onClose) onClose();
};
return (
<LayoutNotificationBubbleView className="flex-col" onClose={ onClose } { ...rest }>
<div onClick={ (e) => e.stopPropagation() }>
<Flex alignItems="center" gap={ 2 } className="mb-2">
<Flex center className="w-[50px] h-[50px] shrink-0">
{ item.iconUrl && <img alt="" className="no-select" src={ item.iconUrl } /> }
</Flex>
<Flex column gap={ 0 }>
<Text bold variant="white">
{ item.senderName
? LocalizeText('notifications.text.received.badge', [ 'user_name' ], [ item.senderName ])
: LocalizeText('prereg.reward.you.received') }
</Text>
<Text variant="white" small>{ item.message }</Text>
</Flex>
</Flex>
<Flex alignItems="center" justifyContent="end" gap={ 2 }>
{ canShowWearButton &&
<button
className="btn btn-success w-full btn-sm"
type="button"
onClick={ handleWear }>
{ LocalizeText('inventory.badges.wearbadge') }
</button> }
<span className="underline cursor-pointer text-nowrap" onClick={ handleDismiss }>
{ LocalizeText('notifications.button.later') }
</span>
</Flex>
</div>
</LayoutNotificationBubbleView>
);
};