feat(instance): сервер установщика больше не «главный»

Понятие главного сервера убрано из поведения: сервер, создаваемый установщиком,
обычный (is_main = false), автовступления в него нет, а в настройках инстанса
он остаётся лишь справочно (main_guild_id → AdminGuildID в ответе API).
Клиент от него больше не зависит: новичок начинает с пустым списком серверов.
This commit is contained in:
2026-09-20 01:12:01 +03:00
parent 33dc1fec96
commit f26728113d
9 changed files with 458 additions and 23 deletions
+68
View File
@@ -0,0 +1,68 @@
import { useEffect, useState } from 'react';
import type { VoiceDeviceKind } from '@/stores/voice';
/** Устройство ввода/вывода в том виде, в каком его показывает интерфейс. */
export interface MediaDevice {
deviceId: string;
/** Подпись может быть пустой: браузер скрывает её без разрешения. */
label: string;
}
export interface MediaDevicesResult {
devices: MediaDevice[];
/** Доступен ли `navigator.mediaDevices.enumerateDevices()`. */
supported: boolean;
}
/**
* Список устройств нужного вида через `navigator.mediaDevices`.
*
* Без разрешения на доступ браузер отдаёт устройства с пустыми подписями —
* их показываем как «Устройство N». Если API недоступно (старый браузер или
* тестовое окружение), возвращаем `supported: false`.
*/
export function useMediaDevices(kind: VoiceDeviceKind): MediaDevicesResult {
const supported =
typeof navigator !== 'undefined' &&
typeof navigator.mediaDevices?.enumerateDevices === 'function';
const [devices, setDevices] = useState<MediaDevice[]>([]);
useEffect(() => {
if (!supported) {
setDevices([]);
return;
}
let cancelled = false;
const load = (): void => {
void navigator.mediaDevices
.enumerateDevices()
.then((list) => {
if (cancelled) {
return;
}
setDevices(
list
.filter((device) => device.kind === kind)
.map((device) => ({ deviceId: device.deviceId, label: device.label })),
);
})
.catch(() => {
if (!cancelled) {
setDevices([]);
}
});
};
load();
// Список меняется при подключении гарнитуры — обновляем его на лету.
navigator.mediaDevices.addEventListener?.('devicechange', load);
return () => {
cancelled = true;
navigator.mediaDevices.removeEventListener?.('devicechange', load);
};
}, [kind, supported]);
return { devices, supported };
}