fix: restore popup opening on user click for detail window

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-05 22:29:27 +09:00
parent 41feabec04
commit 0ee52cf35f
2 changed files with 68 additions and 32 deletions

View File

@@ -35,7 +35,19 @@ interface WindowWithScreenDetails extends Window {
getScreenDetails?: () => Promise<ScreenDetails>;
}
/** 우측 모니터 좌표·크기 계산 */
/** 클릭 직후 동기적으로 쓸 기본 창 위치 (await 없음) */
function buildSyncWindowFeatures(): string {
const left = window.screenX + window.outerWidth;
const top = window.screenY;
let width = window.screen.availWidth - left;
if (width < 800) {
width = 1280;
}
const height = window.screen.availHeight;
return `left=${left},top=${top},width=${width},height=${height},menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=yes`;
}
/** 우측 모니터 좌표·크기 계산 (열린 뒤 위치 조정용) */
export async function getRightMonitorWindowFeatures(): Promise<string> {
let left = window.screenX + window.outerWidth;
let top = window.screenY;
@@ -116,59 +128,73 @@ function postTaskSelected(taskId: string) {
getChannel().postMessage({ type: 'TASK_SELECTED', taskId } satisfies DualMonitorEvent);
}
/** 상세 창 토글 — 열려 있으면 닫고, 닫혀 있으면 오른쪽 모니터에 열기 */
export async function openDetailWindow(): Promise<Window | null> {
if (isDetailWindowOpen()) {
detailWindow!.close();
detailWindow = null;
return null;
}
function scheduleTaskSelected(taskId: string) {
postTaskSelected(taskId);
setTimeout(() => postTaskSelected(taskId), 500);
setTimeout(() => postTaskSelected(taskId), 1500);
}
/** 상세 창 열기 — 반드시 사용자 클릭 직후 동기 호출 */
function openDetailWindowSync(): Window | null {
const detailUrl = `${window.location.origin}/detail`;
// 사용자 클릭 직후 동기적으로 열어야 팝업 차단을 피할 수 있음
detailWindow = window.open(detailUrl, DETAIL_WINDOW_NAME, 'noopener,noreferrer,width=1280,height=900');
const features = buildSyncWindowFeatures();
detailWindow = window.open(detailUrl, DETAIL_WINDOW_NAME, features);
if (!detailWindow) {
alert('팝업이 차단되었습니다. 브라우저에서 이 사이트의 팝업 허용해 주세요.');
console.warn('상세 창을 열지 못했습니다. 브라우저 팝업 허용을 확인해 주세요.');
return null;
}
try {
detailWindow.focus();
} catch {
// popup-blocked 등
// ignore
}
void getRightMonitorWindowFeatures().then((features) => {
const { left, top, width, height } = parseWindowFeatures(features);
// 창을 연 뒤 비동기로 위치만 보정 (팝업 차단과 무관)
void getRightMonitorWindowFeatures().then((f) => {
const { left, top, width, height } = parseWindowFeatures(f);
if (detailWindow && !detailWindow.closed && left != null && top != null && width && height) {
applyWindowPlacement(detailWindow, left, top, width, height);
}
});
const savedTaskId = getPersistedTaskId();
if (savedTaskId) {
postTaskSelected(savedTaskId);
setTimeout(() => postTaskSelected(savedTaskId), 500);
}
return detailWindow;
}
/** 상세 창 토글 — 열려 있으면 닫고, 닫혀 있으면 오른쪽 모니터에 열기 */
export function openDetailWindow(): Window | null {
if (isDetailWindowOpen()) {
detailWindow!.close();
detailWindow = null;
return null;
}
const win = openDetailWindowSync();
const savedTaskId = getPersistedTaskId();
if (win && savedTaskId) {
scheduleTaskSelected(savedTaskId);
}
return win;
}
/** 좌측 → 우측: 업무 선택 이벤트 전송 (창이 닫혀 있으면 열고 전송) */
export async function sendTaskSelected(taskId: string): Promise<void> {
export function sendTaskSelected(taskId: string): void {
persistSelectedTask(taskId);
if (!isDetailWindowOpen()) {
const win = await openDetailWindow();
const win = openDetailWindowSync();
if (!win) return;
postTaskSelected(taskId);
setTimeout(() => postTaskSelected(taskId), 500);
setTimeout(() => postTaskSelected(taskId), 1500);
scheduleTaskSelected(taskId);
return;
}
detailWindow!.focus();
postTaskSelected(taskId);
try {
detailWindow!.focus();
} catch {
// ignore
}
scheduleTaskSelected(taskId);
}
/** 좌측 → 우측: 업무 선택 해제 */
@@ -193,14 +219,24 @@ export function closeChannel(): void {
channel = null;
}
/** 웹 링크를 우측 모니터 새 창에서 열기 (같은 URL이면 기존 창 포커스) */
export async function openLinkOnRightMonitor(url: string, windowName: string): Promise<Window | null> {
const features = await getRightMonitorWindowFeatures();
/** 웹 링크를 우측 모니터 새 창에서 열기 */
export function openLinkOnRightMonitor(url: string, windowName: string): Window | null {
const features = buildSyncWindowFeatures();
const win = window.open(url, windowName, features);
try {
win?.focus();
} catch {
// popup-blocked 등
// ignore
}
if (win) {
void getRightMonitorWindowFeatures().then((f) => {
const { left, top, width, height } = parseWindowFeatures(f);
if (win && !win.closed && left != null && top != null && width && height) {
applyWindowPlacement(win, left, top, width, height);
}
});
}
return win;
}

View File

@@ -216,7 +216,7 @@ export default function DashboardPage() {
stats={stats}
activeStatus={activeStatus}
onStatusChange={setActiveStatus}
onOpenDetailWindow={() => void openDetailWindow()}
onOpenDetailWindow={() => { openDetailWindow(); }}
onOpenTaskManager={() => setShowTaskManager(true)}
/>