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,8 +1,10 @@
|
||||
import type { ConversationSummary } from '@chat-app/shared/chat';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useCall } from '../context/CallContext';
|
||||
import { useFriendshipsContext } from '../context/FriendshipsContext';
|
||||
import { CallPreviewModal } from './CallPreviewModal';
|
||||
import { AvatarColorKey, colorKeyFor } from './CallParticipantTile';
|
||||
import { LockIcon, PhoneIcon, PhoneOffIcon, VideoIcon } from './icons';
|
||||
|
||||
@@ -24,6 +26,7 @@ export function IncomingCallPanel({ conversation }: Props) {
|
||||
const { t } = useTranslation(['app']);
|
||||
const { state, acceptIncoming, rejectIncoming } = useCall();
|
||||
const { friendships } = useFriendshipsContext();
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
|
||||
if (state.kind !== 'incoming' || state.conversationId !== conversation.id) return null;
|
||||
|
||||
@@ -56,13 +59,15 @@ export function IncomingCallPanel({ conversation }: Props) {
|
||||
{t('app:call.incoming_title')}
|
||||
</p>
|
||||
<div className="relative my-4 flex h-[112px] w-[112px] items-center justify-center">
|
||||
{/* Discord-style: animated pulse rings when motion is OK; one
|
||||
static ring + a faint outer glow when prefers-reduced-motion. */}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 rounded-full border-2 border-accent animate-pulse-ring"
|
||||
className="pointer-events-none absolute inset-0 rounded-full border-2 border-accent motion-safe:animate-pulse-ring motion-reduce:opacity-70"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 rounded-full border-2 border-accent animate-pulse-ring"
|
||||
className="pointer-events-none absolute inset-0 rounded-full border-2 border-accent motion-safe:animate-pulse-ring motion-reduce:hidden"
|
||||
style={{ animationDelay: '1s' }}
|
||||
/>
|
||||
{avatarUrl ? (
|
||||
@@ -123,7 +128,7 @@ export function IncomingCallPanel({ conversation }: Props) {
|
||||
{state.mediaKind === 'video' && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void acceptIncoming('video')}
|
||||
onClick={() => setShowPreview(true)}
|
||||
className="inline-flex flex-1 basis-[30%] cursor-pointer items-center justify-center gap-2 rounded-[14px] bg-accent px-4 py-3.5 text-sm font-semibold text-accent-fg shadow-accept-btn transition hover:brightness-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/50"
|
||||
>
|
||||
<VideoIcon className="h-4 w-4" />
|
||||
@@ -132,6 +137,17 @@ export function IncomingCallPanel({ conversation }: Props) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showPreview && (
|
||||
<CallPreviewModal
|
||||
title={title}
|
||||
onJoin={() => {
|
||||
setShowPreview(false);
|
||||
void acceptIncoming('video');
|
||||
}}
|
||||
onCancel={() => setShowPreview(false)}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user