feat(call): introduce StageLayout discriminated union
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -294,12 +294,35 @@ export function InCallPanel({ conversation }: Props) {
|
|||||||
? t('app:call.waiting_for_peers', { defaultValue: 'Warte auf andere…' })
|
? t('app:call.waiting_for_peers', { defaultValue: 'Warte auf andere…' })
|
||||||
: t('app:call.connected');
|
: t('app:call.connected');
|
||||||
|
|
||||||
// Screen shares no longer auto-promote — the user opts in by clicking the
|
// Discord-style precedence:
|
||||||
// "Bildschirm anschauen" overlay, which also toggles whether the audio
|
// 1. focusedId set → 'focus', that tile is the stage.
|
||||||
// plays. Focus falls back to the first tile so focus-mode always has
|
// 2. ≥2 shares, no pin → 'bento', shares fill the stage, webcams strip.
|
||||||
// something to show when no tile was explicitly picked.
|
// 3. exactly 1 share, no pin → 'focus' (auto-promote share).
|
||||||
const effectiveFocusedId = focusedId ?? tiles[0]?.id ?? null;
|
// 4. no shares, no pin → 'equal-grid'.
|
||||||
const speaker = tiles.find((p) => p.id === effectiveFocusedId) ?? tiles[0];
|
type StageLayout =
|
||||||
|
| { kind: 'equal-grid' }
|
||||||
|
| { kind: 'focus'; bigTileId: string }
|
||||||
|
| { kind: 'bento'; shareIds: string[] };
|
||||||
|
|
||||||
|
const shareIds = tiles.filter((t) => t.kind === 'screen').map((t) => t.id);
|
||||||
|
const stageLayout: StageLayout = (() => {
|
||||||
|
if (focusedId !== null && tiles.some((t) => t.id === focusedId)) {
|
||||||
|
return { kind: 'focus', bigTileId: focusedId };
|
||||||
|
}
|
||||||
|
if (shareIds.length >= 2) return { kind: 'bento', shareIds };
|
||||||
|
if (shareIds.length === 1 && shareIds[0]) {
|
||||||
|
return { kind: 'focus', bigTileId: shareIds[0] };
|
||||||
|
}
|
||||||
|
return { kind: 'equal-grid' };
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Tile that owns the big stage when layout is 'focus'. Resolved lazily by
|
||||||
|
// callers below — kept here just so the speaker prop on CallStage/Fullscreen
|
||||||
|
// stays consistent with the layout decision.
|
||||||
|
const bigTile =
|
||||||
|
stageLayout.kind === 'focus'
|
||||||
|
? tiles.find((t) => t.id === stageLayout.bigTileId)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const controls = (
|
const controls = (
|
||||||
<CallControls
|
<CallControls
|
||||||
@@ -369,13 +392,6 @@ export function InCallPanel({ conversation }: Props) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
if (callMode === 'fullscreen') {
|
if (callMode === 'fullscreen') {
|
||||||
// Discord-style: without an explicit pin, fullscreen stays as a stable
|
|
||||||
// equal-size grid. Focus only kicks in when the user pins a tile
|
|
||||||
// (focusedId !== null). Auto-promoting the latest speaker caused the
|
|
||||||
// whole layout to flip on every utterance, which is not what users
|
|
||||||
// expect from a "cinema" view.
|
|
||||||
const hasFocus = focusedId !== null;
|
|
||||||
const effectiveSpeaker = hasFocus ? speaker : undefined;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{micError && (
|
{micError && (
|
||||||
@@ -391,7 +407,7 @@ export function InCallPanel({ conversation }: Props) {
|
|||||||
)}
|
)}
|
||||||
<FullscreenCall
|
<FullscreenCall
|
||||||
tiles={tiles}
|
tiles={tiles}
|
||||||
speaker={effectiveSpeaker}
|
speaker={bigTile}
|
||||||
remoteScreenShares={remoteScreenShares}
|
remoteScreenShares={remoteScreenShares}
|
||||||
conversationMembers={conversation.members}
|
conversationMembers={conversation.members}
|
||||||
activeSpeakers={activeSpeakers}
|
activeSpeakers={activeSpeakers}
|
||||||
@@ -540,7 +556,7 @@ export function InCallPanel({ conversation }: Props) {
|
|||||||
|
|
||||||
<CallStage
|
<CallStage
|
||||||
tiles={tiles}
|
tiles={tiles}
|
||||||
speaker={speaker}
|
speaker={bigTile}
|
||||||
mode={callMode}
|
mode={callMode}
|
||||||
activeSpeakers={activeSpeakers}
|
activeSpeakers={activeSpeakers}
|
||||||
e2ee={isE2EEActive}
|
e2ee={isE2EEActive}
|
||||||
|
|||||||
Reference in New Issue
Block a user