Files
Gacy-Srtool/js/unstuck.js
T
CAtlas0784 992875348d feat: make the web UI usable on phones (360-768px)
Responsive pass limited to CSS plus small markup hooks, so desktop (>900px) is unchanged.

- shell: static header with 2-row controls, sticky scrollable tab bar with fade + snap, 40px touch targets, 16px inputs, full-width toasts with safe-area
- grids collapse to one column; dashboard counters become 3 compact tiles and analyst stats 2x2
- equipment: full-width subtabs/filters, 3-up tiles, inspector becomes a bottom sheet on phones
- inventory: 2 columns fit without side scrolling, full-width quantity inputs
- characters: full-width search, 2-column avatar grid with stacked portrait
- dialogs: dvh-capped bottom sheets, two-column light cone and avatar pickers
- json/monster/settings/console form and scroll fixes
- compact one-row header for landscape phones (844x390 174px -> 108px)
- i18n: localize inventory view and relic-set search aria labels/placeholder
- bump styles.css 2.33, item-catalog.css 3, app.js 3.27, i18n.js 2.22
2026-11-02 10:43:07 +07:00

67 lines
3.3 KiB
JavaScript

import { getCurrentLanguage } from './i18n.js?v=2.22';
const strings = {
en: {
button: 'Unstuck',
description: 'Clear a stuck loading spinner without restarting the game. Keeps saved CW progress.',
offline: 'Connect to the GaCy GO server first.',
queued: 'Recovery queued. Waiting for the game…',
completed: 'The game cleared its network wait. Try closing or retrying the page.',
delayed: 'No acknowledgement from the game yet. Recovery is still queued; the game must be connected.',
failed: 'The game could not clear the wait.',
unsupported: 'This server does not support Unstuck yet.'
},
th: {
button: 'Unstuck / แก้ค้าง',
description: 'ปลดตัวหมุนค้างโดยไม่ต้องออกเกม และเก็บเซฟรอบ CW ไว้',
offline: 'เชื่อมต่อเซิร์ฟเวอร์ GaCy GO ก่อน',
queued: 'ส่งคำสั่งแล้ว กำลังรอเกมตอบกลับ…',
completed: 'เกมปลดการรอแพ็กเก็ตแล้ว ลองปิดหน้าต่างหรือกดใหม่ได้',
delayed: 'เกมยังไม่ตอบรับ คำสั่งยังอยู่ในคิวและต้องเชื่อมต่อเกมไว้',
failed: 'เกมปลดการรอไม่สำเร็จ',
unsupported: 'เซิร์ฟเวอร์นี้ยังไม่รองรับ Unstuck'
}
};
export function initializeUnstuck({ apiCall, isConnected, showToast }) {
const button = document.getElementById('btn-client-unstuck');
const description = document.getElementById('client-unstuck-description');
const status = document.getElementById('client-unstuck-status');
if (!button) return;
const text = () => strings[getCurrentLanguage()] || strings.en;
const translate = () => {
button.textContent = text().button;
if (description) description.textContent = text().description;
};
translate();
document.getElementById('lang-select')?.addEventListener('change', translate);
button.addEventListener('click', async () => {
if (!isConnected()) { showToast(text().offline, 'error'); return; }
button.disabled = true;
try {
const result = await apiCall('/api/client/unstuck', 'POST', {});
if (!result?.request_id || result.state !== 'queued') throw new Error(text().unsupported);
if (status) status.textContent = text().queued;
for (let attempt = 0; attempt < 16; attempt++) {
await new Promise(resolve => setTimeout(resolve, 750));
const acknowledgement = await apiCall('/api/client/unstuck');
if (acknowledgement?.request_id !== result.request_id) continue;
if (acknowledgement.state === 'completed') {
if (status) status.textContent = text().completed;
showToast(text().completed, 'success');
return;
}
if (acknowledgement.state === 'failed') {
throw new Error(acknowledgement.error || text().failed);
}
}
if (status) status.textContent = text().delayed;
} catch (error) {
if (status) status.textContent = error.message;
showToast(error.message, 'error');
} finally {
button.disabled = false;
}
});
}