Merge pull request #1011 from kubet/feat/usage-snack

fix: snack stream re rendering other component and closing issue
This commit is contained in:
kubet 2025-07-19 16:31:32 +02:00 committed by GitHub
commit 91b9cff776
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 21 additions and 13 deletions

View File

@ -509,8 +509,11 @@ export default function ThreadPage({
setDebugMode(debugParam === 'true');
}, [searchParams]);
const hasCheckedUpgradeDialog = useRef(false);
useEffect(() => {
if (initialLoadCompleted && subscriptionData) {
if (initialLoadCompleted && subscriptionData && !hasCheckedUpgradeDialog.current) {
hasCheckedUpgradeDialog.current = true;
const hasSeenUpgradeDialog = localStorage.getItem('suna_upgrade_dialog_displayed');
const isFreeTier = subscriptionStatus === 'no_subscription';
if (!hasSeenUpgradeDialog && isFreeTier && !isLocalMode()) {

View File

@ -125,6 +125,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
const [configModalTab, setConfigModalTab] = useState('integrations');
const [registryDialogOpen, setRegistryDialogOpen] = useState(false);
const [showSnackbar, setShowSnackbar] = useState(defaultShowSnackbar);
const [userDismissedUsage, setUserDismissedUsage] = useState(false);
const [billingModalOpen, setBillingModalOpen] = useState(false);
const {
@ -161,12 +162,12 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
// Auto-show usage preview when we have subscription data
useEffect(() => {
if (shouldShowUsage && defaultShowSnackbar !== false && (showSnackbar === false || showSnackbar === defaultShowSnackbar)) {
if (shouldShowUsage && defaultShowSnackbar !== false && !userDismissedUsage && (showSnackbar === false || showSnackbar === defaultShowSnackbar)) {
setShowSnackbar('upgrade');
} else if (!shouldShowUsage && showSnackbar !== false) {
setShowSnackbar(false);
}
}, [subscriptionData, showSnackbar, defaultShowSnackbar, shouldShowUsage, subscriptionStatus, showToLowCreditUsers]);
}, [subscriptionData, showSnackbar, defaultShowSnackbar, shouldShowUsage, subscriptionStatus, showToLowCreditUsers, userDismissedUsage]);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
@ -340,7 +341,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
showToolPreview={showToolPreview}
showUsagePreview={showSnackbar}
subscriptionData={subscriptionData}
onCloseUsage={() => setShowSnackbar(false)}
onCloseUsage={() => { setShowSnackbar(false); setUserDismissedUsage(true); }}
onOpenUpgrade={() => setBillingModalOpen(true)}
isVisible={showToolPreview || !!showSnackbar}
/>

View File

@ -56,6 +56,8 @@ export const ChatSnack: React.FC<ChatSnackProps> = ({
notifications.push('usage');
}
const totalNotifications = notifications.length;
const hasMultiple = totalNotifications > 1;
@ -136,13 +138,15 @@ export const ChatSnack: React.FC<ChatSnackProps> = ({
type={showUsagePreview}
subscriptionData={subscriptionData}
onClose={() => {
// First close the usage notification
if (onCloseUsage) onCloseUsage();
// If there are other notifications, switch to them
if (totalNotifications > 1) {
const remainingNotifications = notifications.filter(n => n !== 'usage');
if (remainingNotifications.length > 0) {
setCurrentView(0); // Switch to first remaining notification
}
// Check what notifications will remain after closing usage
const willHaveToolNotification = showToolPreview && toolCalls.length > 0;
// If there will be other notifications, switch to them
if (willHaveToolNotification) {
setCurrentView(0); // Switch to tool notification
}
}}
hasMultiple={hasMultiple}

View File

@ -84,7 +84,7 @@ export const UsagePreview: React.FC<UsagePreviewProps> = ({
<div className="flex-1 min-w-0">
<motion.div className="flex items-center gap-2 mb-1">
<h4 className="text-sm font-medium text-foreground truncate">
Upgrade for more usage
Upgrade for more usage & better AI Models
</h4>
</motion.div>
@ -124,8 +124,8 @@ export const UsagePreview: React.FC<UsagePreviewProps> = ({
</button>
)}
<Button value='ghost' data-close-click className="bg-transparent hover:bg-transparent flex-shrink-0" onClick={onClose}>
<X className="h-4 w-4 text-muted-foreground group-hover:text-foreground transition-colors" />
<Button variant="ghost" size="icon" className="h-8 w-8 flex-shrink-0 hover:bg-muted/50" onClick={(e) => { e.stopPropagation(); onClose?.(); }}>
<X className="h-4 w-4 text-muted-foreground hover:text-foreground transition-colors" />
</Button>
</div>
);