chore: test deploy snapshot
This commit is contained in:
@@ -65,6 +65,7 @@ import { extractPreviewItems, type PreviewItem } from '../mainChatPanel/previewI
|
||||
import { buildChatPath, buildPlayAppPath } from '../routes';
|
||||
import type { PreviewKind } from '../mainChatPanel/previewKind';
|
||||
import { normalizeChatResourceUrl } from '../mainChatPanel/chatResourceUrl';
|
||||
import { forceReloadApp } from '../appUpdate';
|
||||
import type {
|
||||
ChatComposerAttachment,
|
||||
ChatConversationSummary,
|
||||
@@ -185,6 +186,7 @@ type ShareNotificationClientStatus = {
|
||||
tone: ShareNotificationStatusTone;
|
||||
};
|
||||
type ShareProcessInspectorMode = 'default' | 'fullscreen' | 'minimized';
|
||||
type ShareProcessInspectorExpandedSection = 'summary' | 'narratives' | null;
|
||||
type ShareProcessChecklistStep = {
|
||||
key: string;
|
||||
label: string;
|
||||
@@ -4030,6 +4032,7 @@ export function ChatSharePage() {
|
||||
const [pendingRequestRetryIds, setPendingRequestRetryIds] = useState<string[]>([]);
|
||||
const [isShareRoomListVisible, setIsShareRoomListVisible] = useState(false);
|
||||
const [shareRoomListLayerStyle, setShareRoomListLayerStyle] = useState<CSSProperties | null>(null);
|
||||
const [conversationToolbarStickyTop, setConversationToolbarStickyTop] = useState(52);
|
||||
const [isRoomSwitching, setIsRoomSwitching] = useState(false);
|
||||
const [replyReferenceRequestId, setReplyReferenceRequestId] = useState('');
|
||||
const [previousQuestionModalRequestId, setPreviousQuestionModalRequestId] = useState('');
|
||||
@@ -4080,8 +4083,7 @@ export function ChatSharePage() {
|
||||
const [pendingShareRuntimeRequestIds, setPendingShareRuntimeRequestIds] = useState<string[]>([]);
|
||||
const [activeProcessInspectorRequestId, setActiveProcessInspectorRequestId] = useState('');
|
||||
const [processInspectorMode, setProcessInspectorMode] = useState<ShareProcessInspectorMode>('default');
|
||||
const [isProcessInspectorSummaryCollapsed, setIsProcessInspectorSummaryCollapsed] = useState(true);
|
||||
const [isProcessInspectorNarrativesCollapsed, setIsProcessInspectorNarrativesCollapsed] = useState(true);
|
||||
const [processInspectorExpandedSection, setProcessInspectorExpandedSection] = useState<ShareProcessInspectorExpandedSection>(null);
|
||||
const [optimisticShareRooms, setOptimisticShareRooms] = useState<ChatShareRoomSummary[]>([]);
|
||||
const [shareRoomPendingCountsBySessionId, setShareRoomPendingCountsBySessionId] = useState<Record<string, ShareRoomPendingCounts>>({});
|
||||
const [isLoadingShareRoomPendingCounts, setIsLoadingShareRoomPendingCounts] = useState(false);
|
||||
@@ -4102,6 +4104,7 @@ export function ChatSharePage() {
|
||||
const shareRoomPendingCountRefreshPromiseBySessionIdRef = useRef<Record<string, Promise<void> | null>>({});
|
||||
const shareRoomPendingCountRefreshQueuedBySessionIdRef = useRef<Record<string, boolean>>({});
|
||||
const conversationHeaderRef = useRef<HTMLDivElement | null>(null);
|
||||
const conversationToolbarRef = useRef<HTMLDivElement | null>(null);
|
||||
const roomListTriggerButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const roomListPanelRef = useRef<HTMLElement | null>(null);
|
||||
const processInspectorCardRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -5428,6 +5431,37 @@ export function ChatSharePage() {
|
||||
};
|
||||
}, [minimizedPrograms.length]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const headerElement = conversationHeaderRef.current;
|
||||
|
||||
if (!headerElement) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const updateStickyOffset = () => {
|
||||
const headerHeight = headerElement.getBoundingClientRect().height;
|
||||
const nextStickyTop = Math.max(48, Math.ceil(headerHeight) + 8);
|
||||
|
||||
setConversationToolbarStickyTop((current) => (current === nextStickyTop ? current : nextStickyTop));
|
||||
};
|
||||
|
||||
const resizeObserver =
|
||||
typeof ResizeObserver === 'undefined'
|
||||
? null
|
||||
: new ResizeObserver(() => {
|
||||
updateStickyOffset();
|
||||
});
|
||||
|
||||
updateStickyOffset();
|
||||
window.addEventListener('resize', updateStickyOffset);
|
||||
resizeObserver?.observe(headerElement);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateStickyOffset);
|
||||
resizeObserver?.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isShareRoomListVisible) {
|
||||
setShareRoomListLayerStyle(null);
|
||||
@@ -5437,7 +5471,8 @@ export function ChatSharePage() {
|
||||
const updateLayerPosition = () => {
|
||||
const triggerRect = roomListTriggerButtonRef.current?.getBoundingClientRect() ?? null;
|
||||
const headerRect = conversationHeaderRef.current?.getBoundingClientRect() ?? null;
|
||||
const anchorRect = headerRect ?? triggerRect;
|
||||
const toolbarRect = conversationToolbarRef.current?.getBoundingClientRect() ?? null;
|
||||
const anchorRect = triggerRect ?? headerRect ?? toolbarRect;
|
||||
|
||||
if (!anchorRect) {
|
||||
return;
|
||||
@@ -5445,14 +5480,17 @@ export function ChatSharePage() {
|
||||
|
||||
const viewportPadding = 8;
|
||||
const availableWidth = Math.max(280, window.innerWidth - (viewportPadding * 2));
|
||||
const preferredWidth = headerRect
|
||||
? Math.min(Math.max(headerRect.width, 280), 420)
|
||||
const preferredWidth = toolbarRect
|
||||
? Math.min(Math.max(toolbarRect.width, 280), 420)
|
||||
: headerRect
|
||||
? Math.min(Math.max(headerRect.width, 280), 420)
|
||||
: Math.min(360, availableWidth);
|
||||
const width = Math.min(preferredWidth, availableWidth);
|
||||
const preferredLeft = headerRect?.left ?? triggerRect?.left ?? viewportPadding;
|
||||
const preferredRight = triggerRect?.right ?? toolbarRect?.right ?? headerRect?.right ?? (viewportPadding + width);
|
||||
const minLeft = viewportPadding;
|
||||
const maxLeft = Math.max(viewportPadding, window.innerWidth - viewportPadding - width);
|
||||
const left = Math.min(Math.max(preferredLeft, viewportPadding), maxLeft);
|
||||
const top = Math.max(anchorRect.bottom, triggerRect?.bottom ?? 0) + 8;
|
||||
const left = Math.min(Math.max(preferredRight - width, minLeft), maxLeft);
|
||||
const top = anchorRect.bottom + 8;
|
||||
const maxHeight = Math.max(220, window.innerHeight - top - viewportPadding);
|
||||
|
||||
setShareRoomListLayerStyle({
|
||||
@@ -5527,14 +5565,20 @@ export function ChatSharePage() {
|
||||
|
||||
setActiveProcessInspectorRequestId(requestId);
|
||||
setProcessInspectorMode('default');
|
||||
setIsProcessInspectorSummaryCollapsed(true);
|
||||
setIsProcessInspectorNarrativesCollapsed(true);
|
||||
setProcessInspectorExpandedSection(null);
|
||||
}, []);
|
||||
|
||||
const closeProcessInspector = useCallback(() => {
|
||||
setActiveProcessInspectorRequestId('');
|
||||
setIsProcessInspectorSummaryCollapsed(true);
|
||||
setIsProcessInspectorNarrativesCollapsed(true);
|
||||
setProcessInspectorExpandedSection(null);
|
||||
}, []);
|
||||
|
||||
const handleToggleProcessInspectorSummary = useCallback(() => {
|
||||
setProcessInspectorExpandedSection((current) => (current === 'summary' ? null : 'summary'));
|
||||
}, []);
|
||||
|
||||
const handleToggleProcessInspectorNarratives = useCallback(() => {
|
||||
setProcessInspectorExpandedSection((current) => (current === 'narratives' ? null : 'narratives'));
|
||||
}, []);
|
||||
|
||||
const handleProgramMinimizedPointerDown = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {
|
||||
@@ -5583,7 +5627,7 @@ export function ChatSharePage() {
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
void forceReloadApp();
|
||||
}, []);
|
||||
|
||||
const handleReloadProgram = useCallback(() => {
|
||||
@@ -7461,6 +7505,8 @@ export function ChatSharePage() {
|
||||
() => (activeProcessInspectorRequestId.trim() ? requestById.get(activeProcessInspectorRequestId.trim()) ?? null : null),
|
||||
[activeProcessInspectorRequestId, requestById],
|
||||
);
|
||||
const isProcessInspectorSummaryCollapsed = processInspectorExpandedSection !== 'summary';
|
||||
const isProcessInspectorNarrativesCollapsed = processInspectorExpandedSection !== 'narratives';
|
||||
const activeProcessInspectorPayload = useMemo(() => {
|
||||
if (!activeProcessInspectorRequest) {
|
||||
return null;
|
||||
@@ -7638,9 +7684,7 @@ export function ChatSharePage() {
|
||||
size="small"
|
||||
className="chat-share-page__process-inspector-summary-toggle"
|
||||
icon={isProcessInspectorSummaryCollapsed ? <DownOutlined /> : <UpOutlined />}
|
||||
onClick={() => {
|
||||
setIsProcessInspectorSummaryCollapsed((current) => !current);
|
||||
}}
|
||||
onClick={handleToggleProcessInspectorSummary}
|
||||
>
|
||||
{isProcessInspectorSummaryCollapsed ? '요청 정보 보기' : '요청 정보 접기'}
|
||||
</Button>
|
||||
@@ -7705,9 +7749,7 @@ export function ChatSharePage() {
|
||||
size="small"
|
||||
className="chat-share-page__process-inspector-summary-toggle"
|
||||
icon={isProcessInspectorNarrativesCollapsed ? <DownOutlined /> : <UpOutlined />}
|
||||
onClick={() => {
|
||||
setIsProcessInspectorNarrativesCollapsed((current) => !current);
|
||||
}}
|
||||
onClick={handleToggleProcessInspectorNarratives}
|
||||
>
|
||||
{isProcessInspectorNarrativesCollapsed ? '보기' : '접기'}
|
||||
</Button>
|
||||
@@ -8686,8 +8728,8 @@ export function ChatSharePage() {
|
||||
key: 'conversation-refresh',
|
||||
label: (
|
||||
<span className="chat-share-page__settings-item">
|
||||
<span className="chat-share-page__settings-item-title">화면 새로고침</span>
|
||||
<span className="chat-share-page__settings-item-description">PWA 반영이 늦을 때 현재 공유채팅방 화면을 다시 불러옵니다.</span>
|
||||
<span className="chat-share-page__settings-item-title">강력 새로고침</span>
|
||||
<span className="chat-share-page__settings-item-description">서비스워커와 캐시를 정리한 뒤 현재 공유채팅방 화면을 다시 불러옵니다.</span>
|
||||
</span>
|
||||
),
|
||||
icon: <ReloadOutlined />,
|
||||
@@ -9249,19 +9291,46 @@ export function ChatSharePage() {
|
||||
<div className={contentLayoutClassName}>
|
||||
<section className="chat-share-page__panel chat-share-page__conversation-panel">
|
||||
<div ref={conversationHeaderRef} className="chat-share-page__section-head">
|
||||
<div className="chat-share-page__section-copy">
|
||||
<div className="chat-share-page__section-copy">
|
||||
<div className="chat-share-page__section-title-row">
|
||||
<Title level={5}>채팅</Title>
|
||||
<Title
|
||||
level={5}
|
||||
className="chat-share-page__conversation-title"
|
||||
ellipsis={{ rows: 1, tooltip: headerTitleText.trim() || '채팅' }}
|
||||
>
|
||||
{headerTitleText.trim() || '채팅'}
|
||||
</Title>
|
||||
{canOpenSharedRoomSettings ? (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="chat-share-page__section-action chat-share-page__section-action--tool chat-share-page__section-action--inline chat-share-page__section-action--title-edit"
|
||||
aria-label="채팅방 이름 및 설정 편집"
|
||||
title="채팅방 이름 및 설정 편집"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => {
|
||||
openSharedRoomSettings();
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<span
|
||||
className={`chat-share-page__live-dot ${isLiveConnected ? 'chat-share-page__live-dot--connected' : 'chat-share-page__live-dot--disconnected'}`}
|
||||
aria-label={isLiveConnected ? '웹소켓 연결됨' : '웹소켓 연결 끊김'}
|
||||
title={isLiveConnected ? '웹소켓 연결됨' : '웹소켓 연결 끊김'}
|
||||
/>
|
||||
{aggregateStatusTag ? <Tag color={aggregateStatusTag.color}>{aggregateStatusTag.label}</Tag> : null}
|
||||
<Text type="secondary" className="chat-share-page__header-summary">
|
||||
{headerSummaryLabel}
|
||||
</Text>
|
||||
{aggregateStatusTag ? (
|
||||
<span
|
||||
className={`chat-share-page__status-badge chat-share-page__status-badge--${aggregateStatusTag.color}`}
|
||||
aria-label={aggregateStatusTag.label}
|
||||
title={aggregateStatusTag.label}
|
||||
>
|
||||
<span className="chat-share-page__status-badge-label">{aggregateStatusTag.label}</span>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<Text type="secondary" className="chat-share-page__header-summary">
|
||||
{headerSummaryLabel}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="chat-share-page__section-actions">
|
||||
{canToggleShareRoomList ? (
|
||||
@@ -9278,29 +9347,6 @@ export function ChatSharePage() {
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<div className="chat-share-page__request-nav" aria-label="요청 이동">
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="chat-share-page__section-action"
|
||||
icon={<LeftOutlined />}
|
||||
disabled={!canMoveToPreviousRequest}
|
||||
onClick={handleMoveToPreviousRequest}
|
||||
>
|
||||
이전
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="chat-share-page__section-action"
|
||||
icon={<RightOutlined />}
|
||||
iconPosition="end"
|
||||
disabled={!canMoveToNextRequest}
|
||||
onClick={handleMoveToNextRequest}
|
||||
>
|
||||
다음
|
||||
</Button>
|
||||
</div>
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
menu={{
|
||||
@@ -9323,6 +9369,64 @@ export function ChatSharePage() {
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={conversationToolbarRef}
|
||||
className="chat-share-page__conversation-toolbar"
|
||||
style={
|
||||
{
|
||||
'--chat-share-page-conversation-toolbar-top': `${conversationToolbarStickyTop}px`,
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<div className="chat-share-page__conversation-toolbar-group" aria-label="요청 이동 및 필터">
|
||||
<div className="chat-share-page__request-nav" aria-label="요청 이동">
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="chat-share-page__section-action chat-share-page__section-action--tool chat-share-page__conversation-toolbar-button"
|
||||
icon={<LeftOutlined />}
|
||||
disabled={!canMoveToPreviousRequest}
|
||||
onClick={handleMoveToPreviousRequest}
|
||||
>
|
||||
<span className="chat-share-page__tool-button-label">이전</span>
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="chat-share-page__section-action chat-share-page__section-action--tool chat-share-page__conversation-toolbar-button"
|
||||
icon={<RightOutlined />}
|
||||
iconPosition="end"
|
||||
disabled={!canMoveToNextRequest}
|
||||
onClick={handleMoveToNextRequest}
|
||||
>
|
||||
<span className="chat-share-page__tool-button-label">다음</span>
|
||||
</Button>
|
||||
</div>
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
placement="bottomRight"
|
||||
menu={{
|
||||
items: shareExpandModeMenuItems,
|
||||
selectable: true,
|
||||
selectedKeys: [expandMode],
|
||||
onClick: handleSelectExpandMode,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className={`chat-share-page__section-action chat-share-page__section-action--tool chat-share-page__conversation-toolbar-button chat-share-page__expand-mode-filter${expandMode !== 'latest' ? ' chat-share-page__expand-mode-filter--active' : ''}`}
|
||||
aria-label={`공유 채팅 펼치기 필터: ${getShareExpandModeLabel(expandMode)} ${requestProgressLabel}`.trim()}
|
||||
title={`공유 채팅 펼치기 필터: ${getShareExpandModeLabel(expandMode)} ${requestProgressLabel}`.trim()}
|
||||
icon={<FilterOutlined />}
|
||||
>
|
||||
<span className="chat-share-page__tool-button-label">
|
||||
{expandMode === 'latest' ? '필터' : getShareExpandModeLabel(expandMode)}
|
||||
</span>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{showRoomSwitchingSkeleton ? (
|
||||
<div className="chat-share-page__conversation-loading-block" role="status" aria-live="polite">
|
||||
<Spin size="large" />
|
||||
@@ -9339,59 +9443,6 @@ export function ChatSharePage() {
|
||||
<Text type="secondary">{`${roomSwitchingStatusLabel} 불러오는 중`}</Text>
|
||||
</div>
|
||||
) : null}
|
||||
{headerInquiryRequest ? (
|
||||
<section className="chat-share-page__first-inquiry">
|
||||
<div className="chat-share-page__first-inquiry-head">
|
||||
<div className="chat-share-page__first-inquiry-copy">
|
||||
<div className="chat-share-page__first-inquiry-title-row">
|
||||
<Title
|
||||
level={5}
|
||||
className="chat-share-page__first-inquiry-title"
|
||||
ellipsis={{ rows: 1, tooltip: headerTitleText }}
|
||||
>
|
||||
{headerTitleText}
|
||||
</Title>
|
||||
{canOpenSharedRoomSettings ? (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="chat-share-page__section-action chat-share-page__section-action--tool"
|
||||
aria-label="채팅방 이름 및 설정 편집"
|
||||
title="채팅방 이름 및 설정 편집"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => {
|
||||
openSharedRoomSettings();
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
placement="bottomRight"
|
||||
menu={{
|
||||
items: shareExpandModeMenuItems,
|
||||
selectable: true,
|
||||
selectedKeys: [expandMode],
|
||||
onClick: handleSelectExpandMode,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className={`chat-share-page__section-action chat-share-page__section-action--tool chat-share-page__expand-mode-filter${expandMode !== 'latest' ? ' chat-share-page__expand-mode-filter--active' : ''}`}
|
||||
aria-label={`공유 채팅 펼치기 필터: ${getShareExpandModeLabel(expandMode)} ${requestProgressLabel}`.trim()}
|
||||
title={`공유 채팅 펼치기 필터: ${getShareExpandModeLabel(expandMode)} ${requestProgressLabel}`.trim()}
|
||||
icon={<FilterOutlined />}
|
||||
>
|
||||
<span className="chat-share-page__tool-button-label">
|
||||
{expandMode === 'latest' ? '필터' : getShareExpandModeLabel(expandMode)}
|
||||
</span>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
{expandMode === 'latest' && hiddenBeforeCount > 0 ? (
|
||||
<div className="chat-share-page__omission chat-share-page__omission--collapsed chat-share-page__omission--before" aria-label={`위쪽 채팅 ${hiddenBeforeCount}건 숨김`}>
|
||||
<span className="chat-share-page__omission-line" aria-hidden="true" />
|
||||
|
||||
Reference in New Issue
Block a user