207 lines
6.2 KiB
TypeScript
207 lines
6.2 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useState, type KeyboardEvent as ReactKeyboardEvent } from 'react';
|
|
import { usePlanTier } from '@/entities/plan';
|
|
import type { RecentThought } from '@/entities/session';
|
|
import { copy } from '@/shared/i18n';
|
|
import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine';
|
|
import type { SpaceAnchorPopoverId, SpaceUtilityPanelId } from './types';
|
|
|
|
interface UseSpaceToolsDockHandlersParams {
|
|
setIdle: (idle: boolean) => void;
|
|
setOpenPopover: (popover: SpaceAnchorPopoverId | null) => void;
|
|
setUtilityPanel: (panel: SpaceUtilityPanelId | null) => void;
|
|
onCaptureThought: (note: string) => RecentThought | null;
|
|
onDeleteThought: (thoughtId: string) => RecentThought | null;
|
|
onSetThoughtCompleted: (thoughtId: string, isCompleted: boolean) => RecentThought | null;
|
|
onRestoreThought: (thought: RecentThought) => void;
|
|
onRestoreThoughts: (thoughts: RecentThought[]) => void;
|
|
onClearInbox: () => RecentThought[];
|
|
onStatusMessage: (payload: HudStatusLinePayload) => void;
|
|
onSetSoundVolume: (volume: number) => void;
|
|
onSetSoundMuted: (muted: boolean) => void;
|
|
soundVolume: number;
|
|
isSoundMuted: boolean;
|
|
showVolumeFeedback: (volume: number) => void;
|
|
}
|
|
|
|
export const useSpaceToolsDockHandlers = ({
|
|
setIdle,
|
|
setOpenPopover,
|
|
setUtilityPanel,
|
|
onCaptureThought,
|
|
onDeleteThought,
|
|
onSetThoughtCompleted,
|
|
onRestoreThought,
|
|
onRestoreThoughts,
|
|
onClearInbox,
|
|
onStatusMessage,
|
|
onSetSoundVolume,
|
|
onSetSoundMuted,
|
|
soundVolume,
|
|
isSoundMuted,
|
|
showVolumeFeedback,
|
|
}: UseSpaceToolsDockHandlersParams) => {
|
|
const { toolsDock } = copy.space;
|
|
const [noteDraft, setNoteDraft] = useState('');
|
|
const { plan, setPlan } = usePlanTier();
|
|
|
|
const openUtilityPanel = useCallback((panel: SpaceUtilityPanelId) => {
|
|
setIdle(false);
|
|
setOpenPopover(null);
|
|
setUtilityPanel(panel);
|
|
}, [setIdle, setOpenPopover, setUtilityPanel]);
|
|
|
|
const handleNoteSubmit = useCallback(() => {
|
|
const trimmedNote = noteDraft.trim();
|
|
|
|
if (!trimmedNote) {
|
|
return;
|
|
}
|
|
|
|
const addedThought = onCaptureThought(trimmedNote);
|
|
|
|
if (!addedThought) {
|
|
return;
|
|
}
|
|
|
|
setNoteDraft('');
|
|
onStatusMessage({
|
|
message: toolsDock.inboxSaved,
|
|
durationMs: 4200,
|
|
priority: 'undo',
|
|
action: {
|
|
label: toolsDock.undo,
|
|
onClick: () => {
|
|
const removed = onDeleteThought(addedThought.id);
|
|
|
|
if (!removed) {
|
|
return;
|
|
}
|
|
|
|
onStatusMessage({ message: toolsDock.inboxSaveUndone });
|
|
},
|
|
},
|
|
});
|
|
}, [noteDraft, onCaptureThought, onDeleteThought, onStatusMessage, toolsDock.inboxSaved, toolsDock.inboxSaveUndone, toolsDock.undo]);
|
|
|
|
const handleInboxComplete = useCallback((thought: RecentThought) => {
|
|
onSetThoughtCompleted(thought.id, !thought.isCompleted);
|
|
}, [onSetThoughtCompleted]);
|
|
|
|
const handleInboxDelete = useCallback((thought: RecentThought) => {
|
|
const removedThought = onDeleteThought(thought.id);
|
|
|
|
if (!removedThought) {
|
|
return;
|
|
}
|
|
|
|
onStatusMessage({
|
|
message: toolsDock.deleted,
|
|
durationMs: 4200,
|
|
priority: 'undo',
|
|
action: {
|
|
label: toolsDock.undo,
|
|
onClick: () => {
|
|
onRestoreThought(removedThought);
|
|
onStatusMessage({ message: toolsDock.deleteUndone });
|
|
},
|
|
},
|
|
});
|
|
}, [onDeleteThought, onRestoreThought, onStatusMessage, toolsDock.deleted, toolsDock.deleteUndone, toolsDock.undo]);
|
|
|
|
const handleInboxClear = useCallback(() => {
|
|
const snapshot = onClearInbox();
|
|
|
|
if (snapshot.length === 0) {
|
|
onStatusMessage({ message: toolsDock.emptyToClear });
|
|
return;
|
|
}
|
|
|
|
onStatusMessage({
|
|
message: toolsDock.clearedAll,
|
|
durationMs: 4200,
|
|
priority: 'undo',
|
|
action: {
|
|
label: toolsDock.undo,
|
|
onClick: () => {
|
|
onRestoreThoughts(snapshot);
|
|
onStatusMessage({ message: toolsDock.restored });
|
|
},
|
|
},
|
|
});
|
|
}, [onClearInbox, onRestoreThoughts, onStatusMessage, toolsDock.clearedAll, toolsDock.emptyToClear, toolsDock.restored, toolsDock.undo]);
|
|
|
|
const handlePlanPillClick = useCallback(() => {
|
|
if (plan === 'pro') {
|
|
openUtilityPanel('manage-plan');
|
|
return;
|
|
}
|
|
|
|
onStatusMessage({ message: toolsDock.normalPlanInfo });
|
|
}, [openUtilityPanel, onStatusMessage, plan, toolsDock.normalPlanInfo]);
|
|
|
|
const handleLockedClick = useCallback((source: string) => {
|
|
onStatusMessage({ message: toolsDock.proFeatureLocked(source) });
|
|
openUtilityPanel('paywall');
|
|
}, [onStatusMessage, openUtilityPanel, toolsDock]);
|
|
|
|
const handleSelectProFeature = useCallback((featureId: string) => {
|
|
const label =
|
|
featureId === 'daily-plan'
|
|
? toolsDock.featureLabels.dailyPlan
|
|
: featureId === 'rituals'
|
|
? toolsDock.featureLabels.rituals
|
|
: toolsDock.featureLabels.weeklyReview;
|
|
|
|
onStatusMessage({ message: toolsDock.proFeaturePending(label) });
|
|
}, [onStatusMessage, toolsDock]);
|
|
|
|
const handleStartPro = useCallback(() => {
|
|
setPlan('pro');
|
|
onStatusMessage({ message: toolsDock.purchaseMock });
|
|
openUtilityPanel('control-center');
|
|
}, [onStatusMessage, openUtilityPanel, setPlan, toolsDock.purchaseMock]);
|
|
|
|
const handleVolumeChange = useCallback((nextVolume: number) => {
|
|
const clamped = Math.min(100, Math.max(0, nextVolume));
|
|
onSetSoundVolume(clamped);
|
|
|
|
if (isSoundMuted && clamped > 0) {
|
|
onSetSoundMuted(false);
|
|
}
|
|
|
|
showVolumeFeedback(clamped);
|
|
}, [isSoundMuted, onSetSoundMuted, onSetSoundVolume, showVolumeFeedback]);
|
|
|
|
const handleVolumeKeyDown = useCallback((event: ReactKeyboardEvent<HTMLInputElement>) => {
|
|
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
const step = event.shiftKey ? 10 : 5;
|
|
const delta = event.key === 'ArrowRight' ? step : -step;
|
|
handleVolumeChange(soundVolume + delta);
|
|
}, [handleVolumeChange, soundVolume]);
|
|
|
|
return {
|
|
noteDraft,
|
|
setNoteDraft,
|
|
plan,
|
|
setPlan,
|
|
openUtilityPanel,
|
|
handleNoteSubmit,
|
|
handleInboxComplete,
|
|
handleInboxDelete,
|
|
handleInboxClear,
|
|
handlePlanPillClick,
|
|
handleLockedClick,
|
|
handleSelectProFeature,
|
|
handleStartPro,
|
|
handleVolumeChange,
|
|
handleVolumeKeyDown,
|
|
};
|
|
};
|