release: v4.8.8 file transfer consent fix
CodeQL Analysis / Analyze CodeQL (push) Has been cancelled
Deploy Application / deploy (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
Mirror to PrivacyGuides / mirror (push) Has been cancelled

Complete the mandatory receiver-consent gate that was wired in the
backend but never connected to the UI callback chain:

- Add the missing onIncomingFileRequest (4th) callback to
  setFileTransferCallbacks in app.jsx — its absence caused
  handleFileTransferStart to auto-reject every incoming file.
- Remove independent callback registration from FileTransferComponent;
  the component was overwriting app-level callbacks on mount and
  nulling all four on unmount, silently breaking progress/received/
  error handlers whenever the panel was hidden.
- Lift pendingIncomingFiles state to the root component so consent
  prompts are shown regardless of panel visibility; auto-open the
  panel on incoming request.
- Add getReceivedFileObjectURL / revokeReceivedFileObjectURL on
  EnhancedSecureWebRTCManager for download buttons in the panel.
- Update file-transfer-ui-cleanup regression test to match the new
  single-owner callback architecture.
- All 14 tests pass; clean production build.
This commit is contained in:
lockbitchat
2026-05-26 22:40:36 -04:00
parent 2468cb495e
commit d11f250257
12 changed files with 200 additions and 209 deletions
+44 -8
View File
@@ -1241,11 +1241,20 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
isVerified,
chatMessagesRef,
scrollToBottom,
webrtcManager
webrtcManager,
pendingIncomingFiles = [],
onIncomingDecision
}) => {
const [showScrollButton, setShowScrollButton] = React.useState(false);
const [showFileTransfer, setShowFileTransfer] = React.useState(false);
// Auto-open the file transfer panel when an incoming request arrives
React.useEffect(() => {
if (pendingIncomingFiles.length > 0) {
setShowFileTransfer(true);
}
}, [pendingIncomingFiles.length]);
React.useEffect(() => {
if (chatMessagesRef.current && messages.length > 0) {
const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;
@@ -1475,7 +1484,9 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
}, 'FileTransferComponent not loaded')
), {
webrtcManager: webrtcManager,
isConnected: isFileTransferReady()
isConnected: isFileTransferReady(),
pendingIncomingFiles: pendingIncomingFiles,
onIncomingDecision: onIncomingDecision
})
]
)
@@ -1584,9 +1595,11 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
const [pendingSession, setPendingSession] = React.useState(null);
// All security features are enabled by default - no payment required
const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]);
// ============================================
// CENTRALIZED CONNECTION STATE MANAGEMENT
// ============================================
@@ -2103,7 +2116,7 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
// Error callback
(error) => {
console.error('File transfer error:', error);
if (error.includes('Connection not ready')) {
addMessageWithAutoScroll(` File transfer error: connection not ready. Try again later.`, 'system');
} else if (error.includes('File too large')) {
@@ -2111,6 +2124,14 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
} else {
addMessageWithAutoScroll(` File transfer error: ${error}`, 'system');
}
},
// Incoming file request callback — receiver must explicitly accept before any data is sent
(fileRequest) => {
setPendingIncomingFiles(prev => {
if (prev.some(f => f.fileId === fileRequest.fileId)) return prev;
return [...prev, fileRequest];
});
}
);
}
@@ -3464,6 +3485,18 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
// Session manager removed - all features enabled by default
};
const handleIncomingDecision = React.useCallback(async (fileId, accepted) => {
try {
if (accepted) {
await webrtcManagerRef.current?.acceptIncomingFile(fileId);
} else {
await webrtcManagerRef.current?.rejectIncomingFile(fileId);
}
} finally {
setPendingIncomingFiles(prev => prev.filter(f => f.fileId !== fileId));
}
}, []);
const handleDisconnect = () => {
try {
setSessionTimeLeft(0);
@@ -3513,7 +3546,8 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
// Clear messages
setMessages([]);
setPendingIncomingFiles([]);
// Clear console
if (typeof console.clear === 'function') {
console.clear();
@@ -3705,7 +3739,9 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
isVerified: isVerified,
chatMessagesRef: chatMessagesRef,
scrollToBottom: scrollToBottom,
webrtcManager: webrtcManagerRef.current
webrtcManager: webrtcManagerRef.current,
pendingIncomingFiles: pendingIncomingFiles,
onIncomingDecision: handleIncomingDecision
});
})()
: React.createElement(EnhancedConnectionSetup, {