// App-shell wrapper hosting the optional top share-target banner, the // `ClientLayout` body, and the bottom call-rail (incoming-ring strips + // active-call status pill) inside a single flex column. // // While at least one ring is queued or an active call's pill is visible, // the container paints `#090909` in the gap and pulls the app shell up // with rounded bottom corners. // // While `pendingShareAtom` is non-null, `ShareTargetStrip` renders above // `appShell` and pushes ClientLayout (including its nav-tab header) // down by the banner's height. Returns null when no share is pending, // so the slot is free in steady state. // // The TOP horseshoe (user profile sheet) used to live here too, but // was hoisted into `features/room/RoomViewProfilePanel.tsx` so it // only affects the room/chat column — on desktop it no longer // blankets the sidebar / nav rails. import React, { ReactNode } from 'react'; import classNames from 'classnames'; import { useAtomValue } from 'jotai'; import { isRingingAtom } from '../state/incomingCalls'; import { IncomingCallStripRenderer } from './IncomingCallStripRenderer'; import { CallStatusRenderer, useCallStatusVisible } from './CallStatusRenderer'; import { ShareTargetStrip } from '../features/share-target'; import * as css from './HorseshoeContainer.css'; type HorseshoeContainerProps = { children: ReactNode; }; export function HorseshoeContainer({ children }: HorseshoeContainerProps) { const ringing = useAtomValue(isRingingAtom); // Gate the bottom horseshoe on whether the rail will *actually* paint // pixels. A naive `callEmbed !== undefined` check would also flip // true while the pill is suppressed (mobile + viewing the call room // + video) — that would leave a black 8px shelf with no visible // rail underneath. Reusing the same visibility predicate as the // renderer keeps shell + content in sync. const pillVisible = useCallStatusVisible(); const callPresent = ringing || pillVisible; return (
{/* Share-target banner — rendered before appShell so it occupies its own flex row at the top and pushes ClientLayout (including the nav-tab header) down by the banner's height while a share is pending. Self-renders null when no share, so this slot is free in steady state. */}
{children}
{ringing &&
}
); }