feat: Implement voice and video calling features including participant tiles, screen sharing, and audio controls with associated UI components and sound assets.
All checks were successful
Build and Release / build-and-release (push) Successful in 13m4s
All checks were successful
Build and Release / build-and-release (push) Successful in 13m4s
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { useQuery, usePaginatedQuery, useMutation, useConvex } from 'convex/react';
|
||||
import { api } from '../../../../convex/_generated/api';
|
||||
@@ -30,6 +31,7 @@ import ColoredIcon from './ColoredIcon';
|
||||
import { usePlatform } from '../platform';
|
||||
import { useVoice } from '../contexts/VoiceContext';
|
||||
import { useSearch } from '../contexts/SearchContext';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { generateUniqueMessage } from '../utils/floodMessages';
|
||||
|
||||
const SCROLL_DEBUG = true;
|
||||
@@ -340,7 +342,7 @@ const Attachment = ({ metadata, onLoad, onImageClick }) => {
|
||||
if (error) return <div style={{ color: '#ed4245', fontSize: '12px' }}>{error}</div>;
|
||||
|
||||
if (metadata.mimeType.startsWith('image/')) {
|
||||
return <img src={url} alt={metadata.filename} draggable="false" style={{ maxHeight: '300px', borderRadius: '4px', cursor: 'zoom-in' }} onLoad={onLoad} onClick={() => onImageClick(url)} />;
|
||||
return <img src={url} alt={metadata.filename} draggable="false" style={{ maxWidth: '100%', maxHeight: '300px', borderRadius: '4px', cursor: 'zoom-in' }} onLoad={onLoad} onClick={() => onImageClick(url)} />;
|
||||
}
|
||||
if (metadata.mimeType.startsWith('video/')) {
|
||||
const handlePlayClick = () => { setShowControls(true); if (videoRef.current) videoRef.current.play(); };
|
||||
@@ -510,6 +512,7 @@ const InputContextMenu = ({ x, y, onClose, onPaste }) => {
|
||||
|
||||
const ChatArea = ({ channelId, channelName, channelType, username, channelKey, userId: currentUserId, showMembers, onToggleMembers, onOpenDM, showPinned, onTogglePinned, jumpToMessageId, onClearJumpToMessage }) => {
|
||||
const { crypto } = usePlatform();
|
||||
const isMobile = useIsMobile();
|
||||
const { isReceivingScreenShareAudio } = useVoice();
|
||||
const searchCtx = useSearch();
|
||||
const [decryptedMessages, setDecryptedMessages] = useState([]);
|
||||
@@ -2234,9 +2237,9 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
isHovered={hoveredMessageId === msg.id}
|
||||
editInput={editInput}
|
||||
username={username}
|
||||
onHover={() => setHoveredMessageId(msg.id)}
|
||||
onLeave={() => setHoveredMessageId(null)}
|
||||
onContextMenu={(e) => { e.preventDefault(); window.dispatchEvent(new Event('close-context-menus')); setContextMenu({ x: e.clientX, y: e.clientY, messageId: msg.id, isOwner, isAttachment: !!parseAttachment(msg.content), canDelete }); }}
|
||||
onHover={isMobile ? undefined : () => setHoveredMessageId(msg.id)}
|
||||
onLeave={isMobile ? undefined : () => setHoveredMessageId(null)}
|
||||
onContextMenu={isMobile ? undefined : (e) => { e.preventDefault(); window.dispatchEvent(new Event('close-context-menus')); setContextMenu({ x: e.clientX, y: e.clientY, messageId: msg.id, isOwner, isAttachment: !!parseAttachment(msg.content), canDelete }); }}
|
||||
onAddReaction={(emoji) => { if (emoji) { addReaction({ messageId: msg.id, userId: currentUserId, emoji }); } else { setReactionPickerMsgId(reactionPickerMsgId === msg.id ? null : msg.id); } }}
|
||||
onEdit={() => { setEditingMessage({ id: msg.id, content: msg.content }); setEditInput(msg.content); }}
|
||||
onReply={() => setReplyingTo({ messageId: msg.id, username: msg.username, content: msg.content?.substring(0, 100) })}
|
||||
@@ -2481,10 +2484,11 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{zoomedImage && (
|
||||
{zoomedImage && ReactDOM.createPortal(
|
||||
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.85)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'zoom-out' }} onClick={() => setZoomedImage(null)}>
|
||||
<img src={zoomedImage} alt="Zoomed" style={{ maxWidth: '90%', maxHeight: '90%', boxShadow: '0 8px 16px rgba(0,0,0,0.5)', borderRadius: '4px', cursor: 'default' }} onClick={(e) => e.stopPropagation()} />
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
)}
|
||||
{profilePopup && (
|
||||
<UserProfilePopup
|
||||
|
||||
Reference in New Issue
Block a user