LibreChat/client/src/components/SidePanel/AssistantSwitcher.tsx
Danny Avila 87d95a9d82
📱 fix: Resolve Android Device and Accessibility Issues of Sidebar Combobox (#3689)
* chore: Update @ariakit/react dependency to version 0.4.8

* refactor: Fix Combobox Android issue with radix-ui

* fix: Improve scrolling behavior by setting abort scroll state to false after scrolling to end

* wip: first pass switcher rewrite

* feat: Add button width calculation for ComboboxComponent

* refactor: Update ComboboxComponent styling for improved layout and appearance

* refactor: Update AssistantSwitcher component to handle null values for assistant names and avatar URLs

* refactor: Update ModelSwitcher component to use SimpleCombobox for improved functionality and styling

* refactor: Update Switcher Separator styling for improved layout and appearance

* refactor: Improve accessibility by adding aria-label to ComboboxComponent select items

* refactor: rename SimpleCombobox -> ControlCombobox
2024-08-18 19:02:46 -04:00

91 lines
3.2 KiB
TypeScript

import { useEffect, useMemo } from 'react';
import { isAssistantsEndpoint, LocalStorageKeys } from 'librechat-data-provider';
import type { AssistantsEndpoint } from 'librechat-data-provider';
import type { SwitcherProps, AssistantListItem } from '~/common';
import { useSetIndexOptions, useSelectAssistant, useLocalize, useAssistantListMap } from '~/hooks';
import { useChatContext, useAssistantsMapContext } from '~/Providers';
import ControlCombobox from '~/components/ui/ControlCombobox';
import Icon from '~/components/Endpoints/Icon';
export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
const localize = useLocalize();
const { setOption } = useSetIndexOptions();
const { index, conversation } = useChatContext();
/* `selectedAssistant` must be defined with `null` to cause re-render on update */
const { assistant_id: selectedAssistant = null, endpoint } = conversation ?? {};
const assistantListMap = useAssistantListMap((res) =>
res.data.map(({ id, name, metadata }) => ({ id, name, metadata })),
);
const assistants: Omit<AssistantListItem, 'model'>[] = useMemo(
() => assistantListMap[endpoint ?? ''] ?? [],
[endpoint, assistantListMap],
);
const assistantMap = useAssistantsMapContext();
const { onSelect } = useSelectAssistant(endpoint as AssistantsEndpoint);
useEffect(() => {
if (!selectedAssistant && assistants && assistants.length && assistantMap) {
const assistant_id =
localStorage.getItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}${endpoint}`) ??
assistants[0]?.id ??
'';
const assistant = assistantMap[endpoint ?? ''][assistant_id];
if (!assistant) {
return;
}
if (!isAssistantsEndpoint(endpoint)) {
return;
}
setOption('model')(assistant.model);
setOption('assistant_id')(assistant_id);
}
}, [index, assistants, selectedAssistant, assistantMap, endpoint, setOption]);
const currentAssistant = assistantMap?.[endpoint ?? '']?.[selectedAssistant ?? ''];
const assistantOptions = useMemo(() => {
return assistants.map((assistant) => {
return {
label: (assistant.name as string | null) ?? '',
value: assistant.id,
icon: (
<Icon
isCreatedByUser={false}
endpoint={endpoint}
assistantName={(assistant.name as string | null) ?? ''}
iconURL={assistant.metadata?.avatar ?? ''}
/>
),
};
});
}, [assistants, endpoint]);
return (
<ControlCombobox
selectedValue={currentAssistant?.id ?? ''}
displayValue={
assistants.find((assistant) => assistant.id === selectedAssistant)?.name ??
localize('com_sidepanel_select_assistant')
}
selectPlaceholder={localize('com_sidepanel_select_assistant')}
searchPlaceholder={localize('com_assistants_search_name')}
isCollapsed={isCollapsed}
ariaLabel={'assistant'}
setValue={onSelect}
items={assistantOptions}
SelectIcon={
<Icon
isCreatedByUser={false}
endpoint={endpoint}
assistantName={currentAssistant?.name ?? ''}
iconURL={currentAssistant?.metadata?.avatar ?? ''}
/>
}
/>
);
}