From 1f5d79c8acca3ddca246933b2182aef2722601c3 Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 13 Feb 2026 18:07:26 +0100 Subject: [PATCH 01/79] fix --- .forgejo/workflows/pipeline.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.forgejo/workflows/pipeline.yaml b/.forgejo/workflows/pipeline.yaml index 4c2c575..47fc91f 100644 --- a/.forgejo/workflows/pipeline.yaml +++ b/.forgejo/workflows/pipeline.yaml @@ -29,10 +29,6 @@ jobs: key: ${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-target- - - run: | - cd frontend - npm install - npm run build - run: ~/.cargo/bin/cargo build -r - run: | export XDG_RUNTIME_DIR=/run/user/$(id -u) From bc933d2c796ae3562f3780029cf3d81d33417edb Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 13 Feb 2026 18:16:59 +0100 Subject: [PATCH 02/79] fix --- .forgejo/workflows/pipeline.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/pipeline.yaml b/.forgejo/workflows/pipeline.yaml index 47fc91f..4a4e218 100644 --- a/.forgejo/workflows/pipeline.yaml +++ b/.forgejo/workflows/pipeline.yaml @@ -34,6 +34,7 @@ jobs: export XDG_RUNTIME_DIR=/run/user/$(id -u) export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus systemctl --user stop discord - cp target/release/discord ~/discord/discord + mkdir -p ~/discord + cp target/release/chattz ~/discord/discord chmod +x ~/discord/discord systemctl --user start discord \ No newline at end of file From c128acef8669196c194b8dce3c83a4b3991c2ca2 Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 13 Feb 2026 18:29:13 +0100 Subject: [PATCH 03/79] fix --- .forgejo/workflows/pipeline.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.forgejo/workflows/pipeline.yaml b/.forgejo/workflows/pipeline.yaml index 4a4e218..4adede5 100644 --- a/.forgejo/workflows/pipeline.yaml +++ b/.forgejo/workflows/pipeline.yaml @@ -37,4 +37,7 @@ jobs: mkdir -p ~/discord cp target/release/chattz ~/discord/discord chmod +x ~/discord/discord + mkdir -p ~/discord/static + rm -rf ~/discord/static/* + cp -r static/* ~/discord/static/ systemctl --user start discord \ No newline at end of file From b353deffbbea8b2c7eac8e4887b2771ad4b900c7 Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 13 Feb 2026 19:26:21 +0100 Subject: [PATCH 04/79] fix: mobile --- static/app.js | 101 +++++++++++++++++++++++++++++++++------------- static/index.html | 11 ++++- static/styles.css | 87 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 166 insertions(+), 33 deletions(-) diff --git a/static/app.js b/static/app.js index 5d0ad15..6bbe240 100644 --- a/static/app.js +++ b/static/app.js @@ -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 = ` ${escapeHtml(channel.name)}`; - + 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 { diff --git a/static/index.html b/static/index.html index ef09364..f5e8b2f 100644 --- a/static/index.html +++ b/static/index.html @@ -108,8 +108,15 @@
+

Select a channel

+
+
@@ -122,7 +129,7 @@
-