Skip to content

Commit a07ce6a

Browse files
committed
v3.10.4: defensive self-DM PiP open fix + sync docs
1 parent c6985f8 commit a07ce6a

6 files changed

Lines changed: 55 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). Haven uses [Sema
1313

1414
---
1515

16+
## [3.10.4] — 2026-04-27
17+
18+
### Fixed
19+
- **Self-DM ("Notes to self") sometimes wouldn't open the picture-in-picture panel** — reported by SerChiz. The PiP would show a loading state forever (or appear not to open at all) in specific edge cases: when the same DM was already the user's active main channel, when the local end-to-end key cache was missing the user's own key for self-DMs, or when the partner key fetch silently stalled. The PiP now (a) renders message history regardless of which channel is currently focused, (b) seeds the partner key for self-DMs from the local E2E key directly without a server round-trip, and (c) replaces the "Loading…" placeholder with a friendly fallback after 6 seconds so the panel never appears stuck.
20+
- **DM picture-in-picture didn't actively clear unread for the DM open in the panel** — new messages arriving for the active PiP DM now mark the DM as read (and clear its unread badge) instead of bumping the unread count, so the badge no longer sticks while you're already reading the conversation.
21+
22+
---
23+
1624
## [3.10.3] — 2026-04-27
1725

1826
### Added

docs/index.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,12 +1415,12 @@ <h2>Download Haven</h2>
14151415
</div>
14161416

14171417
<div class="download-card fade-in">
1418-
<h2>&#x2B21; Haven Server &mdash; v3.10.3</h2>
1418+
<h2>&#x2B21; Haven Server &mdash; v3.10.4</h2>
14191419
<p class="download-version">Latest stable release &middot; Windows, macOS &amp; Linux &middot; ~5 MB</p>
14201420

14211421
<div class="download-btn-group">
1422-
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.3.zip" class="btn btn-primary download-main">
1423-
<span class="icon">&#x2B07;</span> Download v3.10.3 (.zip)
1422+
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.4.zip" class="btn btn-primary download-main">
1423+
<span class="icon">&#x2B07;</span> Download v3.10.4 (.zip)
14241424
</a>
14251425
<div class="download-alt-links">
14261426
<a href="https://github.com/ancsemi/Haven" target="_blank">&#9965; View on GitHub</a>
@@ -1437,7 +1437,11 @@ <h2>&#x2B21; Haven Server &mdash; v3.10.3</h2>
14371437
<div class="version-list">
14381438
<div class="version-list-inner">
14391439
<div class="version-item">
1440-
<div><span class="v-name">v3.10.3</span><span class="v-tag latest">Latest</span> &mdash; #channel autocomplete, DM PiP z-index fix, voice highlight regressions</div>
1440+
<div><span class="v-name">v3.10.4</span><span class="v-tag latest">Latest</span> &mdash; Self-DM PiP open fix, DM PiP unread auto-clear</div>
1441+
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.4.zip">Download &rarr;</a>
1442+
</div>
1443+
<div class="version-item">
1444+
<div><span class="v-name">v3.10.3</span> &mdash; #channel autocomplete, DM PiP z-index fix, voice highlight regressions</div>
14411445
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.3.zip">Download &rarr;</a>
14421446
</div>
14431447
<div class="version-item">

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "haven",
3-
"version": "3.10.3",
3+
"version": "3.10.4",
44
"description": "Haven — self-hosted private chat for your server, your rules",
55
"license": "AGPL-3.0",
66
"main": "server.js",

public/js/modules/app-socket.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,18 +398,22 @@ _setupSocketListeners() {
398398
});
399399

