overseer disableable

This commit is contained in:
legop3
2026-06-18 23:26:16 -04:00
parent 80f1f82d6e
commit 8a556a3634
11 changed files with 211 additions and 33 deletions
+21 -2
View File
@@ -122,6 +122,14 @@ function MobileFeatureTabs({
ownAudioForward?.source === 'upload' &&
ownAudioForward?.state === 'playing',
);
const showOverseerPreferencePanel = useSessionSelector((state) => {
const vote = state.session?.overseerVote;
// Match the panel's server-owned voting gate so the mobile chat row can
// collapse to a single-column layout when voting is unavailable, including
// disabled service and direct-address mode.
return Boolean(vote?.votingEnabled);
});
return (
<section className="text-base">
<Tabs defaultTab="chat" currentTab={activeTab} onTabChange={setActiveTab}>
@@ -150,8 +158,19 @@ function MobileFeatureTabs({
<div className={`grid ${themeGapClass} md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]`}>
<SocialButtonsGrid />
</div>
<div className={`grid ${themeGapClass} grid-cols-[minmax(0,1fr)_minmax(0,1fr)]`}>
<OverseerPreferencePanel />
<div
className={`grid ${themeGapClass} ${
showOverseerPreferencePanel
? 'grid-cols-[minmax(0,1fr)_minmax(0,1fr)]'
: 'grid-cols-[minmax(0,1fr)]'
}`}
>
{/*
The overseer vote panel is server-vote-gated. When voting
is unavailable, the raw user pile should reclaim the row
instead of sitting in a half-empty two-column layout.
*/}
{showOverseerPreferencePanel ? <OverseerPreferencePanel /> : null}
<RawUserPilePanel hideNicknameForm />
</div>
</div>
@@ -40,7 +40,7 @@ function OverseerMemoryPopup({ memory, onClose }) {
);
}
export default function OverseerPreferencePanel() {
function OverseerPreferencePanelBody() {
const { identifySession } = useSessionActions();
const vote = useSessionSelector((state) => state.session?.overseerVote || null);
const overseerMemory = useSessionSelector((state) => state.overseerMemory || null);
@@ -102,3 +102,21 @@ export default function OverseerPreferencePanel() {
</>
);
}
export default function OverseerPreferencePanel() {
const visible = useSessionSelector((state) => {
const vote = state.session?.overseerVote;
// The server owns whether voting has any effect. The panel appears only
// when the autonomous vote-gated scheduler is active; direct-address mode
// is intentionally hidden because users invoke it by starting a chat
// message with the configured overseer name instead of voting it on.
return Boolean(vote?.votingEnabled);
});
if (!visible) {
return null;
}
return <OverseerPreferencePanelBody />;
}
+22 -2
View File
@@ -165,6 +165,14 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
ownAudioForward?.source === 'upload' &&
ownAudioForward?.state === 'playing',
);
const showOverseerPreferencePanel = useSessionSelector((state) => {
const vote = state.session?.overseerVote;
// Match the panel's server-owned voting gate so the measured desktop chat
// dock can give the side-column height to the user pile when voting is
// unavailable, including disabled service and direct-address mode.
return Boolean(vote?.votingEnabled);
});
useLayoutEffect(() => {
const chatDock = chatDockRef.current;
@@ -304,8 +312,20 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
style={{ height: `${chatDockHeight}px` }}
>
<ChatPanel fillHeight />
<div className={`grid min-h-0 ${themeGapClass} grid-rows-[auto_minmax(0,1fr)]`}>
<OverseerPreferencePanel />
<div
className={`grid min-h-0 ${themeGapClass} ${
showOverseerPreferencePanel
? 'grid-rows-[auto_minmax(0,1fr)]'
: 'grid-rows-[minmax(0,1fr)]'
}`}
>
{/*
This side column is height-constrained by the measured
chat dock row. When overseer voting is unavailable on the
server, the user pile becomes the only child and should
receive the whole side-column height.
*/}
{showOverseerPreferencePanel ? <OverseerPreferencePanel /> : null}
<RawUserPilePanel compact hideNicknameForm fillHeight />
</div>
</div>