Skip to content

Commit 48652ac

Browse files
authored
chore(condo): DOMA-13010 Hide UseDeskWidget.tsx when AI Overlay is open + upgrade AI Overlay visuals a bit (#7293)
* chore(condo): DOMA-13010 Hide UseDeskWidget.tsx when AI Overlay is open + upgrade AI Overlay visuals a bit * fix(condo): DOMA-13010 fix linter related issues * fix(condo): DOMA-13010 fixes on review * fix(condo): DOMA-13010 fixes on review
1 parent 415ac92 commit 48652ac

File tree

5 files changed

+90
-13
lines changed

5 files changed

+90
-13
lines changed

apps/condo/domains/ai/components/AIContext.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ export const useAIContext = () => {
2121
}
2222

2323
export const AIProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
24+
const storage = new LocalStorageManager<number>()
25+
2426
const [isAIOverlayOpen, setIsAIOverlayOpen] = useState(false)
2527
const [aiOverlayWidth, setAIOverlayWidthState] = useState(() => {
26-
const storage = new LocalStorageManager<number>()
2728
return storage.getItem('aiOverlayWidth') || 600
2829
})
2930

3031
const setAIOverlayWidth = (width: number) => {
3132
setAIOverlayWidthState(width)
32-
const storage = new LocalStorageManager<number>()
3333
storage.setItem('aiOverlayWidth', width)
3434
}
3535

apps/condo/domains/ai/components/AIOverlay/AIOverlay.module.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
top: 0;
6565
left: 0;
6666
z-index: 1001;
67+
display: flex;
68+
align-items: center;
69+
justify-content: center;
6770
width: 4px;
6871
height: 100%;
6972
background: transparent;
@@ -81,6 +84,14 @@
8184
border-left: 2px solid var(--condo-global-color-green-5);
8285
}
8386

87+
.resize-handle.at-min-width {
88+
cursor: w-resize;
89+
}
90+
91+
.resize-handle.at-max-width {
92+
cursor: e-resize;
93+
}
94+
8495
/* Content area */
8596
.content {
8697
display: flex;

apps/condo/domains/ai/components/AIOverlay/AIOverlay.tsx

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import classnames from 'classnames'
12
import React, { useState, useRef, useEffect } from 'react'
23

34
import { Close, RefreshCw, Download } from '@open-condo/icons'
@@ -6,22 +7,30 @@ import { Button, Typography } from '@open-condo/ui'
67

78
import styles from './AIOverlay.module.css'
89

9-
import { AIChat } from '../AIChat/AIChat'
10+
import { AIChat } from '../AIChat'
1011
import { useAIContext } from '../AIContext'
1112

1213
type AIOverlayProps = {
1314
open: boolean
1415
onClose: () => void
1516
}
1617

18+
const MIN_OVERLAY_WIDTH = 300
19+
const MAX_OVERLAY_WIDTH = 1200
20+
const CLOSE_THRESHOLD = MIN_OVERLAY_WIDTH / 2
21+
1722
export const AIOverlay: React.FC<AIOverlayProps> = ({ open, onClose }) => {
1823
const intl = useIntl()
1924
const title = intl.formatMessage({ id: 'ai.chat.title' })
2025
const resetHistoryLabel = intl.formatMessage({ id: 'ai.chat.resetHistory' })
2126
const saveConversationLabel = intl.formatMessage({ id: 'ai.chat.saveConversation' })
2227
const closeLabel = intl.formatMessage({ id: 'Close' })
23-
const { aiOverlayWidth, setAIOverlayWidth } = useAIContext()
28+
const { aiOverlayWidth, setAIOverlayWidth, openAIOverlay } = useAIContext()
2429
const [isResizing, setIsResizing] = useState(false)
30+
const [isAtMinWidth, setIsAtMinWidth] = useState(false)
31+
const [isAtMaxWidth, setIsAtMaxWidth] = useState(false)
32+
const [dragDirection, setDragDirection] = useState<'left' | 'right' | null>(null)
33+
const dragDirectionRef = useRef<'left' | 'right' | null>(null)
2534
const drawerRef = useRef<HTMLDivElement>(null)
2635
const startXRef = useRef<number>(0)
2736
const startWidthRef = useRef<number>(0)
@@ -32,12 +41,35 @@ export const AIOverlay: React.FC<AIOverlayProps> = ({ open, onClose }) => {
3241
if (!isResizing) return
3342

3443
const newWidth = startWidthRef.current + (startXRef.current - e.clientX)
35-
const clampedWidth = Math.max(300, Math.min(1200, newWidth))
44+
const currentDirection = e.clientX > startXRef.current ? 'right' : 'left'
45+
dragDirectionRef.current = currentDirection
46+
setDragDirection(currentDirection)
47+
48+
// Normal resizing when open
49+
const clampedWidth = Math.max(MIN_OVERLAY_WIDTH, Math.min(MAX_OVERLAY_WIDTH, newWidth))
50+
51+
// Check if we're at minimum or maximum width
52+
setIsAtMinWidth(clampedWidth <= MIN_OVERLAY_WIDTH)
53+
setIsAtMaxWidth(clampedWidth >= MAX_OVERLAY_WIDTH)
54+
55+
// Only close if dragging right consistently and beyond close threshold
56+
if (newWidth < CLOSE_THRESHOLD && dragDirectionRef.current === 'right' && currentDirection === 'right') {
57+
onClose()
58+
return
59+
}
60+
61+
// Open overlay if dragging left consistently and crosses open threshold
62+
if (newWidth > CLOSE_THRESHOLD && dragDirectionRef.current === 'left' && currentDirection === 'left') {
63+
openAIOverlay()
64+
}
65+
3666
setAIOverlayWidth(clampedWidth)
3767
}
3868

3969
const handleMouseUp = () => {
4070
setIsResizing(false)
71+
setDragDirection(null)
72+
dragDirectionRef.current = null
4173
}
4274

4375
if (isResizing) {
@@ -53,9 +85,12 @@ export const AIOverlay: React.FC<AIOverlayProps> = ({ open, onClose }) => {
5385
document.body.style.cursor = ''
5486
document.body.style.userSelect = ''
5587
}
56-
}, [isResizing, setAIOverlayWidth])
88+
}, [isResizing, dragDirection, setAIOverlayWidth, onClose])
5789

