61 lines
2.7 KiB
TypeScript
61 lines
2.7 KiB
TypeScript
// 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 (
|
|
<div className={classNames(css.surface, callPresent && css.surfaceActive)}>
|
|
{/* 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. */}
|
|
<ShareTargetStrip />
|
|
<div className={classNames(css.appShell, callPresent && css.appShellBottomRound)}>
|
|
{children}
|
|
</div>
|
|
<div className={classNames(css.bottomRail, callPresent && css.bottomRailActive)}>
|
|
<IncomingCallStripRenderer />
|
|
<CallStatusRenderer />
|
|
{ringing && <div className={css.ringOrbit} aria-hidden />}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|