1 import { useEffect, useMemo, useState } from 'react';
3 import { useUserSettings } from '@proton/components/hooks';
4 import useAssistantFeatureEnabled from '@proton/components/hooks/assistant/useAssistantFeatureEnabled';
5 import useAssistantSubscriptionStatus from '@proton/components/hooks/assistant/useAssistantSubscriptionStatus';
6 import type { AssistantCommonProps } from '@proton/llm/lib';
7 import { getAssistantHasCompatibleBrowser, getAssistantHasCompatibleHardware } from '@proton/llm/lib';
8 import useAssistantErrors from '@proton/llm/lib/hooks/useAssistantErrors';
9 import useOpenedAssistants from '@proton/llm/lib/hooks/useOpenedAssistants';
10 import { AI_ASSISTANT_ACCESS } from '@proton/shared/lib/interfaces';
11 import { useFlag } from '@proton/unleash';
13 const { OFF } = AI_ASSISTANT_ACCESS;
15 const useAssistantCommons = (): AssistantCommonProps => {
16 const assistantFeatureEnabled = useAssistantFeatureEnabled();
17 const canKeepFormatting = useFlag('ComposerAssistantFormatting');
18 const [{ AIAssistantFlags }] = useUserSettings();
20 const assistantSubscriptionStatus = useAssistantSubscriptionStatus();
22 const { errors, addSpecificError, cleanSpecificErrors, addGlobalError, cleanGlobalErrors } = useAssistantErrors();
23 const openedAssistantsState = useOpenedAssistants({ cleanSpecificErrors });
25 // Show the feature in the UI only when the feature flag is ON and setting is not OFF
26 const canShowAssistant = useMemo(() => {
27 // We don't show the assistant to users that are not in supported plan or have the feature disabled
28 // In addition, we need to hide the assistant if user can't buy scribe because of payments migration
29 const assistantEnabled = assistantFeatureEnabled.enabled;
30 if (!assistantEnabled || AIAssistantFlags === OFF) {
35 }, [assistantFeatureEnabled, AIAssistantFlags]);
37 // When hardware is not compatible, show an error in the UI
38 const [hasCompatibleHardware, setHasCompatibleHardware] = useState(false);
40 // When the browser is not compatible, suggest the user to run it on a compatible browser
41 const [hasCompatibleBrowser, setHasCompatibleBrowser] = useState(getAssistantHasCompatibleBrowser());
43 // The assistant can be run if the user is paying the feature or is in trial
44 const canUseAssistant = assistantSubscriptionStatus.canUseAssistant;
46 const handleCheckHardwareCompatibility = async () => {
47 const compatibility = await getAssistantHasCompatibleHardware();
48 // If the webGPU is not working on Firefox, propose to the user to use the desktop app instead
49 if (compatibility === 'noWebGpuFirefox' || compatibility === 'noWebGpuSafari') {
50 setHasCompatibleBrowser(false);
51 setHasCompatibleHardware(true);
52 return { hasCompatibleBrowser: false, hasCompatibleHardware: true };
54 setHasCompatibleHardware(compatibility === 'ok');
56 hasCompatibleBrowser: getAssistantHasCompatibleBrowser(),
57 hasCompatibleHardware: compatibility === 'ok',
63 if (canShowAssistant && AIAssistantFlags === AI_ASSISTANT_ACCESS.CLIENT_ONLY) {
64 void handleCheckHardwareCompatibility();
65 } else if (AIAssistantFlags === AI_ASSISTANT_ACCESS.SERVER_ONLY) {
66 setHasCompatibleBrowser(true);
67 setHasCompatibleHardware(true);
69 }, [canShowAssistant, AIAssistantFlags]);
73 hasCompatibleHardware,
84 assistantSubscriptionStatus,
85 handleCheckHardwareCompatibility,
86 ...openedAssistantsState,
90 export default useAssistantCommons;