chore: test deploy snapshot
This commit is contained in:
@@ -1591,6 +1591,7 @@ export async function registerChatRoutes(app: FastifyInstance) {
|
||||
accessPinPromptTtlMinutes: z.number().int().min(0).max(7 * 24 * 60).optional().nullable(),
|
||||
chatTypeId: z.string().trim().min(1).max(120).optional().nullable(),
|
||||
chatTypeLabel: z.string().trim().min(1).max(200).optional().nullable(),
|
||||
title: z.string().trim().min(1).max(200).optional().nullable(),
|
||||
notifyOffline: z.boolean().optional().nullable(),
|
||||
}).parse(request.body ?? {});
|
||||
const managedContext = await resolveManagedChatShareContext(params.token);
|
||||
@@ -1655,13 +1656,14 @@ export async function registerChatRoutes(app: FastifyInstance) {
|
||||
|
||||
let updatedConversation = await getChatConversation(tokenPayload.sessionId, getRequestClientId(request));
|
||||
|
||||
if (payload.chatTypeId || payload.notifyOffline != null) {
|
||||
if (payload.chatTypeId || payload.title || payload.notifyOffline != null) {
|
||||
updatedConversation = await updateChatConversationContext(tokenPayload.sessionId, {
|
||||
clientId: getRequestClientId(request),
|
||||
chatTypeId: payload.chatTypeId?.trim() || undefined,
|
||||
lastChatTypeId: payload.chatTypeId?.trim() || undefined,
|
||||
contextLabel: payload.chatTypeLabel?.trim() || undefined,
|
||||
contextDescription: payload.chatTypeId ? null : undefined,
|
||||
title: payload.title?.trim() || undefined,
|
||||
notifyOffline: payload.notifyOffline ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2569,6 +2569,7 @@ export async function saveChatShareRoomSettings(
|
||||
accessPinPromptTtlMinutes?: number | null;
|
||||
chatTypeId?: string | null;
|
||||
chatTypeLabel?: string | null;
|
||||
title?: string | null;
|
||||
notifyOffline?: boolean | null;
|
||||
},
|
||||
) {
|
||||
@@ -2595,6 +2596,7 @@ export async function saveChatShareRoomSettings(
|
||||
accessPinPromptTtlMinutes: input.accessPinPromptTtlMinutes,
|
||||
chatTypeId: input.chatTypeId,
|
||||
chatTypeLabel: input.chatTypeLabel,
|
||||
title: input.title,
|
||||
notifyOffline: input.notifyOffline,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1826,6 +1826,18 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-share-page__first-inquiry-title-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-share-page__first-inquiry-title-row .chat-share-page__section-action.ant-btn {
|
||||
flex: 0 0 auto;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.chat-share-page__first-inquiry-menu-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppstoreOutlined, CheckOutlined, CloseOutlined, CopyOutlined, DeleteOutlined, DownOutlined, EyeInvisibleOutlined, EyeOutlined, FileTextOutlined, FilterOutlined, FullscreenOutlined, LeftOutlined, PlusOutlined, ReloadOutlined, RightOutlined, SearchOutlined, SendOutlined, SettingOutlined, ThunderboltOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import { AppstoreOutlined, CheckOutlined, CloseOutlined, CopyOutlined, DeleteOutlined, DownOutlined, EditOutlined, EyeInvisibleOutlined, EyeOutlined, FileTextOutlined, FilterOutlined, FullscreenOutlined, LeftOutlined, PlusOutlined, ReloadOutlined, RightOutlined, SearchOutlined, SendOutlined, SettingOutlined, ThunderboltOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import { Alert, App, Button, Checkbox, Drawer, Dropdown, Input, Modal, Select, Spin, Tabs, Tag, Typography, type MenuProps } from 'antd';
|
||||
import type { TextAreaRef } from 'antd/es/input/TextArea';
|
||||
import { Suspense, lazy, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type ClipboardEvent, type FocusEvent, type KeyboardEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from 'react';
|
||||
@@ -3006,6 +3006,7 @@ export function ChatSharePage() {
|
||||
const [isRoomSettingsOpen, setIsRoomSettingsOpen] = useState(false);
|
||||
const [isSavingRoomSettings, setIsSavingRoomSettings] = useState(false);
|
||||
const [roomSettingsTabKey, setRoomSettingsTabKey] = useState<'chat-type' | 'default-contexts' | 'room-context' | 'notifications' | 'security'>('chat-type');
|
||||
const [editingRoomTitle, setEditingRoomTitle] = useState('');
|
||||
const [editingRoomChatTypeId, setEditingRoomChatTypeId] = useState<string | null>(null);
|
||||
const [editingRoomDefaultContextIds, setEditingRoomDefaultContextIds] = useState<string[]>([]);
|
||||
const [isEditingRoomDefaultContextsDirty, setIsEditingRoomDefaultContextsDirty] = useState(false);
|
||||
@@ -3482,6 +3483,12 @@ export function ChatSharePage() {
|
||||
}
|
||||
|
||||
const nextChatTypeId = currentSharedChatTypeId ?? enabledChatTypes[0]?.id ?? null;
|
||||
const fallbackRoomTitle =
|
||||
snapshot.conversation.title?.trim()
|
||||
|| snapshot.targetRequest.userText?.trim()
|
||||
|| snapshot.requests?.[0]?.userText?.trim()
|
||||
|| '-';
|
||||
setEditingRoomTitle(fallbackRoomTitle);
|
||||
setEditingRoomChatTypeId(nextChatTypeId);
|
||||
setEditingRoomDefaultContextIds(resolveShareRoomDefaultContextIds(activeRoomContextSettings, chatTypeDefaults, nextChatTypeId));
|
||||
setIsEditingRoomDefaultContextsDirty(false);
|
||||
@@ -3501,9 +3508,12 @@ export function ChatSharePage() {
|
||||
currentSharedChatTypeId,
|
||||
enabledChatTypes,
|
||||
snapshot?.conversation.notifyOffline,
|
||||
snapshot?.conversation.title,
|
||||
snapshot?.requests,
|
||||
snapshot?.share.accessPinPromptTtlMinutes,
|
||||
snapshot?.share.hasAccessPin,
|
||||
snapshot?.conversation.sessionId,
|
||||
snapshot?.targetRequest.userText,
|
||||
]);
|
||||
useEffect(() => {
|
||||
if (!isRoomSettingsOpen) {
|
||||
@@ -3542,6 +3552,7 @@ export function ChatSharePage() {
|
||||
const nextCustomContextContent = editingRoomCustomContextContent.trim();
|
||||
const shouldPersistRoomDefaultContextIds = !areStringListsEqual(normalizedDefaultContextIds, inheritedDefaultContextIds);
|
||||
const shouldPersistRoomCustomContext = Boolean(nextCustomContextTitle || nextCustomContextContent);
|
||||
const normalizedRoomTitle = editingRoomTitle.trim();
|
||||
const normalizedAccessPin = editingRoomAccessPin.trim();
|
||||
const currentHasAccessPin = snapshot?.share.hasAccessPin === true;
|
||||
const currentAccessPinPromptTtlMinutes = resolveAccessPinPromptTtlMinutes(snapshot?.share.accessPinPromptTtlMinutes);
|
||||
@@ -3549,10 +3560,17 @@ export function ChatSharePage() {
|
||||
canManageSharedRoomSettings
|
||||
&& Boolean(nextChatType)
|
||||
&& (
|
||||
currentSharedChatTypeId !== nextChatType.id
|
||||
normalizedRoomTitle !== (snapshot?.conversation.title?.trim() || '')
|
||||
|| !(snapshot?.conversation.title?.trim())
|
||||
|| currentSharedChatTypeId !== nextChatType.id
|
||||
|| snapshot.conversation.notifyOffline !== editingRoomNotifyOffline
|
||||
);
|
||||
|
||||
if (canManageSharedRoomSettings && !normalizedRoomTitle) {
|
||||
message.warning('채팅방 이름을 입력하세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (canEditSharedRoomAccessPin && editingRoomUseAccessPin && !currentHasAccessPin && !normalizedAccessPin) {
|
||||
message.warning('비밀번호를 새로 켜려면 숫자 4자리를 입력하세요.');
|
||||
return;
|
||||
@@ -3623,6 +3641,7 @@ export function ChatSharePage() {
|
||||
accessPinPromptTtlMinutes: editingRoomUseAccessPin ? editingRoomAccessPinPromptTtlMinutes : null,
|
||||
chatTypeId: shouldSaveConversationSettings ? nextChatType?.id ?? null : undefined,
|
||||
chatTypeLabel: shouldSaveConversationSettings ? nextChatType?.name ?? null : undefined,
|
||||
title: shouldSaveConversationSettings ? normalizedRoomTitle : undefined,
|
||||
notifyOffline: shouldSaveConversationSettings ? editingRoomNotifyOffline : undefined,
|
||||
});
|
||||
|
||||
@@ -3669,6 +3688,7 @@ export function ChatSharePage() {
|
||||
canManageSharedRoomSettings,
|
||||
defaultContexts,
|
||||
editingRoomChatTypeId,
|
||||
editingRoomTitle,
|
||||
editingRoomAccessPin,
|
||||
editingRoomAccessPinPromptTtlMinutes,
|
||||
editingRoomCustomContextContent,
|
||||
@@ -5214,6 +5234,15 @@ export function ChatSharePage() {
|
||||
|
||||
return currentRequest ?? latestRequest ?? sortedRequests[0] ?? null;
|
||||
}, [currentRequest, displayedRequests, latestRequest, sortedRequests]);
|
||||
const headerTitleText = useMemo(() => {
|
||||
const savedConversationTitle = snapshot?.conversation.title?.trim() || '';
|
||||
|
||||
if (savedConversationTitle) {
|
||||
return savedConversationTitle;
|
||||
}
|
||||
|
||||
return headerInquiryRequest?.userText.trim() || '-';
|
||||
}, [headerInquiryRequest?.userText, snapshot?.conversation.title]);
|
||||
const hiddenBeforeCount = expandMode === 'latest' && latestRequestIndex >= 0 ? latestRequestIndex : 0;
|
||||
const hiddenAfterCount =
|
||||
expandMode === 'latest' && latestRequestIndex >= 0 ? Math.max(0, sortedRequests.length - latestRequestIndex - 1) : 0;
|
||||
@@ -6538,13 +6567,28 @@ export function ChatSharePage() {
|
||||
<section className="chat-share-page__first-inquiry">
|
||||
<div className="chat-share-page__first-inquiry-head">
|
||||
<div className="chat-share-page__first-inquiry-copy">
|
||||
<Title
|
||||
level={5}
|
||||
className="chat-share-page__first-inquiry-title"
|
||||
ellipsis={{ rows: 1, tooltip: headerInquiryRequest.userText.trim() || '-' }}
|
||||
>
|
||||
{headerInquiryRequest.userText.trim() || '-'}
|
||||
</Title>
|
||||
<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']}
|
||||
@@ -7023,6 +7067,19 @@ export function ChatSharePage() {
|
||||
label: '채팅유형',
|
||||
children: (
|
||||
<div className="chat-share-page__room-settings-panel">
|
||||
<div className="chat-share-page__room-settings-panel-head">
|
||||
<Text strong>채팅방 이름</Text>
|
||||
<Text type="secondary">공유 채팅 헤더와 알림에 표시할 이름을 직접 저장합니다.</Text>
|
||||
</div>
|
||||
<Input
|
||||
value={editingRoomTitle}
|
||||
placeholder="예: 관리자 공유 채팅"
|
||||
readOnly={!canManageSharedRoomSettings}
|
||||
maxLength={200}
|
||||
onChange={(event) => {
|
||||
setEditingRoomTitle(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<div className="chat-share-page__room-settings-panel-head">
|
||||
<Text strong>기본 채팅유형</Text>
|
||||
<Text type="secondary">공유채팅이 기본으로 사용할 유형을 먼저 고릅니다.</Text>
|
||||
|
||||
@@ -159,6 +159,14 @@ type BootstrapInstallMetadataResult = {
|
||||
themeColor: string;
|
||||
} | null;
|
||||
|
||||
type BootstrapInstallMetadataDefinition = {
|
||||
description: string;
|
||||
scope?: string;
|
||||
shortName?: string;
|
||||
themeColor: string;
|
||||
title: string;
|
||||
} | null;
|
||||
|
||||
const PLAY_APP_INSTALL_METADATA: Record<string, { title: string; themeColor: string }> = {
|
||||
'baseball-ticket-bay': { title: 'Baseball Ticket Bay', themeColor: '#1b3f91' },
|
||||
'photoprism': { title: 'PhotoPrism', themeColor: '#0f766e' },
|
||||
@@ -190,13 +198,8 @@ function createCurrentRouteInstallManifestObjectUrl(options: {
|
||||
});
|
||||
}
|
||||
|
||||
export function applyBootstrapInstallMetadataForCurrentRoute(): BootstrapInstallMetadataResult {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pathname = window.location.pathname;
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
function resolveBootstrapInstallMetadataDefinition(pathname: string, search: string): BootstrapInstallMetadataDefinition {
|
||||
const searchParams = new URLSearchParams(search);
|
||||
|
||||
if (pathname === '/play/apps') {
|
||||
const appId = searchParams.get('app')?.trim() ?? '';
|
||||
@@ -207,45 +210,52 @@ export function applyBootstrapInstallMetadataForCurrentRoute(): BootstrapInstall
|
||||
}
|
||||
|
||||
return {
|
||||
manifestObjectUrl: createCurrentRouteInstallManifestObjectUrl({
|
||||
title: metadata.title,
|
||||
shortName: metadata.title,
|
||||
description: `${metadata.title} 앱을 홈 화면에서 바로 엽니다.`,
|
||||
themeColor: metadata.themeColor,
|
||||
scope: pathname,
|
||||
}),
|
||||
title: metadata.title,
|
||||
shortName: metadata.title,
|
||||
description: `${metadata.title} 앱을 홈 화면에서 바로 엽니다.`,
|
||||
themeColor: metadata.themeColor,
|
||||
scope: pathname,
|
||||
};
|
||||
}
|
||||
|
||||
if (pathname === '/plans/shared-resource') {
|
||||
return {
|
||||
manifestObjectUrl: createCurrentRouteInstallManifestObjectUrl({
|
||||
title: '공유 리소스 관리',
|
||||
shortName: '공유 리소스',
|
||||
description: '공유 리소스 관리 화면을 홈 화면 앱으로 바로 엽니다.',
|
||||
themeColor: '#0f766e',
|
||||
scope: pathname,
|
||||
}),
|
||||
title: '공유 리소스 관리',
|
||||
shortName: '공유 리소스',
|
||||
description: '공유 리소스 관리 화면을 홈 화면 앱으로 바로 엽니다.',
|
||||
themeColor: '#0f766e',
|
||||
scope: pathname,
|
||||
};
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/shares/')) {
|
||||
if (pathname.startsWith('/chat/share/') || pathname.startsWith('/shares/')) {
|
||||
return {
|
||||
manifestObjectUrl: createCurrentRouteInstallManifestObjectUrl({
|
||||
title: '리소스 공유 채팅방',
|
||||
shortName: '공유채팅',
|
||||
description: '리소스 공유 채팅방을 홈 화면 앱으로 바로 엽니다.',
|
||||
themeColor: '#165dff',
|
||||
scope: pathname,
|
||||
}),
|
||||
title: '리소스 공유 채팅방',
|
||||
shortName: '공유채팅',
|
||||
description: '리소스 공유 채팅방을 홈 화면 앱으로 바로 엽니다.',
|
||||
themeColor: '#165dff',
|
||||
scope: pathname,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function applyBootstrapInstallMetadataForCurrentRoute(): BootstrapInstallMetadataResult {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pathname = window.location.pathname;
|
||||
const metadata = resolveBootstrapInstallMetadataDefinition(pathname, window.location.search);
|
||||
|
||||
if (!metadata) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
manifestObjectUrl: createCurrentRouteInstallManifestObjectUrl(metadata),
|
||||
title: metadata.title,
|
||||
themeColor: metadata.themeColor,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user