import { maxCharacterSrDebounceDelay, maxCharacterWarningLimit } from 'config' import { useConfig } from 'domains/config/hooks' import { useFormControl } from 'domains/forms/hooks' import { useI18n } from 'domains/i18n/hooks' import { useAppDispatch } from 'domains/store' import { clearAbortTransaction } from 'domains/store/slice' import { useEffect, useMemo } from 'preact/hooks' import { useLiveRegion } from 'ui/hooks/live-region-hooks' import { useEntryTextLimit, useSeamlyStateContext, } from 'ui/hooks/seamly-state-hooks' import { debounce } from 'ui/utils/general-utils' export function useCharacterLimit(controlName: 'textMessageEntry') { const { t } = useI18n() const { sendAssertive } = useLiveRegion() const { hasLimit, limit } = useEntryTextLimit() const debouncedSendAssertive = useMemo( () => debounce(sendAssertive, maxCharacterSrDebounceDelay), [sendAssertive], ) const validateLimit = useMemo(() => { return debounce((_reachedCharacterWarning, _remainingChars) => { if (_reachedCharacterWarning) { debouncedSendAssertive( t('input.srCharacterLimitText', { limit: _remainingChars }), ) } }, maxCharacterSrDebounceDelay) }, [debouncedSendAssertive, t]) const [{ value }] = useFormControl(controlName) const remainingChars = typeof limit === 'number' && hasLimit && value ? limit - value.length : limit const reachedCharacterWarning = hasLimit && typeof remainingChars == 'number' ? remainingChars <= maxCharacterWarningLimit : false const reachedCharacterLimit = hasLimit && typeof remainingChars === 'number' ? remainingChars < 0 : false useEffect(() => { validateLimit(reachedCharacterWarning, remainingChars) }, [reachedCharacterWarning, remainingChars, validateLimit]) return { hasCharacterLimit: hasLimit, characterLimit: limit, reachedCharacterWarning, reachedCharacterLimit, remainingChars, } } export const useEntryTextTranslation = (controlName: 'textMessageEntry') => { const { hasCharacterLimit, characterLimit } = useCharacterLimit(controlName) const { entryMeta: { active: entryType, optionsOverride }, } = useSeamlyStateContext() let labelOverride: string | null = null let placeholderOverride: string | null = null if ( entryType && optionsOverride && typeof optionsOverride[entryType] === 'object' ) { labelOverride = optionsOverride[entryType].label placeholderOverride = optionsOverride[entryType].placeholder } const { alwaysShowEntryLabel } = useConfig() const { t } = useI18n() const placeholder: string = useMemo( () => t('input.inputPlaceholder', { hasLimit: hasCharacterLimit, text: placeholderOverride || t('input.inputPlaceholderText'), limit: hasCharacterLimit ? characterLimit : null, }), [t, hasCharacterLimit, characterLimit, placeholderOverride], ) const label: string = useMemo( () => t('input.inputLabel', { hasLimit: !labelOverride ? hasCharacterLimit : false, text: labelOverride || t('input.inputLabelText'), limit: !labelOverride && hasCharacterLimit ? characterLimit : null, }), [t, hasCharacterLimit, characterLimit, labelOverride], ) const labelClass = useMemo( () => alwaysShowEntryLabel === true || labelOverride ? 'label' : 'visually-hidden', [alwaysShowEntryLabel, labelOverride], ) return { placeholder, label, labelClass } } export const useEntryAbortTransaction = () => { const dispatch = useAppDispatch() const { entryMeta: { actions, translatedActions }, } = useSeamlyStateContext() const clearEntryAbortTransaction = () => { dispatch(clearAbortTransaction()) } const abortTransaction = translatedActions?.abortTransaction || actions?.abortTransaction return { abortTransaction, clearEntryAbortTransaction, } }