Prevent reboot dialog enter reentry
This commit is contained in:
@@ -25,6 +25,7 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를
|
|||||||
- WakeLock 버튼으로 대시보드 화면 꺼짐 방지
|
- WakeLock 버튼으로 대시보드 화면 꺼짐 방지
|
||||||
- Reboot 버튼으로 2단계 확인 후 시스템 재부팅 요청
|
- Reboot 버튼으로 2단계 확인 후 시스템 재부팅 요청
|
||||||
- 기본 브라우저 alert/confirm/prompt 대신 대시보드 디자인에 맞춘 custom dialog 사용
|
- 기본 브라우저 alert/confirm/prompt 대신 대시보드 디자인에 맞춘 custom dialog 사용
|
||||||
|
- custom dialog는 Enter/Escape 키 입력을 자체 처리하며, Reboot 확인 흐름은 중복 실행을 차단
|
||||||
- Translate 버튼으로 `en`, `ko` UI 언어 토글. 버튼 표시는 상태 suffix 없이 고정
|
- Translate 버튼으로 `en`, `ko` UI 언어 토글. 버튼 표시는 상태 suffix 없이 고정
|
||||||
- Theme 버튼으로 `dark`, `light` UI 테마 토글. 버튼 표시는 상태 suffix 없이 고정
|
- Theme 버튼으로 `dark`, `light` UI 테마 토글. 버튼 표시는 상태 suffix 없이 고정
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ control-wifi-observe.service
|
|||||||
6. `control-wifi-observe.timer`는 사용자 접속과 무관하게 10초마다 5G 관측 세션을 갱신합니다.
|
6. `control-wifi-observe.timer`는 사용자 접속과 무관하게 10초마다 5G 관측 세션을 갱신합니다.
|
||||||
7. Push 등록은 권한 요청과 구독 저장이 끝나면 즉시 상태를 갱신합니다.
|
7. Push 등록은 권한 요청과 구독 저장이 끝나면 즉시 상태를 갱신합니다.
|
||||||
8. WakeLock 버튼은 활성 상태를 초록색 버튼으로 표시합니다.
|
8. WakeLock 버튼은 활성 상태를 초록색 버튼으로 표시합니다.
|
||||||
9. Reboot 버튼은 `재부팅` 단어 입력과 관리자 암호 재입력을 모두 통과한 뒤 서버 API로 재부팅을 요청합니다.
|
9. Reboot 버튼은 `재부팅` 단어 입력과 관리자 암호 재입력을 모두 통과한 뒤 서버 API로 재부팅을 요청합니다. 확인 단어 입력창에서 Enter를 눌러도 같은 Reboot 흐름이 다시 열리지 않도록 키 이벤트 기본 동작과 중복 실행을 차단합니다.
|
||||||
10. Translate 버튼은 최초 접속 시 디바이스 언어를 기본값으로 사용하고, 변경값은 `localStorage`의 `controlLang`에 저장합니다. 라벨은 `Translate` 또는 `번역`으로만 표시합니다.
|
10. Translate 버튼은 최초 접속 시 디바이스 언어를 기본값으로 사용하고, 변경값은 `localStorage`의 `controlLang`에 저장합니다. 라벨은 `Translate` 또는 `번역`으로만 표시합니다.
|
||||||
11. Theme 버튼은 기본값 `dark`로 시작하고, 변경값은 `localStorage`의 `controlTheme`에 저장합니다. 라벨은 `Theme` 또는 `테마`로만 표시합니다.
|
11. Theme 버튼은 기본값 `dark`로 시작하고, 변경값은 `localStorage`의 `controlTheme`에 저장합니다. 라벨은 `Theme` 또는 `테마`로만 표시합니다.
|
||||||
12. 시스템 상태는 `vcgencmd get_throttled`를 읽어 현재 저전압 여부와 부팅 후 이력, 최근 감지 시각, 현재/최근 지속시간을 표시합니다.
|
12. 시스템 상태는 `vcgencmd get_throttled`를 읽어 현재 저전압 여부와 부팅 후 이력, 최근 감지 시각, 현재/최근 지속시간을 표시합니다.
|
||||||
|
|||||||
+18
-3
@@ -66,6 +66,7 @@
|
|||||||
wsReconnectTimer: null,
|
wsReconnectTimer: null,
|
||||||
wsReconnectDelay: 1000,
|
wsReconnectDelay: 1000,
|
||||||
pushDevicesLastRefresh: 0,
|
pushDevicesLastRefresh: 0,
|
||||||
|
rebootRequesting: false,
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
};
|
};
|
||||||
@@ -513,8 +514,17 @@
|
|||||||
if (event.target === layer) onCancel();
|
if (event.target === layer) onCancel();
|
||||||
};
|
};
|
||||||
const onKeydown = event => {
|
const onKeydown = event => {
|
||||||
if (event.key === 'Escape') onCancel();
|
if (event.key === 'Escape') {
|
||||||
if (event.key === 'Enter' && (document.activeElement === input || !needsInput)) onOk();
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
onCancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === 'Enter' && (document.activeElement === input || !needsInput)) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
onOk();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
els.customDialogTitle.textContent = options.title || t('dialogConfirm');
|
els.customDialogTitle.textContent = options.title || t('dialogConfirm');
|
||||||
@@ -1576,6 +1586,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function requestReboot() {
|
async function requestReboot() {
|
||||||
|
if (state.rebootRequesting) return;
|
||||||
|
state.rebootRequesting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
const phrase = await window.customPrompt(t('rebootPrompt'), {
|
const phrase = await window.customPrompt(t('rebootPrompt'), {
|
||||||
title: t('rebootTitle'),
|
title: t('rebootTitle'),
|
||||||
okText: t('next'),
|
okText: t('next'),
|
||||||
@@ -1603,7 +1617,6 @@
|
|||||||
|
|
||||||
if (password === null) return;
|
if (password === null) return;
|
||||||
|
|
||||||
try {
|
|
||||||
els.rebootBtn.disabled = true;
|
els.rebootBtn.disabled = true;
|
||||||
notice(t('rebootSending'), 'info');
|
notice(t('rebootSending'), 'info');
|
||||||
await api('reboot', {
|
await api('reboot', {
|
||||||
@@ -1621,6 +1634,8 @@
|
|||||||
title: t('rebootFailed'),
|
title: t('rebootFailed'),
|
||||||
danger: true,
|
danger: true,
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
state.rebootRequesting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -319,7 +319,7 @@ html[data-theme="light"] .btn.secondary{background:#d9e2ef;color:#172033}html[da
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/app.js?v=20260607_prefs1"></script>
|
<script src="/assets/app.js?v=20260617_reboot_enter1"></script>
|
||||||
<script src="/assets/wakelock.js?v=20260607_prefs1"></script>
|
<script src="/assets/wakelock.js?v=20260607_prefs1"></script>
|
||||||
<script src="/push_subscribe.js?v=20260607_prefs1"></script>
|
<script src="/push_subscribe.js?v=20260607_prefs1"></script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user