Add settings toggle for previous-conversation context (#66).
Unit Tests / test (pull_request) Successful in 11s

Wire Account Preferences to use_conversation_context via preferences API,
with tooltip copy plus loading and error handling on update.
This commit is contained in:
2026-08-04 08:13:52 -05:00
parent fb3e138ea2
commit 317b6cd725
2 changed files with 101 additions and 4 deletions
@@ -1,5 +1,7 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import Tooltip from '@mui/material/Tooltip';
import InfoOutlined from '@mui/icons-material/InfoOutlined';
import { import {
useMaterialUIController, useMaterialUIController,
setDarkMode, setDarkMode,
@@ -48,6 +50,16 @@ const SettingLabel = styled.span`
font-size: 1.1rem; font-size: 1.1rem;
color: ${({ theme }) => theme.colors.text}; color: ${({ theme }) => theme.colors.text};
font-weight: 500; font-weight: 500;
display: inline-flex;
align-items: center;
gap: 0.4rem;
`;
const InfoIcon = styled(InfoOutlined)`
font-size: 1rem !important;
opacity: 0.7;
cursor: help;
vertical-align: middle;
`; `;
const ToggleSwitch = styled.label` const ToggleSwitch = styled.label`
@@ -93,6 +105,11 @@ const Checkbox = styled.input`
&:checked + ${Slider}:before { &:checked + ${Slider}:before {
transform: translateX(26px); transform: translateX(26px);
} }
&:disabled + ${Slider} {
opacity: 0.5;
cursor: not-allowed;
}
`; `;
const ColorPickerContainer = styled.div` const ColorPickerContainer = styled.div`
@@ -115,6 +132,23 @@ const ColorButton = styled.button<{ color: string; isSelected: boolean }>`
} }
`; `;
const ErrorText = styled.p`
color: #ff6b6b;
margin: 0.75rem 0 0 0;
font-size: 0.95rem;
`;
const CONTEXT_TOOLTIP =
'This will use previous conversations to better customize the experience.';
function prefsErrorMessage(error: unknown, fallback: string): string {
const axiosError = error as {
response?: { data?: { detail?: string } };
message?: string;
};
return axiosError.response?.data?.detail || axiosError.message || fallback;
}
type PreferencesValues = { type PreferencesValues = {
order: boolean; order: boolean;
} }
@@ -128,6 +162,10 @@ const ThemeSettingsCard = (): JSX.Element => {
const themeColors = Object.keys(palettes); const themeColors = Object.keys(palettes);
const [order, setOrder] = useState<boolean>(true); const [order, setOrder] = useState<boolean>(true);
const [useConversationContext, setUseConversationContext] = useState<boolean>(false);
const [prefsLoading, setPrefsLoading] = useState<boolean>(true);
const [contextSaving, setContextSaving] = useState<boolean>(false);
const [prefsError, setPrefsError] = useState<string>('');
const { updatePreferences } = useContext(PreferenceContext); const { updatePreferences } = useContext(PreferenceContext);
const handleConversationOrder = async ({ order }: PreferencesValues): Promise<void> => { const handleConversationOrder = async ({ order }: PreferencesValues): Promise<void> => {
@@ -141,17 +179,50 @@ const ThemeSettingsCard = (): JSX.Element => {
} }
} }
async function getConversationOrder() { async function getConversationPreferences() {
setPrefsLoading(true);
setPrefsError('');
try { try {
const { data, }: AxiosResponse<PreferencesType> = await axiosInstance.get(`/conversation_preferences`); const { data }: AxiosResponse<PreferencesType> = await axiosInstance.get(
`/conversation_preferences`
);
setOrder(data.order); setOrder(data.order);
setUseConversationContext(Boolean(data.use_conversation_context));
} catch (error) { } catch (error) {
console.log(error) setPrefsError(
prefsErrorMessage(error, 'Could not load account preferences.')
);
} finally {
setPrefsLoading(false);
} }
} }
const handleConversationContext = async (enabled: boolean): Promise<void> => {
const previous = useConversationContext;
setUseConversationContext(enabled);
setContextSaving(true);
setPrefsError('');
try {
const { data }: AxiosResponse<PreferencesType> = await axiosInstance.post(
'/conversation_preferences',
{ use_conversation_context: enabled }
);
setUseConversationContext(Boolean(data.use_conversation_context));
} catch (error) {
setUseConversationContext(previous);
setPrefsError(
prefsErrorMessage(
error,
'Could not update conversation context setting. Try again.'
)
);
} finally {
setContextSaving(false);
}
};
useEffect(() => { useEffect(() => {
getConversationOrder(); getConversationPreferences();
}, []) }, [])
const handleDarkMode = () => setDarkMode(dispatch, !darkMode); const handleDarkMode = () => setDarkMode(dispatch, !darkMode);
@@ -193,6 +264,7 @@ const ThemeSettingsCard = (): JSX.Element => {
<Checkbox <Checkbox
type="checkbox" type="checkbox"
checked={order} checked={order}
disabled={prefsLoading}
onChange={() => { onChange={() => {
setOrder(!order); setOrder(!order);
handleConversationOrder({ order: !order }) handleConversationOrder({ order: !order })
@@ -202,6 +274,30 @@ const ThemeSettingsCard = (): JSX.Element => {
</ToggleSwitch> </ToggleSwitch>
</SettingRow> </SettingRow>
<SettingRow>
<SettingLabel>
Use previous conversations
<Tooltip title={CONTEXT_TOOLTIP}>
<span>
<InfoIcon aria-label={CONTEXT_TOOLTIP} />
</span>
</Tooltip>
</SettingLabel>
<ToggleSwitch>
<Checkbox
type="checkbox"
checked={useConversationContext}
disabled={prefsLoading || contextSaving}
onChange={() => {
handleConversationContext(!useConversationContext);
}}
/>
<Slider />
</ToggleSwitch>
</SettingRow>
{prefsError ? <ErrorText role="alert">{prefsError}</ErrorText> : null}
</GlassCard> </GlassCard>
); );
}; };
+1
View File
@@ -269,6 +269,7 @@ export interface AccountType {
export interface PreferencesType { export interface PreferencesType {
order: boolean; order: boolean;
use_conversation_context?: boolean;
} }
export class Account { export class Account {