Compare commits

..

No commits in common. "main" and "0.0.57" have entirely different histories.

2 changed files with 4 additions and 101 deletions

71
main.js
View file

@ -2,10 +2,9 @@ const { app, BrowserWindow, session, desktopCapturer, ipcMain, clipboard } = req
const { autoUpdater } = require('electron-updater'); const { autoUpdater } = require('electron-updater');
const path = require('path'); const path = require('path');
const packageJson = require('./package.json'); const packageJson = require('./package.json');
const PERIODIC_UPDATE_CHECK_INTERVAL_MS = 1 * 60 * 1000;
const updateState = { const updateState = {
status: 'idle', // idle | checking | available | downloading | downloaded | installing | not-available | error status: 'idle', // idle | checking | available | downloading | downloaded | not-available | error
info: null, info: null,
error: null, error: null,
}; };
@ -25,8 +24,6 @@ function isNewerVersionAvailable(info) {
} }
let updateCheckInProgress = false; let updateCheckInProgress = false;
let installInProgress = false;
let periodicUpdateTimer = null;
function resolveBackendUrl() { function resolveBackendUrl() {
const configuredUrl = (process.env.CHATTZ_URL || process.env.APP_BASE_URL || '').trim(); const configuredUrl = (process.env.CHATTZ_URL || process.env.APP_BASE_URL || '').trim();
@ -52,10 +49,6 @@ function webContentsOrigin(webContents) {
} }
async function runUpdateCheck(reason = 'manual') { async function runUpdateCheck(reason = 'manual') {
if (installInProgress) {
console.log(`Update check skipped (${reason}): install already in progress`);
return;
}
if (updateCheckInProgress) { if (updateCheckInProgress) {
console.log(`Update check skipped (${reason}): another check is already in progress`); console.log(`Update check skipped (${reason}): another check is already in progress`);
return; return;
@ -85,57 +78,6 @@ async function runUpdateCheck(reason = 'manual') {
} }
} }
function installDownloadedUpdate() {
if (installInProgress) {
console.log('Update install already in progress');
return;
}
installInProgress = true;
updateState.status = 'installing';
updateState.error = null;
broadcastUpdateState();
const wins = BrowserWindow.getAllWindows();
for (const win of wins) {
if (win.isDestroyed()) continue;
try {
win.removeAllListeners('close');
win.destroy();
} catch (err) {
console.warn('Failed to destroy window before update install', err);
}
}
setImmediate(() => {
try {
autoUpdater.quitAndInstall(false, true);
} catch (err) {
installInProgress = false;
updateState.status = 'error';
updateState.error = err && err.message ? err.message : String(err);
broadcastUpdateState();
console.error('quitAndInstall failed', err);
}
});
setTimeout(() => {
if (installInProgress) {
console.warn('Update install is still waiting for app shutdown');
}
}, 15000);
}
function startPeriodicUpdateChecks() {
if (periodicUpdateTimer || !app.isPackaged) {
return;
}
periodicUpdateTimer = setInterval(() => {
void runUpdateCheck('periodic');
}, PERIODIC_UPDATE_CHECK_INTERVAL_MS);
}
function createWindow() { function createWindow() {
const sess = session.fromPartition('persist:chattz'); const sess = session.fromPartition('persist:chattz');
const backendUrl = resolveBackendUrl(); const backendUrl = resolveBackendUrl();
@ -267,7 +209,6 @@ app.whenReady().then(() => {
}); });
ipcMain.on('download-update', () => { ipcMain.on('download-update', () => {
if (installInProgress) return;
updateState.status = 'downloading'; updateState.status = 'downloading';
updateState.error = null; updateState.error = null;
broadcastUpdateState(); broadcastUpdateState();
@ -275,14 +216,13 @@ app.whenReady().then(() => {
}); });
ipcMain.on('quit-and-install', () => { ipcMain.on('quit-and-install', () => {
installDownloadedUpdate(); autoUpdater.quitAndInstall();
}); });
// Check once on startup // Check once on startup
setTimeout(() => { setTimeout(() => {
void runUpdateCheck('startup'); void runUpdateCheck('startup');
}, 5000); }, 5000);
startPeriodicUpdateChecks();
app.on('activate', () => { app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) { if (BrowserWindow.getAllWindows().length === 0) {
@ -297,13 +237,6 @@ app.on('window-all-closed', () => {
} }
}); });
app.on('before-quit', () => {
if (periodicUpdateTimer) {
clearInterval(periodicUpdateTimer);
periodicUpdateTimer = null;
}
});
function safeOrigin(value) { function safeOrigin(value) {
try { try {
return new URL(value).origin; return new URL(value).origin;

View file

@ -29,7 +29,6 @@ const state = {
viewMode: 'chat', // 'chat' or 'video' viewMode: 'chat', // 'chat' or 'video'
iceServers: [{ urls: "stun:stun.l.google.com:19302" }], iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
peerGainNodes: new Map(), // userId -> GainNode peerGainNodes: new Map(), // userId -> GainNode
peerAudioNodes: new Map(), // userId -> { sourceNode, compressorNode, gainNode }
visibleVolumeSliders: new Set(), // userIds whose sliders are visible visibleVolumeSliders: new Set(), // userIds whose sliders are visible
}, },
voicePresencePollId: null, voicePresencePollId: null,
@ -1040,24 +1039,7 @@ function shouldInitiateOffer(peerId) {
return state.me.id > peerId; return state.me.id > peerId;
} }
function disconnectPeerAudioNodes(peerId) {
const nodes = state.voice.peerAudioNodes.get(peerId);
if (!nodes) return;
for (const node of [nodes.sourceNode, nodes.compressorNode, nodes.gainNode]) {
try {
node?.disconnect();
} catch { }
}
state.voice.peerAudioNodes.delete(peerId);
state.voice.peerGainNodes.delete(peerId);
}
function stopAndClearAudioPipeline() { function stopAndClearAudioPipeline() {
for (const peerId of state.voice.peerAudioNodes.keys()) {
disconnectPeerAudioNodes(peerId);
}
if (state.voice.deepFilterProcessor) { if (state.voice.deepFilterProcessor) {
state.voice.deepFilterProcessor.destroy(); state.voice.deepFilterProcessor.destroy();
state.voice.deepFilterProcessor = null; state.voice.deepFilterProcessor = null;
@ -1283,7 +1265,7 @@ async function createLocalVoiceStream() {
sampleRate: 48000, sampleRate: 48000,
echoCancellation: true, echoCancellation: true,
noiseSuppression: false, noiseSuppression: false,
autoGainControl: true, autoGainControl: false,
}, },
video: false, video: false,
}; };
@ -1406,27 +1388,16 @@ function ensurePeerConnection(peerId) {
const ctx = state.voice.audioContext; const ctx = state.voice.audioContext;
if (ctx.state === 'suspended') ctx.resume(); if (ctx.state === 'suspended') ctx.resume();
disconnectPeerAudioNodes(peerId);
const sourceNode = ctx.createMediaStreamSource(audio.srcObject); const sourceNode = ctx.createMediaStreamSource(audio.srcObject);
const compressorNode = ctx.createDynamicsCompressor();
const gainNode = ctx.createGain(); const gainNode = ctx.createGain();
compressorNode.threshold.setValueAtTime(-24, ctx.currentTime);
compressorNode.knee.setValueAtTime(18, ctx.currentTime);
compressorNode.ratio.setValueAtTime(4, ctx.currentTime);
compressorNode.attack.setValueAtTime(0.003, ctx.currentTime);
compressorNode.release.setValueAtTime(0.25, ctx.currentTime);
const currentVolume = state.userVolumes.get(peerId) ?? 1.0; const currentVolume = state.userVolumes.get(peerId) ?? 1.0;
gainNode.gain.setValueAtTime(currentVolume, ctx.currentTime); gainNode.gain.setValueAtTime(currentVolume, ctx.currentTime);
sourceNode.connect(compressorNode); sourceNode.connect(gainNode);
compressorNode.connect(gainNode);
gainNode.connect(ctx.destination); gainNode.connect(ctx.destination);
state.voice.peerGainNodes.set(peerId, gainNode); state.voice.peerGainNodes.set(peerId, gainNode);
state.voice.peerAudioNodes.set(peerId, { sourceNode, compressorNode, gainNode });
// Mute the original element as we play through Web Audio destination // Mute the original element as we play through Web Audio destination
audio.volume = 0; audio.volume = 0;
@ -1611,7 +1582,6 @@ async function joinVoice() {
pc.close(); pc.close();
state.voice.peerConnections.delete(msg.user_id); state.voice.peerConnections.delete(msg.user_id);
} }
disconnectPeerAudioNodes(msg.user_id);
document.getElementById(`audio-${msg.user_id}`)?.remove(); document.getElementById(`audio-${msg.user_id}`)?.remove();
} else if (msg.type === "signal") { } else if (msg.type === "signal") {
await handleSignal(msg.from_user_id, msg.kind, msg.data); await handleSignal(msg.from_user_id, msg.kind, msg.data);