import { useUserHasResponded } from 'domains/app/hooks' import { useConfig } from 'domains/config/hooks' import { useI18n } from 'domains/i18n/hooks' import { selectLastUnreadEvent } from 'domains/store/selectors' import { useTranslatedEventData } from 'domains/translations/hooks' import { useVisibility } from 'domains/visibility/hooks' import { className } from 'lib/css' import { RefCallback } from 'preact' import { useCallback, useEffect, useMemo, useRef } from 'preact/hooks' import { useSelector } from 'react-redux' import AppView from 'ui/components/view/app-view' import InlineView from 'ui/components/view/inline-view' import WindowView from 'ui/components/view/window-view' import { useSeamlyAppContainerClassNames } from 'ui/hooks/component-helper-hooks' import { useSeamlyContainerElement } from 'ui/hooks/focus-helper-hooks' import { useSeamlyCurrentAgent, useSeamlyServiceInfo, } from 'ui/hooks/seamly-state-hooks' import useNotification from 'ui/hooks/use-notifications' const ViewComponentsMap = { app: AppView, inline: InlineView, window: WindowView, } function stripHtml(html?: string) { const tmp = document.createElement('div') tmp.innerHTML = html || '' return tmp.textContent || tmp.innerText || '' } const View = ({ children = undefined }) => { const { sendNotification } = useNotification() const [, setSeamlyContainerElement] = useSeamlyContainerElement() const config = useConfig() const currentAgent = useSeamlyCurrentAgent() const { proactiveMessages } = useSeamlyServiceInfo() const { isOpen, isVisible } = useVisibility() const appContainerClassNames = useSeamlyAppContainerClassNames() const userHasResponded = useUserHasResponded() const lastUnreadEvent = useSelector(selectLastUnreadEvent) const { body } = useTranslatedEventData(lastUnreadEvent) const { userLocale } = useI18n() const prevLastUnreadEventId = useRef(undefined) const notificationBody = useMemo(() => { if (!body) return '' if (typeof body === 'string') return stripHtml(body) if ('text' in body) return stripHtml(body.text) if ('prompt' in body && body.prompt && 'text' in body.prompt) return stripHtml(body.prompt?.text) return '' }, [body]) useEffect(() => { if ( !currentAgent?.name || !notificationBody || !proactiveMessages || !lastUnreadEvent || prevLastUnreadEventId.current === lastUnreadEvent.payload?.id ) { return } sendNotification(currentAgent.name, { body: notificationBody, icon: currentAgent?.avatar || undefined, }) prevLastUnreadEventId.current = lastUnreadEvent.payload.id }, [ currentAgent?.avatar, currentAgent?.name, lastUnreadEvent, notificationBody, proactiveMessages, sendNotification, ]) const ViewComponent = ViewComponentsMap[config.layoutMode] const containerElementRef = useCallback>( (container) => { if (typeof setSeamlyContainerElement !== 'function') return setSeamlyContainerElement(container) }, [setSeamlyContainerElement], ) const blockLang = useMemo(() => { if (userLocale) { const htmlElementLang = document .querySelector('html') ?.getAttribute('lang') if (htmlElementLang !== userLocale) { return userLocale } } return undefined }, [userLocale]) if (!ViewComponent) { console.warn('"layoutMode" should be one of "app", "inline" or "window"') return null } const defaultClassNames = [ `app--layout-${config.layoutMode}`, `namespace--${config.namespace}`, ] const classNames = ['app', ...defaultClassNames] if (typeof appContainerClassNames !== 'function') { classNames.push(...appContainerClassNames) } else { classNames.push(...appContainerClassNames(config)) } if (!isOpen && config.layoutMode !== 'app') { classNames.push('app--collapsed') } if (userHasResponded) { classNames.push('app--user-responded') } return isVisible ? (
{children || }
) : null } export default View