fix(space): stale hud 참조 정리
This commit is contained in:
@@ -2,10 +2,10 @@ import { useEffect, useRef, useState } from 'react';
|
|||||||
import { copy } from '@/shared/i18n';
|
import { copy } from '@/shared/i18n';
|
||||||
import { cn } from '@/shared/lib/cn';
|
import { cn } from '@/shared/lib/cn';
|
||||||
import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine';
|
import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine';
|
||||||
import { SpaceTimerHudWidget } from '@/widgets/space-timer-hud';
|
import { ExitHoldButton } from '@/features/exit-hold';
|
||||||
import { GoalCompleteSheet } from './GoalCompleteSheet';
|
import { GoalCompleteSheet } from './GoalCompleteSheet';
|
||||||
import { InlineMicrostep } from './InlineMicrostep';
|
import { InlineMicrostep } from './InlineMicrostep';
|
||||||
import { ReturnPrompt } from './ReturnPrompt';
|
import { ThoughtOrb } from './ThoughtOrb';
|
||||||
|
|
||||||
interface SpaceFocusHudWidgetProps {
|
interface SpaceFocusHudWidgetProps {
|
||||||
goal: string;
|
goal: string;
|
||||||
@@ -14,21 +14,12 @@ interface SpaceFocusHudWidgetProps {
|
|||||||
hasActiveSession?: boolean;
|
hasActiveSession?: boolean;
|
||||||
playbackState?: 'running' | 'paused';
|
playbackState?: 'running' | 'paused';
|
||||||
sessionPhase?: 'focus' | 'break' | null;
|
sessionPhase?: 'focus' | 'break' | null;
|
||||||
isSessionActionPending?: boolean;
|
|
||||||
canStartSession?: boolean;
|
|
||||||
canPauseSession?: boolean;
|
|
||||||
canRestartSession?: boolean;
|
|
||||||
entryOverlayIntent?: 'resume-refocus' | null;
|
|
||||||
returnPromptMode?: 'focus' | 'break' | null;
|
|
||||||
onEntryOverlayIntentHandled?: () => void;
|
|
||||||
onStartRequested?: () => void;
|
|
||||||
onPauseRequested?: () => void;
|
|
||||||
onRestartRequested?: () => void;
|
|
||||||
onDismissReturnPrompt?: () => void;
|
|
||||||
onIntentUpdate: (payload: { goal?: string; microStep?: string | null }) => boolean | Promise<boolean>;
|
onIntentUpdate: (payload: { goal?: string; microStep?: string | null }) => boolean | Promise<boolean>;
|
||||||
onGoalUpdate: (nextGoal: string) => boolean | Promise<boolean>;
|
onGoalUpdate: (nextGoal: string) => boolean | Promise<boolean>;
|
||||||
onGoalFinish: () => boolean | Promise<boolean>;
|
onGoalFinish: () => boolean | Promise<boolean>;
|
||||||
onStatusMessage: (payload: HudStatusLinePayload) => void;
|
onStatusMessage: (payload: HudStatusLinePayload) => void;
|
||||||
|
onCaptureThought: (note: string) => void;
|
||||||
|
onExitRequested: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SpaceFocusHudWidget = ({
|
export const SpaceFocusHudWidget = ({
|
||||||
@@ -38,42 +29,20 @@ export const SpaceFocusHudWidget = ({
|
|||||||
hasActiveSession = false,
|
hasActiveSession = false,
|
||||||
playbackState = 'paused',
|
playbackState = 'paused',
|
||||||
sessionPhase = 'focus',
|
sessionPhase = 'focus',
|
||||||
isSessionActionPending = false,
|
|
||||||
canStartSession = false,
|
|
||||||
canPauseSession = false,
|
|
||||||
canRestartSession = false,
|
|
||||||
entryOverlayIntent = null,
|
|
||||||
returnPromptMode = null,
|
|
||||||
onEntryOverlayIntentHandled,
|
|
||||||
onStartRequested,
|
|
||||||
onPauseRequested,
|
|
||||||
onRestartRequested,
|
|
||||||
onDismissReturnPrompt,
|
|
||||||
onIntentUpdate,
|
onIntentUpdate,
|
||||||
onGoalUpdate,
|
onGoalUpdate,
|
||||||
onGoalFinish,
|
onGoalFinish,
|
||||||
onStatusMessage,
|
onStatusMessage,
|
||||||
|
onCaptureThought,
|
||||||
|
onExitRequested,
|
||||||
}: SpaceFocusHudWidgetProps) => {
|
}: SpaceFocusHudWidgetProps) => {
|
||||||
const [overlay, setOverlay] = useState<'none' | 'return' | 'complete'>('none');
|
const [overlay, setOverlay] = useState<'none' | 'complete'>('none');
|
||||||
const [completePreferredView, setCompletePreferredView] = useState<'choice' | 'next'>('choice');
|
const [completePreferredView, setCompletePreferredView] = useState<'choice' | 'next'>('choice');
|
||||||
const [isSavingIntent, setSavingIntent] = useState(false);
|
const [isSavingIntent, setSavingIntent] = useState(false);
|
||||||
|
|
||||||
const visibleRef = useRef(false);
|
const visibleRef = useRef(false);
|
||||||
const resumePlaybackStateRef = useRef<'running' | 'paused'>(playbackState);
|
|
||||||
const restReminderTimerRef = useRef<number | null>(null);
|
|
||||||
const normalizedGoal = goal.trim().length > 0 ? goal.trim() : copy.space.focusHud.goalFallback;
|
const normalizedGoal = goal.trim().length > 0 ? goal.trim() : copy.space.focusHud.goalFallback;
|
||||||
const isReturnPromptOpen = overlay === 'return';
|
|
||||||
const isCompleteOpen = overlay === 'complete';
|
const isCompleteOpen = overlay === 'complete';
|
||||||
const isIntentOverlayOpen = overlay !== 'none';
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (restReminderTimerRef.current) {
|
|
||||||
window.clearTimeout(restReminderTimerRef.current);
|
|
||||||
restReminderTimerRef.current = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hasActiveSession) {
|
if (!hasActiveSession) {
|
||||||
@@ -83,21 +52,6 @@ export const SpaceFocusHudWidget = ({
|
|||||||
}
|
}
|
||||||
}, [hasActiveSession]);
|
}, [hasActiveSession]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!returnPromptMode) {
|
|
||||||
if (overlay === 'return') {
|
|
||||||
setOverlay('none');
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlay === 'complete') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOverlay('return');
|
|
||||||
}, [overlay, returnPromptMode]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visibleRef.current && playbackState === 'running') {
|
if (!visibleRef.current && playbackState === 'running') {
|
||||||
onStatusMessage({
|
onStatusMessage({
|
||||||
@@ -108,36 +62,11 @@ export const SpaceFocusHudWidget = ({
|
|||||||
visibleRef.current = true;
|
visibleRef.current = true;
|
||||||
}, [normalizedGoal, onStatusMessage, playbackState]);
|
}, [normalizedGoal, onStatusMessage, playbackState]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (resumePlaybackStateRef.current === 'paused' && playbackState === 'running') {
|
|
||||||
onStatusMessage({
|
|
||||||
message: copy.space.focusHud.goalToast(normalizedGoal),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
resumePlaybackStateRef.current = playbackState;
|
|
||||||
}, [normalizedGoal, onStatusMessage, playbackState]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (entryOverlayIntent !== 'resume-refocus' || !hasActiveSession || overlay !== 'none') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// With inline microsteps, we just handle the intent and let the user click if they want.
|
|
||||||
onEntryOverlayIntentHandled?.();
|
|
||||||
}, [entryOverlayIntent, hasActiveSession, onEntryOverlayIntentHandled, overlay]);
|
|
||||||
|
|
||||||
const handleOpenCompleteSheet = (preferredView: 'choice' | 'next' = 'choice') => {
|
const handleOpenCompleteSheet = (preferredView: 'choice' | 'next' = 'choice') => {
|
||||||
setCompletePreferredView(preferredView);
|
setCompletePreferredView(preferredView);
|
||||||
setOverlay('complete');
|
setOverlay('complete');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDismissReturnPrompt = () => {
|
|
||||||
onDismissReturnPrompt?.();
|
|
||||||
if (overlay === 'return') {
|
|
||||||
setOverlay('none');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInlineMicrostepUpdate = async (nextStep: string | null) => {
|
const handleInlineMicrostepUpdate = async (nextStep: string | null) => {
|
||||||
if (isSavingIntent) return false;
|
if (isSavingIntent) return false;
|
||||||
|
|
||||||
@@ -159,18 +88,19 @@ export const SpaceFocusHudWidget = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ThoughtOrb isFocusMode={hasActiveSession} onCaptureThought={onCaptureThought} />
|
||||||
|
|
||||||
<div className="pointer-events-none fixed inset-0 z-20 flex flex-col items-center justify-center pt-10 pb-32">
|
<div className="pointer-events-none fixed inset-0 z-20 flex flex-col items-center justify-center pt-10 pb-32">
|
||||||
{/* The Monolith (Central Hub) */}
|
{/* The Monolith (Central Hub) */}
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
"pointer-events-auto flex flex-col items-center text-center max-w-4xl px-6 transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]",
|
"pointer-events-auto flex flex-col items-center text-center max-w-4xl px-6 transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]",
|
||||||
isIntentOverlayOpen ? "opacity-0 scale-95 blur-md" : "opacity-100 scale-100 blur-0"
|
isCompleteOpen ? "opacity-0 scale-95 blur-md" : "opacity-100 scale-100 blur-0"
|
||||||
)}>
|
)}>
|
||||||
{/* Massive Central Timer */}
|
{/* Massive Unstoppable Timer */}
|
||||||
<div className="relative group cursor-pointer" onClick={() => playbackState === 'running' ? onPauseRequested?.() : onStartRequested?.()}>
|
<div className="relative group cursor-default">
|
||||||
<p className={cn(
|
<p className={cn(
|
||||||
"text-[8rem] md:text-[14rem] font-light tracking-tighter leading-none transition-colors duration-500",
|
"text-[8rem] md:text-[14rem] font-light tracking-tighter leading-none transition-colors duration-500",
|
||||||
sessionPhase === 'break' ? "text-emerald-300 drop-shadow-[0_0_40px_rgba(16,185,129,0.3)]" : "text-white drop-shadow-[0_0_40px_rgba(255,255,255,0.15)]",
|
sessionPhase === 'break' ? "text-emerald-300 drop-shadow-[0_0_40px_rgba(16,185,129,0.3)]" : "text-white drop-shadow-[0_0_40px_rgba(255,255,255,0.15)]"
|
||||||
playbackState === 'paused' && "opacity-60"
|
|
||||||
)}>
|
)}>
|
||||||
{timeDisplay}
|
{timeDisplay}
|
||||||
</p>
|
</p>
|
||||||
@@ -206,28 +136,6 @@ export const SpaceFocusHudWidget = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pointer-events-none fixed inset-0 z-30">
|
<div className="pointer-events-none fixed inset-0 z-30">
|
||||||
<ReturnPrompt
|
|
||||||
open={isReturnPromptOpen && Boolean(returnPromptMode)}
|
|
||||||
mode={returnPromptMode === 'break' ? 'break' : 'focus'}
|
|
||||||
isBusy={isSavingIntent}
|
|
||||||
onContinue={() => {
|
|
||||||
handleDismissReturnPrompt();
|
|
||||||
}}
|
|
||||||
onRefocus={() => {
|
|
||||||
handleDismissReturnPrompt();
|
|
||||||
}}
|
|
||||||
onRest={() => {
|
|
||||||
handleDismissReturnPrompt();
|
|
||||||
}}
|
|
||||||
onNextGoal={() => {
|
|
||||||
handleDismissReturnPrompt();
|
|
||||||
handleOpenCompleteSheet('next');
|
|
||||||
}}
|
|
||||||
onFinish={() => {
|
|
||||||
handleDismissReturnPrompt();
|
|
||||||
handleOpenCompleteSheet('choice');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<GoalCompleteSheet
|
<GoalCompleteSheet
|
||||||
open={isCompleteOpen}
|
open={isCompleteOpen}
|
||||||
currentGoal={goal}
|
currentGoal={goal}
|
||||||
@@ -236,16 +144,7 @@ export const SpaceFocusHudWidget = ({
|
|||||||
onFinish={() => Promise.resolve(onGoalFinish())}
|
onFinish={() => Promise.resolve(onGoalFinish())}
|
||||||
onRest={() => {
|
onRest={() => {
|
||||||
setOverlay('none');
|
setOverlay('none');
|
||||||
onPauseRequested?.();
|
// The timer doesn't pause, they just rest within the flow.
|
||||||
|
|
||||||
if (restReminderTimerRef.current) {
|
|
||||||
window.clearTimeout(restReminderTimerRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
restReminderTimerRef.current = window.setTimeout(() => {
|
|
||||||
onStatusMessage({ message: copy.space.focusHud.restReminder });
|
|
||||||
restReminderTimerRef.current = null;
|
|
||||||
}, 5 * 60 * 1000);
|
|
||||||
}}
|
}}
|
||||||
onConfirm={(nextGoal) => {
|
onConfirm={(nextGoal) => {
|
||||||
return Promise.resolve(onGoalUpdate(nextGoal));
|
return Promise.resolve(onGoalUpdate(nextGoal));
|
||||||
@@ -253,18 +152,16 @@ export const SpaceFocusHudWidget = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SpaceTimerHudWidget
|
{/* Emergency Tether (Exit) */}
|
||||||
hasActiveSession={hasActiveSession}
|
<div className="fixed bottom-8 inset-x-0 z-40 flex justify-center pointer-events-none">
|
||||||
sessionPhase={sessionPhase}
|
<div className="pointer-events-auto opacity-10 hover:opacity-100 transition-opacity duration-500">
|
||||||
playbackState={playbackState}
|
<ExitHoldButton
|
||||||
isControlsDisabled={isSessionActionPending}
|
variant="bar"
|
||||||
canStart={canStartSession}
|
onConfirm={onExitRequested}
|
||||||
canPause={canPauseSession}
|
className="bg-black/20 text-white/70 hover:bg-black/40 hover:text-white border border-white/5 backdrop-blur-md px-6 py-2 rounded-full"
|
||||||
canReset={canRestartSession}
|
|
||||||
onStartClick={onStartRequested}
|
|
||||||
onPauseClick={onPauseRequested}
|
|
||||||
onResetClick={onRestartRequested}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,11 +19,9 @@ import { useHudStatusLine } from "@/shared/lib/useHudStatusLine";
|
|||||||
import { copy } from "@/shared/i18n";
|
import { copy } from "@/shared/i18n";
|
||||||
import { SpaceFocusHudWidget } from "@/widgets/space-focus-hud";
|
import { SpaceFocusHudWidget } from "@/widgets/space-focus-hud";
|
||||||
import { SpaceSetupDrawerWidget } from "@/widgets/space-setup-drawer";
|
import { SpaceSetupDrawerWidget } from "@/widgets/space-setup-drawer";
|
||||||
import { SpaceToolsDockWidget } from "@/widgets/space-tools-dock";
|
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import type { SessionEntryPoint, WorkspaceMode } from "../model/types";
|
import type { SessionEntryPoint, WorkspaceMode } from "../model/types";
|
||||||
import { useAwayReturnRecovery } from "../model/useAwayReturnRecovery";
|
|
||||||
import { useSpaceWorkspaceSelection } from "../model/useSpaceWorkspaceSelection";
|
import { useSpaceWorkspaceSelection } from "../model/useSpaceWorkspaceSelection";
|
||||||
import { useSpaceWorkspaceSessionControls } from "../model/useSpaceWorkspaceSessionControls";
|
import { useSpaceWorkspaceSessionControls } from "../model/useSpaceWorkspaceSessionControls";
|
||||||
import {
|
import {
|
||||||
@@ -31,7 +29,6 @@ import {
|
|||||||
resolveInitialSceneId,
|
resolveInitialSceneId,
|
||||||
resolveInitialSoundPreset,
|
resolveInitialSoundPreset,
|
||||||
resolveInitialTimerLabel,
|
resolveInitialTimerLabel,
|
||||||
resolveTimerLabelFromPresetId,
|
|
||||||
TIMER_SELECTION_PRESETS,
|
TIMER_SELECTION_PRESETS,
|
||||||
} from "../model/workspaceSelection";
|
} from "../model/workspaceSelection";
|
||||||
import { FocusTopToast } from "./FocusTopToast";
|
import { FocusTopToast } from "./FocusTopToast";
|
||||||
@@ -49,14 +46,7 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
thoughts,
|
|
||||||
thoughtCount,
|
|
||||||
addThought,
|
addThought,
|
||||||
removeThought,
|
|
||||||
clearThoughts,
|
|
||||||
restoreThought,
|
|
||||||
restoreThoughts,
|
|
||||||
setThoughtCompleted,
|
|
||||||
} = useThoughtInbox();
|
} = useThoughtInbox();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -108,14 +98,11 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
setSelectedPresetId,
|
setSelectedPresetId,
|
||||||
masterVolume,
|
masterVolume,
|
||||||
setMasterVolume,
|
|
||||||
isMuted,
|
isMuted,
|
||||||
setMuted,
|
|
||||||
} = useSoundPresetSelection(initialSoundPresetId);
|
} = useSoundPresetSelection(initialSoundPresetId);
|
||||||
const {
|
const {
|
||||||
currentSession,
|
currentSession,
|
||||||
isBootstrapping,
|
isBootstrapping,
|
||||||
isMutating: isSessionMutating,
|
|
||||||
timeDisplay,
|
timeDisplay,
|
||||||
phase,
|
phase,
|
||||||
startSession,
|
startSession,
|
||||||
@@ -127,7 +114,6 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
completeSession,
|
completeSession,
|
||||||
advanceGoal,
|
advanceGoal,
|
||||||
abandonSession,
|
abandonSession,
|
||||||
syncCurrentSession,
|
|
||||||
} = useFocusSessionEngine();
|
} = useFocusSessionEngine();
|
||||||
|
|
||||||
const isFocusMode = workspaceMode === "focus";
|
const isFocusMode = workspaceMode === "focus";
|
||||||
@@ -208,13 +194,7 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
setSelectedGoalId: selection.setSelectedGoalId,
|
setSelectedGoalId: selection.setSelectedGoalId,
|
||||||
setShowResumePrompt: selection.setShowResumePrompt,
|
setShowResumePrompt: selection.setShowResumePrompt,
|
||||||
});
|
});
|
||||||
const handleStartRequested = controls.handleStartRequested;
|
|
||||||
|
|
||||||
const awayReturnRecovery = useAwayReturnRecovery({
|
|
||||||
currentSession,
|
|
||||||
isBootstrapping,
|
|
||||||
syncCurrentSession,
|
|
||||||
});
|
|
||||||
const hasEnoughWeeklyData =
|
const hasEnoughWeeklyData =
|
||||||
weeklySummary.last7Days.startedSessions >= 3 &&
|
weeklySummary.last7Days.startedSessions >= 3 &&
|
||||||
(weeklySummary.last7Days.completedSessions >= 2 ||
|
(weeklySummary.last7Days.completedSessions >= 2 ||
|
||||||
@@ -349,22 +329,6 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
hasActiveSession={Boolean(currentSession)}
|
hasActiveSession={Boolean(currentSession)}
|
||||||
playbackState={resolvedPlaybackState}
|
playbackState={resolvedPlaybackState}
|
||||||
sessionPhase={phase ?? 'focus'}
|
sessionPhase={phase ?? 'focus'}
|
||||||
isSessionActionPending={isSessionMutating}
|
|
||||||
canStartSession={controls.canStartSession}
|
|
||||||
canPauseSession={controls.canPauseSession}
|
|
||||||
canRestartSession={controls.canRestartSession}
|
|
||||||
returnPromptMode={awayReturnRecovery.returnPromptMode}
|
|
||||||
onStartRequested={() => {
|
|
||||||
void handleStartRequested();
|
|
||||||
}}
|
|
||||||
onPauseRequested={() => {
|
|
||||||
void controls.handlePauseRequested();
|
|
||||||
}}
|
|
||||||
onRestartRequested={() => {
|
|
||||||
void controls.handleRestartRequested();
|
|
||||||
}}
|
|
||||||
onDismissReturnPrompt={awayReturnRecovery.dismissReturnPrompt}
|
|
||||||
onStatusMessage={pushStatusLine}
|
|
||||||
onIntentUpdate={controls.handleIntentUpdate}
|
onIntentUpdate={controls.handleIntentUpdate}
|
||||||
onGoalFinish={async () => {
|
onGoalFinish={async () => {
|
||||||
const didFinish = await controls.handleGoalComplete();
|
const didFinish = await controls.handleGoalComplete();
|
||||||
@@ -376,6 +340,9 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
return didFinish;
|
return didFinish;
|
||||||
}}
|
}}
|
||||||
onGoalUpdate={controls.handleGoalAdvance}
|
onGoalUpdate={controls.handleGoalAdvance}
|
||||||
|
onStatusMessage={pushStatusLine}
|
||||||
|
onCaptureThought={(note) => addThought(note, selection.selectedScene.name)}
|
||||||
|
onExitRequested={() => void controls.handleExitRequested()}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
@@ -385,47 +352,6 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
actionLabel={activeStatus?.action?.label}
|
actionLabel={activeStatus?.action?.label}
|
||||||
onAction={runActiveAction}
|
onAction={runActiveAction}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SpaceToolsDockWidget
|
|
||||||
isFocusMode={isFocusMode}
|
|
||||||
scenes={selection.setupScenes}
|
|
||||||
sceneAssetMap={sceneAssetMap}
|
|
||||||
selectedSceneId={selection.selectedScene.id}
|
|
||||||
selectedTimerLabel={selection.selectedTimerLabel}
|
|
||||||
timerPresets={TIMER_SELECTION_PRESETS}
|
|
||||||
thoughts={thoughts}
|
|
||||||
thoughtCount={thoughtCount}
|
|
||||||
selectedPresetId={selectedPresetId}
|
|
||||||
onSceneSelect={selection.handleSelectScene}
|
|
||||||
onTimerSelect={(timerLabel) =>
|
|
||||||
selection.handleSelectTimer(timerLabel, true)
|
|
||||||
}
|
|
||||||
onQuickSoundSelect={(presetId) =>
|
|
||||||
selection.handleSelectSound(presetId, true)
|
|
||||||
}
|
|
||||||
sceneRecommendedSoundLabel={selection.selectedScene.recommendedSound}
|
|
||||||
sceneRecommendedTimerLabel={
|
|
||||||
resolveTimerLabelFromPresetId(
|
|
||||||
selection.selectedScene.recommendedTimerPresetId,
|
|
||||||
) ?? selection.selectedTimerLabel
|
|
||||||
}
|
|
||||||
soundVolume={masterVolume}
|
|
||||||
onSetSoundVolume={setMasterVolume}
|
|
||||||
isSoundMuted={isMuted}
|
|
||||||
onSetSoundMuted={setMuted}
|
|
||||||
onCaptureThought={(note) =>
|
|
||||||
addThought(note, selection.selectedScene.name)
|
|
||||||
}
|
|
||||||
onDeleteThought={removeThought}
|
|
||||||
onSetThoughtCompleted={setThoughtCompleted}
|
|
||||||
onRestoreThought={restoreThought}
|
|
||||||
onRestoreThoughts={restoreThoughts}
|
|
||||||
onClearInbox={clearThoughts}
|
|
||||||
onStatusMessage={pushStatusLine}
|
|
||||||
onExitRequested={() => {
|
|
||||||
void controls.handleExitRequested();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user