5890
const handleResizeStart = (e: React.MouseEvent) => {
91+
// Only allow resizing when overlay is open
92+
if (!open) return
93+
5994
setIsResizing(true)
6095
startXRef.current = e.clientX
6196
startWidthRef.current = aiOverlayWidth
@@ -103,7 +138,10 @@ export const AIOverlay: React.FC<AIOverlayProps> = ({ open, onClose }) => {
103138
/>
104139
</div>
105140
</div>
106-
<div className={styles.resizeHandle} onMouseDown={handleResizeStart} />
141+
<div className={classnames(styles.resizeHandle, {
142+
[styles.atMinWidth]: isAtMinWidth,
143+
[styles.atMaxWidth]: isAtMaxWidth,
144+
})} onMouseDown={handleResizeStart} />
107145
<div className={styles.content}>
108146
<AIChat
109147
ref={aiChatRef}

apps/condo/domains/common/components/UseDeskWidget.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import get from 'lodash/get'
33
import isFunction from 'lodash/isFunction'
44
import set from 'lodash/set'
55
import getConfig from 'next/config'
6-
import React, { useEffect, useMemo } from 'react'
7-
6+
import React, { useEffect, useMemo, useState } from 'react'
87

98
import { useAuth } from '@open-condo/next/auth'
109
import { useOrganization } from '@open-condo/next/organization'
@@ -26,7 +25,8 @@ const getUsedeskMessenger = () => {
2625
return get(window, 'usedeskMessenger', null)
2726
}
2827

29-
const UseDeskWidget: React.FC = () => {
28+
const UseDeskWidget: React.FC<{ hide?: boolean }> = ({ hide = false }) => {
29+
const [isWidgetScriptLoaded, setIsWidgetScriptLoaded] = useState(false)
3030
const { link } = useOrganization()
3131
const { user } = useAuth()
3232

@@ -97,7 +97,29 @@ const UseDeskWidget: React.FC = () => {
9797
}
9898
}, [link, userIdentify, user, messenger])
9999

100-
return UseDeskWidgetId ?
101-
<script async src={`//lib.usedesk.ru/secure.usedesk.ru/${UseDeskWidgetId}.js`}></script> : null
100+
useEffect(() => {
101+
if (!UseDeskWidgetId || !isWidgetScriptLoaded) return
102+
103+
const messenger = getUsedeskMessenger()
104+
if (!messenger) return
105+
106+
try {
107+
if (hide) {
108+
messenger.toggle(false)
109+
} else {
110+
messenger.toggle(true)
111+
}
112+
} catch (e) {
113+
console.error('Failed to toggle UseDesk widget visibility', e)
114+
}
115+
}, [hide, isWidgetScriptLoaded])
116+
117+
return UseDeskWidgetId ? (
118+
<script
119+
async
120+
src={`//lib.usedesk.ru/secure.usedesk.ru/${UseDeskWidgetId}.js`}
121+
onLoad={() => setIsWidgetScriptLoaded(true)}
122+
/>
123+
) : null
102124
}
103125
export default UseDeskWidget

apps/condo/pages/_app.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ const AIOverlayWrapper = () => {
126126
return <AIOverlay open={isAIOverlayOpen} onClose={closeAIOverlay} />
127127
}
128128

129+
const UseDeskWidgetWrapper = () => {
130+
const { isAIOverlayOpen } = useAIContext()
131+
132+
return <UseDeskWidget hide={isAIOverlayOpen} />
133+
}
134+
129135

130136
const { publicRuntimeConfig: { defaultLocale, sppConfig, isDisabledSsr } } = getConfig()
131137

@@ -569,12 +575,12 @@ const MyApp = ({ Component, pageProps }) => {
569575
</HCaptchaProvider>
570576
{!isSnowfallDisabled && <Snowfall />}
571577
<AIOverlayWrapper />
578+
{UseDeskWidgetId && <UseDeskWidgetWrapper />}
572579
</LayoutContextProvider>
573580
</AIProvider>
574581
{yandexMetrikaID && <YandexMetrika />}
575582
{googleTagManagerId && <GoogleTagManager />}
576583
{!isEmpty(popupSmartConfig) && <PopupSmart />}
577-
{UseDeskWidgetId && <UseDeskWidget/>}
578584
</CacheProvider>
579585
</ConfigProvider>
580586
</>

0 commit comments

Comments
 (0)