release: v4.8.12 chat notification & file-transfer UI fixes
fix(file-transfer): announce received file once, not many times The per-transfer lock used a single `if` check, so when 3+ chunk operations queued on the same fileId they awaited the same in-flight lock and then ran concurrently, breaking assembly atomicity. The lock now loops until the slot is free (true serialization) and file assembly is idempotent, so `File received` shows exactly once per file. fix(verification): stop duplicate connection-setup system messages handleVerificationBothConfirmed had no guard, so when both peers sent verification_both_confirmed symmetrically one side ran both the local detection path and the peer-notification path, emitting "Both parties confirmed!" and the verified transition (and "Secure connection established") twice. It now bails out if both confirmations are already recorded. fix(ui): wrap long DTLS fingerprint inside the chat bubble The message text column is a flex child with default min-width:auto, so the long unbroken fingerprint overflowed. Added min-w-0 so break-words can wrap it. chore(release): bump version to 4.8.12 in header, init banner, manifest
This commit is contained in:
+2
-2
@@ -276,7 +276,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic
|
||||
}),
|
||||
React.createElement('div', {
|
||||
key: 'text',
|
||||
className: "flex-1"
|
||||
className: "flex-1 min-w-0"
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'message',
|
||||
@@ -2071,7 +2071,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic
|
||||
}
|
||||
}
|
||||
|
||||
handleMessage(' SecureBit.chat Enhanced Security Edition v4.8.10 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
|
||||
handleMessage(' SecureBit.chat Enhanced Security Edition v4.8.12 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
|
||||
|
||||
const handleBeforeUnload = (event) => {
|
||||
if (event.type === 'beforeunload' && !isTabSwitching) {
|
||||
|
||||
@@ -539,7 +539,7 @@ const EnhancedMinimalHeader = ({
|
||||
React.createElement('p', {
|
||||
key: 'subtitle',
|
||||
className: 'text-xs sm:text-sm text-muted hidden sm:block'
|
||||
}, 'End-to-end freedom v4.8.10')
|
||||
}, 'End-to-end freedom v4.8.12')
|
||||
])
|
||||
]),
|
||||
|
||||
|
||||
@@ -11460,6 +11460,14 @@ async processMessage(data) {
|
||||
}
|
||||
|
||||
handleVerificationBothConfirmed(data) {
|
||||
// Both parties can reach the "both confirmed" state independently: this
|
||||
// party may have already detected it locally (_checkBothVerificationsConfirmed)
|
||||
// and now also receives the peer's notification. Without this guard the
|
||||
// "Both parties confirmed" message and the verified transition fire twice.
|
||||
if (this.bothVerificationsConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle notification that both parties have confirmed
|
||||
this.bothVerificationsConfirmed = true;
|
||||
|
||||
|
||||
@@ -185,20 +185,24 @@ class AtomicOperations {
|
||||
}
|
||||
|
||||
async withLock(key, operation) {
|
||||
if (this.locks.has(key)) {
|
||||
// Serialize all operations sharing the same key. Must loop (not a single
|
||||
// `if`): when 3+ callers queue on one key they all await the same in-flight
|
||||
// lock, so after it resolves we have to re-check before claiming the slot —
|
||||
// otherwise multiple operations run concurrently and break atomicity.
|
||||
while (this.locks.has(key)) {
|
||||
await this.locks.get(key);
|
||||
}
|
||||
|
||||
const lockPromise = (async () => {
|
||||
try {
|
||||
return await operation();
|
||||
} finally {
|
||||
this.locks.delete(key);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
let releaseLock;
|
||||
const lockPromise = new Promise(resolve => { releaseLock = resolve; });
|
||||
this.locks.set(key, lockPromise);
|
||||
return lockPromise;
|
||||
|
||||
try {
|
||||
return await operation();
|
||||
} finally {
|
||||
this.locks.delete(key);
|
||||
releaseLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1384,6 +1388,14 @@ class EnhancedSecureFileTransfer {
|
||||
}
|
||||
|
||||
async assembleFile(receivingState) {
|
||||
// Idempotency guard: assembly must run (and onFileReceived must fire)
|
||||
// exactly once per transfer. Guards against any duplicate/concurrent
|
||||
// completion trigger so the receiver never sees the same file repeated.
|
||||
if (receivingState._assembled) {
|
||||
return;
|
||||
}
|
||||
receivingState._assembled = true;
|
||||
|
||||
try {
|
||||
receivingState.status = 'assembling';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user