import { useEffect, useState } from "react"; import { useClientSettings, useUpdateClientSettings } from "../../hooks/useSettings";import { Input } from "../ui/input";import { Switch } from "../ui/switch";import { SettingsPageContainer, SettingsRow, SettingsSection } from "./settingsLayout"; const AUTO_SETTLE_MIN_DAYS = 1;const AUTO_SETTLE_MAX_DAYS = 90;const AUTO_SETTLE_DEFAULT_DAYS = 3; function AutoSettleDaysInput({ value, onCommit,}: { value: number; onCommit: (days: number) => void;}) { // Local draft so the field can be emptied mid-edit; the setting only moves // on valid input and snaps back to the persisted value on blur. const [draft, setDraft] = useState(String(value)); useEffect(() => { setDraft(String(value)); }, [value]); return ( <Input type="number" min={AUTO_SETTLE_MIN_DAYS} max={AUTO_SETTLE_MAX_DAYS} className="w-full sm:w-24" value={draft} onChange={(event) => { setDraft(event.target.value); // Number(), not parseInt: "3.5" must be rejected (not truncated to a // committed 3 while the field shows 3.5) — commit only when the // persisted value matches the displayed one. const parsed = Number(event.target.value); if ( Number.isInteger(parsed) && parsed >= AUTO_SETTLE_MIN_DAYS && parsed <= AUTO_SETTLE_MAX_DAYS ) { onCommit(parsed); } }} onBlur={() => setDraft(String(value))} aria-label="Days of inactivity before auto-settle" /> );} export function BetaSettingsPanel() { const sidebarV2Enabled = useClientSettings((settings) => settings.sidebarV2Enabled); const sidebarAutoSettleAfterDays = useClientSettings( (settings) => settings.sidebarAutoSettleAfterDays, ); const updateSettings = useUpdateClientSettings(); return ( <SettingsPageContainer> <SettingsSection title="Beta features"> <SettingsRow title="Sidebar v2" description="One flat thread list in creation order. Active work renders as rich cards; settled threads collapse to compact rows. Settling requires an up-to-date server — on older servers threads simply stay active. Switch back any time." control={ <Switch checked={sidebarV2Enabled} onCheckedChange={(checked) => updateSettings({ sidebarV2Enabled: Boolean(checked) })} aria-label="Enable the sidebar v2 beta" /> } /> {sidebarV2Enabled ? ( <> <SettingsRow title="Auto-settle inactive threads" description="Threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle." control={ <Switch checked={sidebarAutoSettleAfterDays !== null} onCheckedChange={(checked) => updateSettings({ sidebarAutoSettleAfterDays: checked ? AUTO_SETTLE_DEFAULT_DAYS : null, }) } aria-label="Auto-settle inactive threads" /> } /> {sidebarAutoSettleAfterDays !== null ? ( <SettingsRow title="Days of inactivity before auto-settle" description="Any new activity un-settles a thread automatically." control={ <AutoSettleDaysInput value={sidebarAutoSettleAfterDays} onCommit={(days) => updateSettings({ sidebarAutoSettleAfterDays: days })} /> } /> ) : null} </> ) : null} </SettingsSection> </SettingsPageContainer> );}
import { useEffect, useState } from "react";
import { useClientSettings, useUpdateClientSettings } from "../../hooks/useSettings";
import { Input } from "../ui/input";
import { Switch } from "../ui/switch";
import { SettingsPageContainer, SettingsRow, SettingsSection } from "./settingsLayout";
const AUTO_SETTLE_MIN_DAYS = 1;
const AUTO_SETTLE_MAX_DAYS = 90;
const AUTO_SETTLE_DEFAULT_DAYS = 3;
function AutoSettleDaysInput({
value,
onCommit,
}: {
value: number;
onCommit: (days: number) => void;
}) {
// Local draft so the field can be emptied mid-edit; the setting only moves
// on valid input and snaps back to the persisted value on blur.
const [draft, setDraft] = useState(String(value));
useEffect(() => {
setDraft(String(value));
}, [value]);
return (
<Input
type="number"
min={AUTO_SETTLE_MIN_DAYS}
max={AUTO_SETTLE_MAX_DAYS}
className="w-full sm:w-24"
value={draft}
onChange={(event) => {
setDraft(event.target.value);
// Number(), not parseInt: "3.5" must be rejected (not truncated to a
// committed 3 while the field shows 3.5) — commit only when the
// persisted value matches the displayed one.
const parsed = Number(event.target.value);
if (
Number.isInteger(parsed) &&
parsed >= AUTO_SETTLE_MIN_DAYS &&
parsed <= AUTO_SETTLE_MAX_DAYS
) {
onCommit(parsed);
}
}}
onBlur={() => setDraft(String(value))}
aria-label="Days of inactivity before auto-settle"
/>
);
}
export function BetaSettingsPanel() {
const sidebarV2Enabled = useClientSettings((settings) => settings.sidebarV2Enabled);
const sidebarAutoSettleAfterDays = useClientSettings(
(settings) => settings.sidebarAutoSettleAfterDays,
);
const updateSettings = useUpdateClientSettings();
return (
<SettingsPageContainer>
<SettingsSection title="Beta features">
<SettingsRow
title="Sidebar v2"
description="One flat thread list in creation order. Active work renders as rich cards; settled threads collapse to compact rows. Settling requires an up-to-date server — on older servers threads simply stay active. Switch back any time."
control={
<Switch
checked={sidebarV2Enabled}
onCheckedChange={(checked) => updateSettings({ sidebarV2Enabled: Boolean(checked) })}
aria-label="Enable the sidebar v2 beta"
/>
}
/>
{sidebarV2Enabled ? (
<>
<SettingsRow
title="Auto-settle inactive threads"
description="Threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
control={
<Switch
checked={sidebarAutoSettleAfterDays !== null}
onCheckedChange={(checked) =>
updateSettings({
sidebarAutoSettleAfterDays: checked ? AUTO_SETTLE_DEFAULT_DAYS : null,
})
}
aria-label="Auto-settle inactive threads"
/>
}
/>
{sidebarAutoSettleAfterDays !== null ? (
<SettingsRow
title="Days of inactivity before auto-settle"
description="Any new activity un-settles a thread automatically."
control={
<AutoSettleDaysInput
value={sidebarAutoSettleAfterDays}
onCommit={(days) => updateSettings({ sidebarAutoSettleAfterDays: days })}
/>
}
/>
) : null}
</>
) : null}
</SettingsSection>
</SettingsPageContainer>
);
}