// Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useCallback, useId, useMemo, useState } from 'react'; import type { LocalizerType } from '../types/I18N.std.js'; import { AxoSymbol } from '../axo/AxoSymbol.dom.js'; import { AxoButton } from '../axo/AxoButton.dom.js'; import { AxoDialog } from '../axo/AxoDialog.dom.js'; import { CallQualitySurvey } from '../types/CallQualitySurvey.std.js'; import { tw } from '../axo/tw.dom.js'; import { missingCaseError } from '../util/missingCaseError.std.js'; import { AxoCheckbox } from '../axo/AxoCheckbox.dom.js'; import { strictAssert } from '../util/assert.std.js'; import { Tooltip, TooltipPlacement } from './Tooltip.dom.js'; import { I18n } from './I18n.dom.js'; import Issue = CallQualitySurvey.Issue; function DiagnosticInfoLink({ parts, onClick, }: { parts: Array; onClick: () => void; }): React.JSX.Element { return ( ); } enum Page { HOW_WAS_YOUR_CALL, WHAT_ISSUES_DID_YOU_HAVE, CONFIRM_SUBMISSION, } export type CallQualitySurveyDialogProps = Readonly<{ i18n: LocalizerType; open: boolean; onOpenChange: (open: boolean) => void; onSubmit: (form: CallQualitySurvey.Form) => void; onViewDebugLog: () => void; onViewDiagnosticInfo: () => void; isSubmitting?: boolean; }>; export function CallQualitySurveyDialog( props: CallQualitySurveyDialogProps ): React.JSX.Element { const { i18n, onSubmit, onViewDebugLog, onViewDiagnosticInfo, isSubmitting } = props; const [page, setPage] = useState(Page.HOW_WAS_YOUR_CALL); const [userSatisfied, setUserSatisfied] = useState(null); const [callQualityIssues, setCallQualityIssues] = useState< ReadonlySet >(() => new Set()); const [additionalIssuesDescription, setAdditionalIssuesDescription] = useState(''); const [userHasSeenOtherForm, setUserHasSeenOtherForm] = useState(false); const debugLogCheckboxId = useId(); const otherTextareaErrorId = useId(); const [shareDebugLog, setShareDebugLog] = useState(false); // Validation for the issues page const hasOtherIssue = callQualityIssues.has(Issue.OTHER); const isOtherInputValid = useMemo(() => { if (!hasOtherIssue) { return true; } return additionalIssuesDescription.trim() !== ''; }, [hasOtherIssue, additionalIssuesDescription]); const canContinueFromIssuesPage = callQualityIssues.size > 0 && isOtherInputValid; const showOtherInputError = userHasSeenOtherForm && !isOtherInputValid; const handleSubmit = useCallback(() => { strictAssert(userSatisfied != null, 'userSatisfied cannot be null'); const form: CallQualitySurvey.Form = { userSatisfied, // TODO: Only include if `!userSatisfied` callQualityIssues, // TODO: Only include if `callQualityIssues.has(Issue.OTHER)` additionalIssuesDescription, shareDebugLog, }; onSubmit(form); }, [ onSubmit, userSatisfied, callQualityIssues, additionalIssuesDescription, shareDebugLog, ]); const renderDiagnosticInfoLink = useCallback( (parts: Array) => ( ), [onViewDiagnosticInfo] ); const diagnosticInfoLinkComponents = useMemo( () => ({ diagnosticInfoLink: renderDiagnosticInfoLink, }), [renderDiagnosticInfoLink] ); return ( {page === Page.HOW_WAS_YOUR_CALL && ( <> {i18n('icu:CallQualitySurvey__HowWasYourCall__PageTitle')}

{i18n( 'icu:CallQualitySurvey__HowWasYourCall__PageDescription' )}

{ setUserSatisfied(false); setPage(Page.WHAT_ISSUES_DID_YOU_HAVE); }} label={i18n( 'icu:CallQualitySurvey__HowWasYourCall__Response__HadIssues' )} /> { setUserSatisfied(true); setPage(Page.CONFIRM_SUBMISSION); }} label={i18n( 'icu:CallQualitySurvey__HowWasYourCall__Response__Great' )} />
)} {page === Page.WHAT_ISSUES_DID_YOU_HAVE && ( <> { setPage(Page.HOW_WAS_YOUR_CALL); }} /> {i18n('icu:CallQualitySurvey__WhatIssuesDidYouHave__PageTitle')}

{i18n( 'icu:CallQualitySurvey__WhatIssuesDidYouHave__IssuesList__Heading' )}

{ if (!newIssues.has(Issue.OTHER)) { setUserHasSeenOtherForm(false); } setCallQualityIssues(newIssues); }} />
{hasOtherIssue && (