import { useEffect, useState } from "react";import { useClientSettings, useSidebarV2Enabled, useUpdateClientSettings,} from "../../hooks/useSettings";import { Input } from "../ui/input";import { Switch } from "../ui/switch";import { SettingsPageContainer, SettingsRow, SettingsSection } from "./settingsLayout";import { searchableSetting } from "./settingsSearch";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 = useSidebarV2Enabled(); const sidebarAutoSettleAfterDays = useClientSettings( (settings) => settings.sidebarAutoSettleAfterDays, ); const updateSettings = useUpdateClientSettings(); return ( <SettingsPageContainer> <SettingsSection title="Beta features"> <SettingsRow {...searchableSetting("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} // Touching the switch pins the choice, so a nightly build that // defaults v2 on does not flip it back after the user opts out. onCheckedChange={(checked) => updateSettings({ sidebarV2Enabled: Boolean(checked), sidebarV2ConfiguredByUser: true, }) } aria-label="Enable the sidebar v2 beta" /> } /> {sidebarV2Enabled ? ( <> <SettingsRow title={searchableSetting("auto-settle-inactive-threads").title} 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,
useSidebarV2Enabled,
useUpdateClientSettings,
} from "../../hooks/useSettings";
import { Input } from "../ui/input";
import { Switch } from "../ui/switch";
import { SettingsPageContainer, SettingsRow, SettingsSection } from "./settingsLayout";
import { searchableSetting } from "./settingsSearch";
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 = useSidebarV2Enabled();
const sidebarAutoSettleAfterDays = useClientSettings(
(settings) => settings.sidebarAutoSettleAfterDays,
);
const updateSettings = useUpdateClientSettings();
return (
<SettingsPageContainer>
<SettingsSection title="Beta features">
<SettingsRow
{...searchableSetting("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}
// Touching the switch pins the choice, so a nightly build that
// defaults v2 on does not flip it back after the user opts out.
onCheckedChange={(checked) =>
updateSettings({
sidebarV2Enabled: Boolean(checked),
sidebarV2ConfiguredByUser: true,
})
}
aria-label="Enable the sidebar v2 beta"
/>
}
/>
{sidebarV2Enabled ? (
<>
<SettingsRow
title={searchableSetting("auto-settle-inactive-threads").title}
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>
);
}