feat(desktop): port v0.11.4-v0.15.2 from Tauri to Electron + Discord-parity audio (v0.16.0)
Chronological port of every Tauri release commit (v0.11.4 -> v0.15.2)
into the Electron rebuild, plus Discord-style audio handling that goes
beyond the Tauri original.
Highlights:
- All 17 Tauri release commits ported (audio fixes, custom notification
sound, Discord-style chat UX, profile banner, changelog page,
Discord-parity call UX, screen-share echo + re-watch UX, audio-loop
fix).
- Native napi-rs audio-loopback addon with WASAPI process-loopback:
* EXCLUDE_TARGET_PROCESS_TREE for full-screen shares -> peers
never hear themselves echoed back through the capture.
* INCLUDE_TARGET_PROCESS_TREE for window shares -> only the
picked window's audio is captured, not the whole OS mixer
(Discord parity).
* HWND -> PID resolution via Win32 GetWindowThreadProcessId.
- Discord-style screen-source picker (thumbnail grid, screens vs
apps tabs, live-refreshing thumbnails).
- Hash routing fix for packaged builds (file:// can't resolve
BrowserRouter paths).
- Tauri sources removed (apps/desktop/src-tauri).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { check as checkUpdate } from '@tauri-apps/plugin-updater';
|
||||
|
||||
import { isTauriRuntime } from './globalShortcut';
|
||||
|
||||
export interface UpdateState {
|
||||
@@ -20,38 +18,34 @@ export const IDLE_UPDATE_STATE: UpdateState = {
|
||||
error: null,
|
||||
};
|
||||
|
||||
type UpdateHandle = Awaited<ReturnType<typeof checkUpdate>>;
|
||||
|
||||
let cachedUpdate: UpdateHandle | null = null;
|
||||
// Module-local flag: main owns the real "pending update" state via
|
||||
// electron-updater, but the renderer also needs to refuse installUpdate()
|
||||
// calls that weren't preceded by a successful checkForUpdate().
|
||||
let hasPendingUpdate = false;
|
||||
|
||||
export async function checkForUpdate(): Promise<UpdateState> {
|
||||
if (!isTauriRuntime()) {
|
||||
return { ...IDLE_UPDATE_STATE };
|
||||
}
|
||||
try {
|
||||
const update = await checkUpdate();
|
||||
if (!update) {
|
||||
cachedUpdate = null;
|
||||
const result = await window.electronAPI.checkForUpdate();
|
||||
if (!result.available || !result.info) {
|
||||
hasPendingUpdate = false;
|
||||
return { ...IDLE_UPDATE_STATE };
|
||||
}
|
||||
cachedUpdate = update;
|
||||
hasPendingUpdate = true;
|
||||
return {
|
||||
available: true,
|
||||
version: update.version ?? null,
|
||||
notes: update.body ?? null,
|
||||
version: result.info.version ?? null,
|
||||
notes: result.info.releaseNotes ?? null,
|
||||
downloading: false,
|
||||
downloaded: false,
|
||||
error: null,
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
// Swallow "no release on GitHub yet" / network-unreachable cases silently.
|
||||
// The updater endpoint serves `latest.json` from GitHub releases; a fresh
|
||||
// repo or offline machine produces a generic "Could not fetch a valid
|
||||
// release JSON" error that has no actionable information for the user —
|
||||
// logging it on every launch just pollutes the console.
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const benign =
|
||||
/could not fetch a valid release json|network|timed? out|failed to fetch|connection/i.test(
|
||||
/could not fetch a valid release json|network|timed? out|failed to fetch|connection|enotfound|econnreset|etimedout|404/i.test(
|
||||
msg,
|
||||
);
|
||||
if (!benign) {
|
||||
@@ -64,25 +58,25 @@ export async function checkForUpdate(): Promise<UpdateState> {
|
||||
}
|
||||
}
|
||||
|
||||
// Downloads + installs the previously checked update. On Windows the app
|
||||
// quits during install (passive installer); on macOS/Linux tauri triggers
|
||||
// a relaunch automatically.
|
||||
// Downloads + installs the previously checked update. On Windows the
|
||||
// app quits during install (passive installer); electron-updater
|
||||
// triggers a relaunch on macOS/Linux.
|
||||
export async function installUpdate(
|
||||
onProgress?: (downloaded: number, total: number | null) => void,
|
||||
): Promise<void> {
|
||||
if (!cachedUpdate) {
|
||||
if (!hasPendingUpdate) {
|
||||
throw new Error('no pending update — call checkForUpdate() first');
|
||||
}
|
||||
let total: number | null = null;
|
||||
let downloaded = 0;
|
||||
await cachedUpdate.downloadAndInstall((event) => {
|
||||
if (event.event === 'Started') {
|
||||
total = event.data.contentLength ?? null;
|
||||
downloaded = 0;
|
||||
} else if (event.event === 'Progress') {
|
||||
downloaded += event.data.chunkLength;
|
||||
}
|
||||
onProgress?.(downloaded, total);
|
||||
});
|
||||
cachedUpdate = null;
|
||||
let unsub: (() => void) | null = null;
|
||||
if (onProgress) {
|
||||
unsub = window.electronAPI.onUpdaterProgress((p) => {
|
||||
onProgress(p.transferred, p.total || null);
|
||||
});
|
||||
}
|
||||
try {
|
||||
await window.electronAPI.downloadInstallUpdate();
|
||||
} finally {
|
||||
unsub?.();
|
||||
hasPendingUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user