fix: filter panel overlap, Wallet & Stash rename, Sasquatch on all pages
Filter panel (MapPage): - Sheet now h-[82vh] max-h-[680px] with flex-col layout - Scrollable content area uses flex-1 min-h-0 (proper flex children) - "Show Results" button is now flex-shrink-0 at bottom of column - No more overlap between Show Results and Stash/Tour quick action buttons Wallet & Stash rename: - Settings tab renamed from "Wallet" to "Wallet & Stash" - "Me" dropdown in mobile header shows "Wallet & Stash" for the wallet link Sasquatch persistent button (all pages): - Minimized bubble is now ALWAYS visible on every page (not just pages with a guide message) - Tapping the bubble on any page opens Sasquatch with a generic helpful greeting - Pages with specific context (/, /map, /lab, /onboarding) still show their custom message - Pages without context (/wallet, /settings, /stash, /events, etc.) now show a generic "Ask me anything" greeting with common questions - Removed the separate X close button (minimize was already the same) — bubble stays visible as entry point at all times - onPointerUp now also sets isVisible=true so panel opens on pages without a message Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
340f6fd89f
commit
0dc0d272e1
4 changed files with 49 additions and 35 deletions
|
|
@ -204,7 +204,7 @@ function MobileHeaderUserMenu({
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link to="/wallet" className="flex items-center gap-2 cursor-pointer">
|
<Link to="/wallet" className="flex items-center gap-2 cursor-pointer">
|
||||||
<Wallet className="h-4 w-4 text-orange-400" />
|
<Wallet className="h-4 w-4 text-orange-400" />
|
||||||
<span className="text-orange-400">Wallet</span>
|
<span className="text-orange-400">Wallet & Stash</span>
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,9 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) {
|
||||||
dragMoved.current = false;
|
dragMoved.current = false;
|
||||||
setTimeout(() => setIsDragging(false), 50);
|
setTimeout(() => setIsDragging(false), 50);
|
||||||
if (!wasDrag) {
|
if (!wasDrag) {
|
||||||
|
// Always open the panel — even on pages without a context message
|
||||||
setIsMinimized(false);
|
setIsMinimized(false);
|
||||||
|
setIsVisible(true);
|
||||||
}
|
}
|
||||||
}, [setIsMinimized]);
|
}, [setIsMinimized]);
|
||||||
|
|
||||||
|
|
@ -576,16 +578,33 @@ Instructions:
|
||||||
};
|
};
|
||||||
}, [handleSendMessage, setIsMinimized]);
|
}, [handleSendMessage, setIsMinimized]);
|
||||||
|
|
||||||
// Show if we have a message prop OR if we've been opened externally
|
// The minimized bubble is ALWAYS visible so the user can reopen Sasquatch on any page.
|
||||||
const shouldShow = !!message || isVisible;
|
// The full panel shows only when there's a message or when opened externally/by user tap.
|
||||||
if (!shouldShow) return null;
|
const shouldShowPanel = !!message || isVisible;
|
||||||
|
|
||||||
const inputDisabled = isThinking;
|
const inputDisabled = isThinking;
|
||||||
|
|
||||||
|
// Build a fallback generic message for pages with no specific context
|
||||||
|
const activeMessage = message ?? {
|
||||||
|
id: 'generic',
|
||||||
|
title: 'SkateSpots Assistant 🦧',
|
||||||
|
message: "Yo! I'm The Sasquatch. Ask me anything about spots, events, coaches, gear, or how the app works. I've got live data from the SkateSpots relay!",
|
||||||
|
context: undefined,
|
||||||
|
suggestedQuestions: [
|
||||||
|
'Find spots near me',
|
||||||
|
'Any upcoming events?',
|
||||||
|
'What is Nostr?',
|
||||||
|
'How do zaps work?',
|
||||||
|
'What is My Stash?',
|
||||||
|
'How to add a spot?',
|
||||||
|
],
|
||||||
|
enableChat: true,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* ── Minimized bubble — draggable ── */}
|
{/* ── Minimized bubble — always visible, draggable ── */}
|
||||||
{isMinimized && (
|
{(!shouldShowPanel || isMinimized) && (
|
||||||
<div
|
<div
|
||||||
ref={dragRef}
|
ref={dragRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -621,7 +640,7 @@ Instructions:
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Full panel ── */}
|
{/* ── Full panel ── */}
|
||||||
{!isMinimized && (
|
{shouldShowPanel && !isMinimized && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed z-50 transition-all duration-300 ease-out',
|
'fixed z-50 transition-all duration-300 ease-out',
|
||||||
|
|
@ -665,24 +684,19 @@ Instructions:
|
||||||
<CVMStatus connected={cvmConnected} />
|
<CVMStatus connected={cvmConnected} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[10px] text-[#ccff00]/50 leading-none mt-0.5">
|
<div className="text-[10px] text-[#ccff00]/50 leading-none mt-0.5">
|
||||||
{message.title}
|
{activeMessage.title}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1 flex-shrink-0">
|
<div className="flex gap-1 flex-shrink-0">
|
||||||
|
{/* Minimize — keeps the bubble visible */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsMinimized(true)}
|
onClick={() => setIsMinimized(true)}
|
||||||
className="w-6 h-6 flex items-center justify-center rounded text-[#ccff00]/40 hover:text-[#ccff00] hover:bg-[#ccff00]/10 transition-colors"
|
className="w-6 h-6 flex items-center justify-center rounded text-[#ccff00]/40 hover:text-[#ccff00] hover:bg-[#ccff00]/10 transition-colors"
|
||||||
aria-label="Minimize"
|
aria-label="Minimize to bubble"
|
||||||
|
title="Minimize — bubble stays visible so you can reopen anytime"
|
||||||
>
|
>
|
||||||
<Minimize2 className="h-3 w-3" />
|
<Minimize2 className="h-3 w-3" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
onClick={() => setIsMinimized(true)}
|
|
||||||
className="w-6 h-6 flex items-center justify-center rounded text-[#ccff00]/40 hover:text-[#ff0040] hover:bg-[#ff0040]/10 transition-colors"
|
|
||||||
aria-label="Close"
|
|
||||||
>
|
|
||||||
<X className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -690,21 +704,21 @@ Instructions:
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
{/* Greeting */}
|
{/* Greeting */}
|
||||||
<p className="text-xs text-gray-300 leading-relaxed mb-3">
|
<p className="text-xs text-gray-300 leading-relaxed mb-3">
|
||||||
{message.message}
|
{activeMessage.message}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Action button */}
|
{/* Action button */}
|
||||||
{message.action && (
|
{activeMessage.action && (
|
||||||
<button
|
<button
|
||||||
onClick={message.action.onClick}
|
onClick={activeMessage.action.onClick}
|
||||||
className="w-full py-2 px-3 rounded-lg text-xs font-bold text-black bg-[#ccff00] hover:bg-[#d4ff1a] transition-colors mb-3"
|
className="w-full py-2 px-3 rounded-lg text-xs font-bold text-black bg-[#ccff00] hover:bg-[#d4ff1a] transition-colors mb-3"
|
||||||
>
|
>
|
||||||
{message.action.label}
|
{activeMessage.action.label}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Chat section */}
|
{/* Chat section */}
|
||||||
{message.enableChat && (
|
{activeMessage.enableChat && (
|
||||||
<div className="border-t border-[#ccff00]/10 pt-3">
|
<div className="border-t border-[#ccff00]/10 pt-3">
|
||||||
{!showChat ? (
|
{!showChat ? (
|
||||||
<button
|
<button
|
||||||
|
|
@ -712,21 +726,21 @@ Instructions:
|
||||||
className="w-full py-2 px-3 rounded-lg text-xs font-bold text-[#ccff00] border border-[#ccff00]/30 hover:border-[#ccff00]/60 hover:bg-[#ccff00]/5 transition-all flex items-center justify-center gap-1.5"
|
className="w-full py-2 px-3 rounded-lg text-xs font-bold text-[#ccff00] border border-[#ccff00]/30 hover:border-[#ccff00]/60 hover:bg-[#ccff00]/5 transition-all flex items-center justify-center gap-1.5"
|
||||||
>
|
>
|
||||||
<HelpCircle className="h-3 w-3" />
|
<HelpCircle className="h-3 w-3" />
|
||||||
{message.context === 'lab' && 'Ask about Coaching & Filming'}
|
{activeMessage.context === 'lab' && 'Ask about Coaching & Filming'}
|
||||||
{message.context === 'map' && 'Ask about Spots, Shops & Gear'}
|
{activeMessage.context === 'map' && 'Ask about Spots, Shops & Gear'}
|
||||||
{(message.context === 'home' || message.context === 'onboarding') && 'Ask me anything 🛹'}
|
{(activeMessage.context === 'home' || activeMessage.context === 'onboarding') && 'Ask me anything 🛹'}
|
||||||
{!message.context && 'Ask me anything 🛹'}
|
{(!activeMessage.context || (activeMessage.context !== 'lab' && activeMessage.context !== 'map' && activeMessage.context !== 'home' && activeMessage.context !== 'onboarding')) && 'Ask me anything 🛹'}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
|
|
||||||
{/* Suggested questions */}
|
{/* Suggested questions */}
|
||||||
{chatMessages.length === 0 && message.suggestedQuestions && (
|
{chatMessages.length === 0 && activeMessage.suggestedQuestions && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-[9px] text-[#ccff00]/50 font-bold uppercase tracking-wider mb-1.5">
|
<div className="text-[9px] text-[#ccff00]/50 font-bold uppercase tracking-wider mb-1.5">
|
||||||
Quick questions:
|
Quick questions:
|
||||||
</div>
|
</div>
|
||||||
{message.suggestedQuestions.map((q) => (
|
{activeMessage.suggestedQuestions.map((q) => (
|
||||||
<button
|
<button
|
||||||
key={q}
|
key={q}
|
||||||
onClick={() => handleSendMessage(q)}
|
onClick={() => handleSendMessage(q)}
|
||||||
|
|
|
||||||
|
|
@ -682,8 +682,8 @@ export default function MapPage() {
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
<SheetContent side="bottom" className="h-[70vh] max-h-[600px] rounded-t-2xl px-4">
|
<SheetContent side="bottom" className="h-[82vh] max-h-[680px] rounded-t-2xl px-4 flex flex-col">
|
||||||
<SheetHeader className="pb-2">
|
<SheetHeader className="pb-2 flex-shrink-0">
|
||||||
<SheetTitle className="flex items-center justify-between text-base">
|
<SheetTitle className="flex items-center justify-between text-base">
|
||||||
<span>Filters</span>
|
<span>Filters</span>
|
||||||
{hasActiveFilters && (
|
{hasActiveFilters && (
|
||||||
|
|
@ -702,7 +702,7 @@ export default function MapPage() {
|
||||||
</SheetTitle>
|
</SheetTitle>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
|
|
||||||
<div className="space-y-4 overflow-y-auto pb-20 -mx-4 px-4" style={{ maxHeight: 'calc(70vh - 120px)' }}>
|
<div className="space-y-4 overflow-y-auto flex-1 min-h-0 -mx-4 px-4 pb-2 scroll-container">
|
||||||
{/* Layer toggles */}
|
{/* Layer toggles */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xs font-bold text-muted-foreground mb-2 uppercase tracking-wide">Show on Map</h3>
|
<h3 className="text-xs font-bold text-muted-foreground mb-2 uppercase tracking-wide">Show on Map</h3>
|
||||||
|
|
@ -858,10 +858,10 @@ export default function MapPage() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Apply button fixed at bottom */}
|
{/* Show Results button — sticks to bottom, never overlaps content */}
|
||||||
<div className="absolute bottom-0 left-0 right-0 p-3 bg-background border-t safe-area-bottom">
|
<div className="flex-shrink-0 pt-3 pb-2 border-t safe-area-bottom bg-background">
|
||||||
<Button
|
<Button
|
||||||
className="w-full h-11 font-bold"
|
className="w-full h-12 font-bold text-base"
|
||||||
onClick={() => setMobileFiltersOpen(false)}
|
onClick={() => setMobileFiltersOpen(false)}
|
||||||
>
|
>
|
||||||
Show Results
|
Show Results
|
||||||
|
|
|
||||||
|
|
@ -659,9 +659,9 @@ export default function SettingsPage() {
|
||||||
<Radio className="h-4 w-4 flex-shrink-0" />
|
<Radio className="h-4 w-4 flex-shrink-0" />
|
||||||
<span className="text-[10px] font-semibold whitespace-nowrap">Relays</span>
|
<span className="text-[10px] font-semibold whitespace-nowrap">Relays</span>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="wallet" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[64px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
<TabsTrigger value="wallet" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[72px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
||||||
<Wallet className="h-4 w-4 flex-shrink-0" />
|
<Wallet className="h-4 w-4 flex-shrink-0" />
|
||||||
<span className="text-[10px] font-semibold whitespace-nowrap">Wallet</span>
|
<span className="text-[10px] font-semibold whitespace-nowrap">Wallet & Stash</span>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="advanced" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[64px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
<TabsTrigger value="advanced" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[64px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
||||||
<Wrench className="h-4 w-4 flex-shrink-0" />
|
<Wrench className="h-4 w-4 flex-shrink-0" />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue