fix: mobile
All checks were successful
/ upload (release) Successful in 22s

This commit is contained in:
pavel 2026-02-13 19:26:21 +01:00
commit b353deffbb
3 changed files with 166 additions and 33 deletions

View file

@ -30,13 +30,13 @@ const el = {
loginBtn: document.getElementById("login-btn"),
main: document.getElementById("main"),
status: document.getElementById("status"),
// Guilds
guildList: document.getElementById("guild-list"),
addGuildBtn: document.getElementById("add-guild-btn"),
guildTitle: document.getElementById("guild-title"),
createInviteBtn: document.getElementById("create-invite-btn"),
// Channels
channelList: document.getElementById("channel-list"),
voiceChannelList: document.getElementById("voice-channel-list"),
@ -44,36 +44,42 @@ const el = {
addVoiceBtn: document.getElementById("add-voice-btn"),
dmList: document.getElementById("dm-list"),
channelTitle: document.getElementById("channel-title"),
// Messages
messageList: document.getElementById("message-list"),
messageForm: document.getElementById("message-form"),
messageBody: document.getElementById("message-body"),
// User Panel
userName: document.getElementById("user-name"),
userAvatar: document.getElementById("user-avatar"),
logoutBtn: document.getElementById("logout-btn"),
// Voice Connection
voiceConnection: document.getElementById("voice-connection"),
vcChannelName: document.getElementById("vc-channel-name"),
voiceMuteBtn: document.getElementById("voice-mute-btn"),
voiceLeaveBtn: document.getElementById("voice-leave-btn"),
// Members
memberList: document.getElementById("member-list"),
// Modals
modalContainer: document.getElementById("modal-container"),
guildForm: document.getElementById("guild-form"),
guildName: document.getElementById("guild-name"),
modalCancel: document.getElementById("modal-cancel"),
channelModal: document.getElementById("channel-modal"),
channelForm: document.getElementById("channel-form"),
channelName: document.getElementById("channel-name"),
channelModalCancel: document.getElementById("channel-modal-cancel"),
// Mobile
mobileMenuBtn: document.getElementById("mobile-menu-btn"),
mobileMembersBtn: document.getElementById("mobile-members-btn"),
overlay: document.getElementById("overlay"),
utilitySidebar: document.getElementById("utility-sidebar"),
};
// --- API Helpers ---
@ -93,7 +99,7 @@ async function api(path, options = {}) {
try {
const body = await res.json();
detail = body.error || detail;
} catch {}
} catch { }
throw new Error(`${res.status}: ${detail}`);
}
@ -127,7 +133,7 @@ function formatDate(isoString) {
const d = new Date(isoString);
const now = new Date();
const isToday = d.toDateString() === now.toDateString();
if (isToday) {
return `Today at ${d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
}
@ -176,14 +182,13 @@ function renderChannels() {
for (const channel of state.channels) {
const row = document.createElement("button");
row.className = `channel-row ${
(channel.kind === 'text' && state.selectedTextChannelId === channel.id) ||
(channel.kind === 'voice' && state.selectedVoiceChannelId === channel.id) ? "active" : ""
}`;
row.className = `channel-row ${(channel.kind === 'text' && state.selectedTextChannelId === channel.id) ||
(channel.kind === 'voice' && state.selectedVoiceChannelId === channel.id) ? "active" : ""
}`;
const iconName = channel.kind === 'text' ? 'hash' : 'volume-2';
row.innerHTML = `<i data-lucide="${iconName}"></i> <span>${escapeHtml(channel.name)}</span>`;
row.onclick = async () => {
if (channel.kind === 'text') {
state.selectedDmUserId = null;
@ -202,13 +207,14 @@ function renderChannels() {
renderDMs();
joinVoice();
}
if (window.innerWidth <= 768) closeMobileMenus();
};
if (channel.kind === 'text') {
el.channelList.appendChild(row);
} else {
el.voiceChannelList.appendChild(row);
const participants = state.voicePresence.get(channel.id) || [];
if (participants.length > 0) {
const pList = document.createElement("div");
@ -259,11 +265,11 @@ function renderMessages(messages) {
for (const m of messages.slice().reverse()) {
const row = document.createElement("div");
const mDate = new Date(m.created_at);
const isGrouped = lastAuthorId === m.author_user_id &&
lastTime && (mDate - lastTime < 300000); // 5 minutes
const isGrouped = lastAuthorId === m.author_user_id &&
lastTime && (mDate - lastTime < 300000); // 5 minutes
row.className = `msg ${isGrouped ? "msg-grouped" : ""}`;
const displayName = m.author_display_name || "Unknown User";
if (isGrouped) {
@ -412,7 +418,7 @@ async function refreshVoicePresence() {
function startVoicePresencePolling() {
if (state.voicePresencePollId) clearInterval(state.voicePresencePollId);
state.voicePresencePollId = setInterval(() => {
refreshVoicePresence().catch(() => {});
refreshVoicePresence().catch(() => { });
}, 3000);
}
@ -469,7 +475,7 @@ function stopAndClearAudioPipeline() {
for (const track of state.voice.rawStream.getTracks()) track.stop();
}
if (state.voice.audioContext) {
state.voice.audioContext.close().catch(() => {});
state.voice.audioContext.close().catch(() => { });
}
state.voice.localStream = null;
state.voice.rawStream = null;
@ -615,7 +621,7 @@ async function joinVoice() {
const channel = state.channels.find(c => c.id === state.selectedVoiceChannelId);
el.vcChannelName.textContent = channel ? channel.name : "Voice";
el.voiceConnection.classList.remove("hidden");
refreshVoicePresence().catch(() => {});
refreshVoicePresence().catch(() => { });
};
ws.onmessage = async (event) => {
@ -636,7 +642,7 @@ async function joinVoice() {
} else if (msg.type === "signal") {
await handleSignal(msg.from_user_id, msg.kind, msg.data);
}
refreshVoicePresence().catch(() => {});
refreshVoicePresence().catch(() => { });
};
ws.onclose = () => {
@ -646,7 +652,7 @@ async function joinVoice() {
stopAndClearAudioPipeline();
state.voice.joinedChannelId = null;
state.voice.ws = null;
refreshVoicePresence().catch(() => {});
refreshVoicePresence().catch(() => { });
};
}
@ -665,23 +671,55 @@ function toggleMute() {
lucide.createIcons();
}
// --- Mobile Logic ---
function toggleMobileMenu() {
const isOpen = el.main.classList.contains("menu-open");
if (isOpen) {
el.main.classList.remove("menu-open");
el.overlay.classList.add("hidden");
} else {
el.main.classList.add("menu-open");
el.utilitySidebar.classList.remove("active"); // close members if open
el.overlay.classList.remove("hidden");
}
}
function toggleMobileMembers() {
const isOpen = el.utilitySidebar.classList.contains("active");
if (isOpen) {
el.utilitySidebar.classList.remove("active");
el.overlay.classList.add("hidden");
} else {
el.utilitySidebar.classList.add("active");
el.main.classList.remove("menu-open"); // close menu if open
el.overlay.classList.remove("hidden");
}
}
function closeMobileMenus() {
el.main.classList.remove("menu-open");
el.utilitySidebar.classList.remove("active");
el.overlay.classList.add("hidden");
}
// --- Initialization ---
async function init() {
lucide.createIcons();
el.loginBtn.onclick = () => { location.href = "/auth/login"; };
el.logoutBtn.onclick = async () => {
await leaveVoice();
await api("/auth/logout", { method: "POST" });
location.reload();
};
el.addGuildBtn.onclick = () => { el.modalContainer.classList.remove("hidden"); };
el.createInviteBtn.onclick = createInviteLink;
el.modalCancel.onclick = () => { el.modalContainer.classList.add("hidden"); };
el.addTextBtn.onclick = () => {
if (!state.selectedGuildId) {
alert("Create or select a server first.");
@ -700,6 +738,11 @@ async function init() {
};
el.channelModalCancel.onclick = () => { el.channelModal.classList.add("hidden"); };
// Mobile Listeners
if (el.mobileMenuBtn) el.mobileMenuBtn.onclick = toggleMobileMenu;
if (el.mobileMembersBtn) el.mobileMembersBtn.onclick = toggleMobileMembers;
if (el.overlay) el.overlay.onclick = closeMobileMenus;
el.guildForm.onsubmit = async (e) => {
e.preventDefault();
try {