400400
this.socket.on('message-history', async (data) => {
401-
// DM PiP: if this history is for the active PiP DM, render it there
402-
// (and ignore the non-current-channel guard).
403-
if (this._activeDMPip && data.channelCode === this._activeDMPip
404-
&& data.channelCode !== this.currentChannel) {
401+
// DM PiP: if this history is for the active PiP DM, render it there.
402+
// We render the PiP regardless of currentChannel so the loading
403+
// placeholder always clears even when the same DM is also the active
404+
// main channel (e.g. user opened the DM in fullscreen previously,
405+
// then opened the PiP — issue: SerChiz v3.10.3).
406+
if (this._activeDMPip && data.channelCode === this._activeDMPip) {
405407
// E2E: ensure partner key is fetched before decrypting (self-DMs included)
406408
const pipCh = this.channels.find(c => c.code === data.channelCode);
407409
if (pipCh && pipCh.is_dm && pipCh.dm_target && !this._dmPublicKeys[pipCh.dm_target.id]) {
408410
await this._fetchDMPartnerKey(pipCh);
409411
}
410412
await this._decryptMessages(data.messages, data.channelCode);
411413
this._renderDMPiPHistory?.(data.messages);
412-
return;
414+
// If the PiP DM isn't ALSO the current channel, we're done.
415+
if (data.channelCode !== this.currentChannel) return;
416+
// Otherwise fall through so the main pane renders too.
413417
}
414418
if (data.channelCode !== this.currentChannel) return;
415419
// E2E: decrypt DM messages before rendering

public/js/modules/app-utilities.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,11 +1833,29 @@ _openDMPiP(code) {
18331833
// Clear messages and request fresh
18341834
const msgsEl = document.getElementById('dm-pip-messages');
18351835
if (msgsEl) msgsEl.innerHTML = '<div class="dm-pip-loading">Loading…</div>';
1836-
// E2E: ensure partner key is loaded before history arrives so messages decrypt
1836+
// E2E: ensure partner key is loaded before history arrives so messages decrypt.
1837+
// For self-DMs the "partner" is the user themselves, so seed our own public
1838+
// key directly instead of round-tripping through the server. Avoids any
1839+
// chance of the loading state lingering when the server's get-public-key
1840+
// for our own id returns null/empty (issue: SerChiz v3.10.3).
18371841
if (ch.dm_target && this._dmPublicKeys && !this._dmPublicKeys[ch.dm_target.id]) {
1838-
try { this._fetchDMPartnerKey?.(ch); } catch {}
1842+
if (ch.is_self_dm && this.e2e && this.e2e.publicKeyJwk) {
1843+
this._dmPublicKeys[ch.dm_target.id] = this.e2e.publicKeyJwk;
1844+
} else {
1845+
try { this._fetchDMPartnerKey?.(ch); } catch {}
1846+
}
18391847
}
18401848
this.socket.emit('get-messages', { code });
1849+
// Safety: if message-history doesn't arrive within 6s (e.g. a transient
1850+
// server issue or a stuck E2E key fetch), replace the localized "Loading…"
1851+
// placeholder so the panel never looks frozen. Cleared on next open/close.
1852+
clearTimeout(this._dmPipLoadingTimer);
1853+
this._dmPipLoadingTimer = setTimeout(() => {
1854+
const stillLoading = document.querySelector('#dm-pip-messages .dm-pip-loading');
1855+
if (stillLoading && this._activeDMPip === code) {
1856+
stillLoading.textContent = 'No messages yet.';
1857+
}
1858+
}, 6000);
18411859

18421860
// Clear any stale reply state
18431861
this._clearDMPiPReply();
@@ -1850,6 +1868,7 @@ _openDMPiP(code) {
18501868
_closeDMPiP() {
18511869
this._activeDMPip = null;
18521870
this._dmPipReplyingTo = null;
1871+
clearTimeout(this._dmPipLoadingTimer);
18531872
try { localStorage.removeItem('haven_active_dm_pip'); } catch {}
18541873
const panel = document.getElementById('dm-pip-panel');
18551874
if (panel) panel.style.display = 'none';

website/index.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,12 +1415,12 @@ <h2>Download Haven</h2>
14151415
</div>
14161416

14171417
<div class="download-card fade-in">
1418-
<h2>&#x2B21; Haven Server &mdash; v3.10.3</h2>
1418+
<h2>&#x2B21; Haven Server &mdash; v3.10.4</h2>
14191419
<p class="download-version">Latest stable release &middot; Windows, macOS &amp; Linux &middot; ~5 MB</p>
14201420

14211421
<div class="download-btn-group">
1422-
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.3.zip" class="btn btn-primary download-main">
1423-
<span class="icon">&#x2B07;</span> Download v3.10.3 (.zip)
1422+
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.4.zip" class="btn btn-primary download-main">
1423+
<span class="icon">&#x2B07;</span> Download v3.10.4 (.zip)
14241424
</a>
14251425
<div class="download-alt-links">
14261426
<a href="https://github.com/ancsemi/Haven" target="_blank">&#9965; View on GitHub</a>
@@ -1437,7 +1437,11 @@ <h2>&#x2B21; Haven Server &mdash; v3.10.3</h2>
14371437
<div class="version-list">
14381438
<div class="version-list-inner">
14391439
<div class="version-item">
1440-
<div><span class="v-name">v3.10.3</span><span class="v-tag latest">Latest</span> &mdash; #channel autocomplete, DM PiP z-index fix, voice highlight regressions</div>
1440+
<div><span class="v-name">v3.10.4</span><span class="v-tag latest">Latest</span> &mdash; Self-DM PiP open fix, DM PiP unread auto-clear</div>
1441+
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.4.zip">Download &rarr;</a>
1442+
</div>
1443+
<div class="version-item">
1444+
<div><span class="v-name">v3.10.3</span> &mdash; #channel autocomplete, DM PiP z-index fix, voice highlight regressions</div>
14411445
<a href="https://github.com/ancsemi/Haven/archive/refs/tags/v3.10.3.zip">Download &rarr;</a>
14421446
</div>
14431447
<div class="version-item">

0 commit comments

Comments
 (0)