차량 모니터 복귀 링크 경로 정리
This commit is contained in:
@@ -15,7 +15,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저
|
||||
- 허용된 차량 명령 TCP 전송
|
||||
- monitor 화면과 AJAX 상태 갱신
|
||||
- monitor 화면 WakeLock, 언어, 테마 토글
|
||||
- monitor 좌측 상단 블로그 복귀 링크
|
||||
- monitor 좌측 상단 이전 페이지 복귀 링크
|
||||
- 모바일 및 좁은 카드 폭에 맞춘 사용량 2줄 배치, 로그 가로 스크롤, 시간 문구 축약
|
||||
- 사용량/요금 표시와 통신사 과금 단위 보정 metadata
|
||||
- 4.95초 UI timeout과 60초 사용량 정산 분리
|
||||
@@ -48,6 +48,8 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저
|
||||
|
||||
헤더 우측 컨트롤은 WakeLock, 언어, 테마 순서입니다. WakeLock은 Screen Wake Lock API를 지원하는 브라우저에서 화면 꺼짐을 방지하며, 활성 상태는 초록색 아이콘 버튼으로 표시합니다. 사용자의 선택은 `localStorage`에 저장하고, 새로고침이나 탭 복귀 후에는 브라우저 정책에 맞춰 다시 획득을 시도합니다.
|
||||
|
||||
좌측 상단 복귀 링크는 이전 페이지가 `chaegeon.com` 계열이면 해당 URL을 사용합니다. 같은 출처에서 이동한 경우에는 이전 문서의 title을 읽어 표시하고, 직접 접속이나 이전 페이지 확인이 어려운 경우에는 `https://chaegeon.com/blog`를 기본 목적지로 사용합니다. `seo.chaegeon.com`의 상대 경로로 이동하지 않도록 절대 URL을 기본값으로 둡니다.
|
||||
|
||||
## 구성
|
||||
|
||||
- `api.php`: 차량 상태/제어 API
|
||||
|
||||
+80
-1
@@ -956,6 +956,10 @@ car_monitor_require_access(false);
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
|
||||
max-width: min(44vw, 260px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.blog-back-link:hover,
|
||||
.blog-back-link:focus {
|
||||
@@ -1368,7 +1372,7 @@ car_monitor_require_access(false);
|
||||
<i class="fas fa-car"></i>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/blog" class="blog-back-link"><span class="backArrow">‹</span> 블로그</a>
|
||||
<a href="https://chaegeon.com/blog" id="blogBackLink" class="blog-back-link"><span class="backArrow">‹</span> 블로그</a>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="app-title" data-i18n="appTitle">Car Monitor</div>
|
||||
<span id="live-badge" class="badge bg-secondary bg-opacity-10 text-secondary border border-secondary border-opacity-20 rounded-pill" style="font-size:0.6rem; vertical-align: top;">WAIT</span>
|
||||
@@ -1822,8 +1826,15 @@ car_monitor_require_access(false);
|
||||
let wakeLockWanted = false;
|
||||
let latestDashboardData = null;
|
||||
let latestDashboardMeta = null;
|
||||
const FALLBACK_BACK_TARGET = 'https://chaegeon.com/blog';
|
||||
const BACK_LINK_PATH_TITLE = {
|
||||
'/': '채건닷컴',
|
||||
'/home': '홈',
|
||||
'/blog': '블로그',
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setupBackLink();
|
||||
initLanguage();
|
||||
initTheme();
|
||||
initWakeLock();
|
||||
@@ -1858,6 +1869,74 @@ car_monitor_require_access(false);
|
||||
return I18N[currentLang]?.[key] || I18N.en[key] || key;
|
||||
}
|
||||
|
||||
function cleanPageTitle(title) {
|
||||
return String(title || '')
|
||||
.replace(/^채건닷컴\s*-\s*/i, '')
|
||||
.replace(/\s*-\s*채건닷컴$/i, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function sameSiteUrl(url) {
|
||||
try {
|
||||
const parsed = new URL(url, location.href);
|
||||
return parsed.hostname === location.hostname || parsed.hostname.endsWith('.chaegeon.com') || parsed.hostname === 'chaegeon.com'
|
||||
? parsed
|
||||
: null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function titleFromBackUrl(url) {
|
||||
const parsed = sameSiteUrl(url);
|
||||
if (!parsed) return '';
|
||||
return BACK_LINK_PATH_TITLE[parsed.pathname.replace(/\/$/, '') || '/'] || cleanPageTitle(parsed.pathname.split('/').filter(Boolean).pop() || '');
|
||||
}
|
||||
|
||||
function updateBackLinkLabel(label) {
|
||||
const link = document.getElementById('blogBackLink');
|
||||
if (!link) return;
|
||||
link.innerHTML = `<span class="backArrow">‹</span> ${label}`;
|
||||
}
|
||||
|
||||
async function resolveBackTarget() {
|
||||
const ref = sameSiteUrl(document.referrer || '');
|
||||
const current = new URL(location.href);
|
||||
const target = ref && ref.href !== current.href ? ref.href : FALLBACK_BACK_TARGET;
|
||||
let label = titleFromBackUrl(target);
|
||||
|
||||
if (ref && ref.origin === location.origin) {
|
||||
try {
|
||||
const res = await fetch(ref.href, { credentials: 'same-origin', cache: 'force-cache' });
|
||||
if (res.ok) {
|
||||
const html = await res.text();
|
||||
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
label = cleanPageTitle(doc.querySelector('title')?.textContent) || label;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return { href: target, label: label || '블로그' };
|
||||
}
|
||||
|
||||
function setupBackLink() {
|
||||
const link = document.getElementById('blogBackLink');
|
||||
if (!link) return;
|
||||
|
||||
link.href = FALLBACK_BACK_TARGET;
|
||||
updateBackLinkLabel('블로그');
|
||||
|
||||
resolveBackTarget().then(({ href, label }) => {
|
||||
link.href = href;
|
||||
updateBackLinkLabel(label);
|
||||
});
|
||||
|
||||
link.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
location.href = link.href || FALLBACK_BACK_TARGET;
|
||||
});
|
||||
}
|
||||
|
||||
function applyLanguage() {
|
||||
document.documentElement.setAttribute('lang', currentLang);
|
||||
document.title = t('appTitle');
|
||||
|
||||
Reference in New Issue
Block a user