cffcec72b8
Первый инкремент Фазы 6 (docs/client-tauri.md): окно с тем же веб-клиентом (по умолчанию https://gl.mhspx.su, адрес настраивается), трей с меню и индикатором непрочитанных, нативные уведомления, глобальный push-to-talk и переключение микрофона/deafen, автозапуск, single-instance, сохранение геометрии окна, deep links glchat://…, автообновление по подписанному манифесту. Связь страницы с обёрткой — через платформенный адаптер (web/src/lib/platform.ts) и минимальный типизированный набор команд; в веб-клиенте появились системные уведомления об упоминаниях и личных сообщениях и переключатель в настройках. В web/src/stores/gateway.ts вместе с вызовом уведомления лежат правки по дефектам realtime (READY и сессии) из параллельной волны: файл коммитится целиком по договорённости с координатором.
166 lines
6.1 KiB
JavaScript
166 lines
6.1 KiB
JavaScript
// Страница настроек desktop-обёртки.
|
|
//
|
|
// Локальная страница (tauri://localhost) внутри главного окна: команды обёртки
|
|
// вызываются через встроенный IPC Tauri. Ничего, кроме настроек платформы,
|
|
// здесь нет — настройки клиента живут в самом клиенте.
|
|
|
|
/** Вызов команды обёртки: тонкая обёртка над IPC Tauri v2. */
|
|
const invoke = (command, args) => window.__TAURI_INTERNALS__.invoke(command, args);
|
|
|
|
/** Варианты комбинаций: пустая строка — «выключено». */
|
|
const OFF = { value: '', label: 'Выключено' };
|
|
|
|
const PTT_OPTIONS = [
|
|
OFF,
|
|
{ value: 'CommandOrControl+Shift+Space', label: 'Cmd/Ctrl + Shift + Пробел' },
|
|
{ value: 'CommandOrControl+Shift+M', label: 'Cmd/Ctrl + Shift + M' },
|
|
{ value: 'CommandOrControl+Shift+V', label: 'Cmd/Ctrl + Shift + V' },
|
|
{ value: 'CommandOrControl+Shift+B', label: 'Cmd/Ctrl + Shift + B' },
|
|
{ value: 'Control+Shift+Space', label: 'Ctrl + Shift + Пробел' },
|
|
];
|
|
|
|
const MUTE_OPTIONS = [
|
|
OFF,
|
|
{ value: 'CommandOrControl+Shift+M', label: 'Cmd/Ctrl + Shift + M' },
|
|
{ value: 'CommandOrControl+Shift+U', label: 'Cmd/Ctrl + Shift + U' },
|
|
{ value: 'Control+Shift+M', label: 'Ctrl + Shift + M' },
|
|
];
|
|
|
|
const DEAFEN_OPTIONS = [
|
|
OFF,
|
|
{ value: 'CommandOrControl+Shift+D', label: 'Cmd/Ctrl + Shift + D' },
|
|
{ value: 'CommandOrControl+Shift+F', label: 'Cmd/Ctrl + Shift + F' },
|
|
{ value: 'Control+Shift+D', label: 'Ctrl + Shift + D' },
|
|
];
|
|
|
|
/** Поля формы и соответствующие настройки. */
|
|
const fields = {
|
|
instanceUrl: document.getElementById('instance-url'),
|
|
autostart: document.getElementById('autostart'),
|
|
closeToTray: document.getElementById('close-to-tray'),
|
|
notifications: document.getElementById('notifications'),
|
|
pttShortcut: document.getElementById('ptt-shortcut'),
|
|
muteShortcut: document.getElementById('mute-shortcut'),
|
|
deafenShortcut: document.getElementById('deafen-shortcut'),
|
|
};
|
|
|
|
const status = document.getElementById('status');
|
|
const version = document.getElementById('version');
|
|
const updatesHint = document.getElementById('updates-hint');
|
|
|
|
/** Заполняет выпадающий список комбинаций. */
|
|
function fillSelect(select, options, current) {
|
|
select.replaceChildren();
|
|
for (const option of options) {
|
|
const node = document.createElement('option');
|
|
node.value = option.value;
|
|
node.textContent = option.label;
|
|
select.append(node);
|
|
}
|
|
// Комбинация могла быть задана вручную (например, прежней версией) — не теряем её.
|
|
if (current && !options.some((option) => option.value === current)) {
|
|
const node = document.createElement('option');
|
|
node.value = current;
|
|
node.textContent = current;
|
|
select.append(node);
|
|
}
|
|
select.value = current ?? '';
|
|
}
|
|
|
|
/** Показывает состояние сохранения. */
|
|
function setStatus(text, isError = false) {
|
|
status.textContent = text;
|
|
status.classList.toggle('error', isError);
|
|
}
|
|
|
|
/** Переносит настройки в форму. */
|
|
function render(settings) {
|
|
fields.instanceUrl.value = settings.instanceUrl ?? '';
|
|
fields.autostart.checked = Boolean(settings.autostart);
|
|
fields.closeToTray.checked = Boolean(settings.closeToTray);
|
|
fields.notifications.checked = Boolean(settings.notifications);
|
|
fillSelect(fields.pttShortcut, PTT_OPTIONS, settings.pttShortcut);
|
|
fillSelect(fields.muteShortcut, MUTE_OPTIONS, settings.muteShortcut);
|
|
fillSelect(fields.deafenShortcut, DEAFEN_OPTIONS, settings.deafenShortcut);
|
|
}
|
|
|
|
/** Собирает настройки из формы. */
|
|
function collect() {
|
|
return {
|
|
instanceUrl: fields.instanceUrl.value.trim(),
|
|
autostart: fields.autostart.checked,
|
|
closeToTray: fields.closeToTray.checked,
|
|
notifications: fields.notifications.checked,
|
|
pttShortcut: fields.pttShortcut.value,
|
|
muteShortcut: fields.muteShortcut.value,
|
|
deafenShortcut: fields.deafenShortcut.value,
|
|
};
|
|
}
|
|
|
|
/** Сохраняет настройки и, если нужно, открывает клиент. */
|
|
async function save({ openClient = false } = {}) {
|
|
setStatus('Сохраняем…');
|
|
try {
|
|
const saved = await invoke('desktop_save_settings', { settings: collect() });
|
|
render(saved);
|
|
setStatus('Сохранено');
|
|
if (openClient) {
|
|
await invoke('desktop_open_instance');
|
|
}
|
|
} catch (error) {
|
|
setStatus(String(error), true);
|
|
}
|
|
}
|
|
|
|
document.getElementById('save').addEventListener('click', () => {
|
|
void save();
|
|
});
|
|
|
|
document.getElementById('open-client').addEventListener('click', () => {
|
|
void save({ openClient: true });
|
|
});
|
|
|
|
document.getElementById('reload-client').addEventListener('click', async () => {
|
|
await save();
|
|
try {
|
|
await invoke('desktop_reload');
|
|
} catch (error) {
|
|
setStatus(String(error), true);
|
|
}
|
|
});
|
|
|
|
document.getElementById('check-updates').addEventListener('click', async () => {
|
|
updatesHint.textContent = 'Проверяем обновления…';
|
|
try {
|
|
await invoke('desktop_check_updates');
|
|
updatesHint.textContent = 'Проверка завершена: результат показан в системном диалоге.';
|
|
} catch (error) {
|
|
updatesHint.textContent = String(error);
|
|
}
|
|
});
|
|
|
|
// Автосохранение переключателей и списков: отдельная кнопка нужна только для адреса.
|
|
for (const node of [
|
|
fields.autostart,
|
|
fields.closeToTray,
|
|
fields.notifications,
|
|
fields.pttShortcut,
|
|
fields.muteShortcut,
|
|
fields.deafenShortcut,
|
|
]) {
|
|
node.addEventListener('change', () => {
|
|
void save();
|
|
});
|
|
}
|
|
|
|
async function start() {
|
|
try {
|
|
render(await invoke('desktop_settings'));
|
|
version.textContent = `Версия обёртки: ${await invoke('desktop_version')}`;
|
|
} catch (error) {
|
|
setStatus(`Не удалось прочитать настройки: ${error}`, true);
|
|
}
|
|
}
|
|
|
|
void start();
|