fix: restore popup opening on user click for detail window
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ export default function DashboardPage() {
|
||||
stats={stats}
|
||||
activeStatus={activeStatus}
|
||||
onStatusChange={setActiveStatus}
|
||||
onOpenDetailWindow={() => void openDetailWindow()}
|
||||
onOpenDetailWindow={() => { openDetailWindow(); }}
|
||||
onOpenTaskManager={() => setShowTaskManager(true)}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user