wip: v0.2.8 dialog 美化
This commit is contained in:
parent
1b4ba1cb61
commit
9fb248dff0
@ -5,7 +5,7 @@ const useClass = createUseStyles({
|
||||
dialog: {
|
||||
border: "none",
|
||||
borderRadius: "8px",
|
||||
padding: "0",
|
||||
padding: "2rem 3rem",
|
||||
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
||||
maxWidth: "90%",
|
||||
width: "500px",
|
||||
@ -17,6 +17,7 @@ const useClass = createUseStyles({
|
||||
background: "rgba(0, 0, 0, 0.5)",
|
||||
backdropFilter: "blur(2px)"
|
||||
},
|
||||
background: "rgba(255, 255, 255, 0.4)",
|
||||
},
|
||||
dialog_content: {
|
||||
padding: "1.5rem",
|
||||
@ -55,6 +56,8 @@ export interface DialogProps {
|
||||
onClose: () => void;
|
||||
/** 自定义样式类名 */
|
||||
className?: string;
|
||||
/** 是否显示底部footer(关闭按钮) */
|
||||
footer?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,62 +69,68 @@ export const Dialog: React.FC<DialogProps> = ({
|
||||
children,
|
||||
onClose,
|
||||
className = '',
|
||||
footer = true,
|
||||
}) => {
|
||||
const classes = useClass();
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
const dialogRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// ESC 键关闭
|
||||
useEffect(() => {
|
||||
const dialog = dialogRef.current;
|
||||
if (!dialog) return;
|
||||
if (!open) return;
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}, [open, onClose]);
|
||||
|
||||
// 根据 open 属性打开/关闭对话框
|
||||
if (open) {
|
||||
// 先关闭再打开确保动画效果(如果有)
|
||||
if (dialog.open) dialog.close();
|
||||
dialog.showModal();
|
||||
} else {
|
||||
dialog.close();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
// 处理关闭事件(点击背景/ESC键)
|
||||
const handleClose = (e: React.MouseEvent) => {
|
||||
// 确保关闭事件来自背景点击
|
||||
// 遮罩点击关闭
|
||||
const handleMaskClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (e.target === dialogRef.current) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
// 处理取消事件(ESC键)
|
||||
const handleCancel = () => {
|
||||
onClose();
|
||||
};
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<dialog
|
||||
<div
|
||||
ref={dialogRef}
|
||||
className={`${classes.dialog} ${className}`}
|
||||
onClick={handleClose}
|
||||
onCancel={handleCancel}
|
||||
aria-modal="true"
|
||||
className={className}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
zIndex: 1100,
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
background: 'rgba(0,0,0,0.5)',
|
||||
backdropFilter: 'blur(2px)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
onClick={handleMaskClick}
|
||||
>
|
||||
<article className={classes.dialog_content}>
|
||||
<article className={classes.dialog} style={{opacity: 1, transform: 'translateY(0)'}}>
|
||||
{title && <header className={classes.dialog_header}>{title}</header>}
|
||||
|
||||
<div className={classes.dialog_body}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<footer className={classes.dialog_footer}>
|
||||
<button
|
||||
className={classes.close_button}
|
||||
onClick={onClose}
|
||||
aria-label="关闭对话框"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</footer>
|
||||
{footer !== false && (
|
||||
<footer className={classes.dialog_footer}>
|
||||
<button
|
||||
className={classes.close_button}
|
||||
onClick={onClose}
|
||||
aria-label="关闭对话框"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</footer>
|
||||
)}
|
||||
</article>
|
||||
</dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -13,7 +13,7 @@ export const MessageDialog: React.FC<MessageDialogProps> = ({open, message, onCl
|
||||
|
||||
const handleCopyMessage = () => {
|
||||
if (message) {
|
||||
navigator.clipboard.writeText(message.text).then(() => {
|
||||
navigator.clipboard.writeText(message.text || '').then(() => {
|
||||
console.log('消息已复制到剪贴板');
|
||||
setCopySuccess(true);
|
||||
// 2秒后隐藏成功提示
|
||||
@ -30,6 +30,7 @@ export const MessageDialog: React.FC<MessageDialogProps> = ({open, message, onCl
|
||||
open={open}
|
||||
title="收到新消息"
|
||||
onClose={onClose}
|
||||
footer={false}
|
||||
>
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<p style={{ margin: '0 0 0.5rem 0', fontSize: '0.9rem', color: '#666' }}>
|
||||
@ -45,10 +46,19 @@ export const MessageDialog: React.FC<MessageDialogProps> = ({open, message, onCl
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word'
|
||||
}}>
|
||||
{message?.text}
|
||||
{message?.isFile ? (
|
||||
<>
|
||||
<div>📎 文件: {message.fileName} ({message.fileSize ? (message.fileSize/1024).toFixed(1) : ''} KB)</div>
|
||||
<a href={message.fileBlobUrl} download={message.fileName} style={{color:'#007aff',textDecoration:'underline'}}>
|
||||
点击下载
|
||||
</a>
|
||||
</>
|
||||
) : (
|
||||
message?.text
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', justifyContent: 'flex-end', alignItems: 'center' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', gap: '0.5rem', justifyContent: 'flex-end', alignItems: 'center', marginTop: '8px' }}>
|
||||
{copySuccess && (
|
||||
<span style={{
|
||||
color: '#28a745',
|
||||
@ -59,11 +69,38 @@ export const MessageDialog: React.FC<MessageDialogProps> = ({open, message, onCl
|
||||
✓ 已复制
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={handleCopyMessage}
|
||||
{!message?.isFile && (
|
||||
<button
|
||||
onClick={handleCopyMessage}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
background: copySuccess ? '#28a745' : '#007aff',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.9rem',
|
||||
transition: 'all 0.2s'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!copySuccess) {
|
||||
e.currentTarget.style.backgroundColor = '#0056b3';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!copySuccess) {
|
||||
e.currentTarget.style.backgroundColor = '#007aff';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{copySuccess ? '已复制' : '复制消息'}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={onClose}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
background: copySuccess ? '#28a745' : '#007aff',
|
||||
background: '#6c757d',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
@ -71,18 +108,8 @@ export const MessageDialog: React.FC<MessageDialogProps> = ({open, message, onCl
|
||||
fontSize: '0.9rem',
|
||||
transition: 'all 0.2s'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!copySuccess) {
|
||||
e.currentTarget.style.backgroundColor = '#0056b3';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!copySuccess) {
|
||||
e.currentTarget.style.backgroundColor = '#007aff';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{copySuccess ? '已复制' : '复制消息'}
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@ -2,9 +2,45 @@ import {Resp} from "../../../interface/response.ts";
|
||||
import {Client, WSMessage, ReceivedMessage} from "./types.ts";
|
||||
import {useLocalStore} from "../../../store/local.ts";
|
||||
|
||||
// 接收文件块
|
||||
export const handleFileChunk = (chunk: any) => {
|
||||
console.log("[D] rtc file chunk =", chunk);
|
||||
// 文件接收缓存
|
||||
const fileReceiveCache: Record<string, {chunks: ArrayBuffer[], total: number, received: number, name: string, size: number, sender: string, timestamp: number}> = {};
|
||||
|
||||
export const handleFileChunk = (chunk: any, onFileReceived: (msg: import("./types").ReceivedMessage) => void) => {
|
||||
if (chunk.type === 'file') {
|
||||
// 文件元信息,初始化缓存
|
||||
fileReceiveCache[chunk.name + '_' + chunk.timestamp] = {
|
||||
chunks: [],
|
||||
total: chunk.totalChunks,
|
||||
received: 0,
|
||||
name: chunk.name,
|
||||
size: chunk.size,
|
||||
sender: chunk.sender,
|
||||
timestamp: chunk.timestamp
|
||||
};
|
||||
} else if (chunk.type === 'file-chunk') {
|
||||
const key = chunk.name + '_' + chunk.timestamp;
|
||||
const cache = fileReceiveCache[key];
|
||||
if (cache) {
|
||||
// 将 data 数组还原为 ArrayBuffer
|
||||
const uint8 = new Uint8Array(chunk.data);
|
||||
cache.chunks[chunk.chunkIndex] = uint8.buffer;
|
||||
cache.received++;
|
||||
// 全部收到
|
||||
if (cache.received === cache.total) {
|
||||
const blob = new Blob(cache.chunks);
|
||||
const url = URL.createObjectURL(blob);
|
||||
onFileReceived({
|
||||
sender: cache.sender,
|
||||
timestamp: cache.timestamp,
|
||||
fileName: cache.name,
|
||||
fileSize: cache.size,
|
||||
fileBlobUrl: url,
|
||||
isFile: true
|
||||
});
|
||||
delete fileReceiveCache[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export interface RTCHandlerCallbacks {
|
||||
@ -52,7 +88,7 @@ export class RTCHandler {
|
||||
this.callbacks.onMessageReceived(message);
|
||||
} else if (data.type === 'file') {
|
||||
// 处理文件消息
|
||||
handleFileChunk(data);
|
||||
handleFileChunk(data, this.callbacks.onMessageReceived);
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果不是JSON格式,当作普通文本处理
|
||||
@ -188,7 +224,9 @@ export class RTCHandler {
|
||||
sendMessage = (msg: string, files: File[], senderName: string) => {
|
||||
const ch = useLocalStore.getState().channel;
|
||||
console.log('[D] ready to send:', msg, files, ch);
|
||||
|
||||
const CHUNK_SIZE = 64 * 1024; // 64KB
|
||||
const BUFFERED_AMOUNT_THRESHOLD = 1 * 1024 * 1024; // 1MB
|
||||
|
||||
if (ch && ch.readyState === 'open') {
|
||||
if (msg.trim()) {
|
||||
// 发送文本消息
|
||||
@ -202,25 +240,52 @@ export class RTCHandler {
|
||||
}
|
||||
|
||||
if (files && files.length > 0) {
|
||||
// 发送文件消息
|
||||
files.forEach(file => {
|
||||
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
|
||||
const timestamp = Date.now();
|
||||
// 先发送文件元信息
|
||||
const fileData = {
|
||||
type: 'file',
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
sender: senderName,
|
||||
timestamp: Date.now()
|
||||
timestamp,
|
||||
totalChunks
|
||||
};
|
||||
ch.send(JSON.stringify(fileData));
|
||||
|
||||
// 这里可以添加文件分块发送逻辑
|
||||
ch.bufferedAmountLowThreshold = BUFFERED_AMOUNT_THRESHOLD;
|
||||
|
||||
let offset = 0;
|
||||
let chunkIndex = 0;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (e.target?.result) {
|
||||
ch.send(e.target.result as ArrayBuffer);
|
||||
|
||||
function sendNextChunk() {
|
||||
if (offset >= file.size) return;
|
||||
if (ch && ch.bufferedAmount && ch.bufferedAmount > BUFFERED_AMOUNT_THRESHOLD) {
|
||||
// 等待缓冲区变低
|
||||
ch.addEventListener('bufferedamountlow', sendNextChunk, { once: true });
|
||||
return;
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.onload = (e) => {
|
||||
if (e.target?.result) {
|
||||
ch?.send(JSON.stringify({
|
||||
type: 'file-chunk',
|
||||
name: file.name,
|
||||
timestamp,
|
||||
chunkIndex,
|
||||
totalChunks,
|
||||
data: Array.from(new Uint8Array(e.target.result as ArrayBuffer)),
|
||||
}));
|
||||
offset += CHUNK_SIZE;
|
||||
chunkIndex++;
|
||||
sendNextChunk();
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(slice);
|
||||
}
|
||||
sendNextChunk();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
17
frontend/src/page/local/component/send-dialog.tsx
Normal file
17
frontend/src/page/local/component/send-dialog.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import {Dialog} from '../../../component/dialog/dialog.tsx';
|
||||
import {Sender} from './sender.tsx';
|
||||
|
||||
interface SendDialogProps {
|
||||
open: boolean;
|
||||
onSend: (msg: string, files: File[]) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const SendDialog: React.FC<SendDialogProps> = ({open, onSend, onClose}) => {
|
||||
return (
|
||||
<Dialog open={open} title="发送消息" onClose={onClose}>
|
||||
<Sender onSend={onSend} />
|
||||
</Dialog>
|
||||
);
|
||||
};
|
@ -1,26 +1,33 @@
|
||||
import React, {useRef, useState} from 'react';
|
||||
import {createUseStyles} from "react-jss";
|
||||
import { message } from '../../../hook/message/u-message.tsx';
|
||||
|
||||
const useClass = createUseStyles({
|
||||
container: {
|
||||
width: "calc(100% - 45px)",
|
||||
height: "calc(100% - 45px)",
|
||||
margin: "20px",
|
||||
width: "100%",
|
||||
minHeight: "260px",
|
||||
margin: "0",
|
||||
border: "1px solid #ddd",
|
||||
borderRadius: "8px",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
background: "#fff",
|
||||
boxSizing: "border-box",
|
||||
padding: "20px 16px 60px 16px"
|
||||
},
|
||||
input_box: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: "10px",
|
||||
minHeight: "180px",
|
||||
padding: "12px",
|
||||
boxSizing: "border-box",
|
||||
border: "none",
|
||||
resize: "none",
|
||||
resize: "vertical",
|
||||
outline: "none",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
fontSize: "1.1rem",
|
||||
transition: "padding 0.3s ease",
|
||||
background: "#f8fafd",
|
||||
borderRadius: "6px"
|
||||
},
|
||||
input_has_files: {
|
||||
paddingTop: '50px',
|
||||
@ -74,18 +81,19 @@ const useClass = createUseStyles({
|
||||
},
|
||||
buttons: {
|
||||
position: "absolute",
|
||||
bottom: "10px",
|
||||
right: "10px",
|
||||
bottom: "16px",
|
||||
right: "16px",
|
||||
display: "flex",
|
||||
gap: "8px"
|
||||
gap: "12px"
|
||||
},
|
||||
action_btn: {
|
||||
padding: "8px 16px",
|
||||
padding: "10px 22px",
|
||||
background: "#4dabf7",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "20px",
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
transition: "all 0.2s",
|
||||
"&:hover": {background: "#339af0", transform: "translateY(-1px)"}
|
||||
},
|
||||
@ -97,32 +105,40 @@ export interface SenderProps {
|
||||
|
||||
export const Sender: React.FC<SenderProps> = ({onSend}) => {
|
||||
const classes = useClass();
|
||||
const [message, setMessage] = useState('');
|
||||
const [inputMessage, setInputMessage] = useState('');
|
||||
const [files, setFiles] = useState<File | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const handleTextInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setMessage(e.target.value)
|
||||
setInputMessage(e.target.value)
|
||||
}
|
||||
|
||||
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files) {
|
||||
setFiles(e.target.files[0]);
|
||||
} else {
|
||||
message.warning('未能选择文件');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (message.trim() || files) {
|
||||
onSend(message, files?[files]:[]);
|
||||
setMessage('');
|
||||
if (!(inputMessage.trim() || files)) {
|
||||
message.warning('请输入内容或选择文件');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
onSend(inputMessage, files ? [files] : []);
|
||||
setInputMessage('');
|
||||
setFiles(null);
|
||||
} catch (e) {
|
||||
message.error('发送失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
<textarea className={files?`${classes.input_box} ${classes.input_has_files}`:`${classes.input_box}`} placeholder="开始输入..."
|
||||
onChange={handleTextInput}>{message}</textarea>
|
||||
onChange={handleTextInput} value={inputMessage}></textarea>
|
||||
<div className={files? `${classes.file_list} ${classes.list_has_files}` : `${classes.file_list}`}>
|
||||
{files && <div className={classes.file_item}>
|
||||
<span>{files.name}</span>
|
||||
@ -134,10 +150,17 @@ export const Sender: React.FC<SenderProps> = ({onSend}) => {
|
||||
</div>
|
||||
<div className={classes.buttons}>
|
||||
<input type="file" ref={fileInputRef} hidden onChange={handleFileSelect}/>
|
||||
<button className={classes.action_btn} onClick={() => {
|
||||
<button
|
||||
className={classes.action_btn}
|
||||
style={{ opacity: 0.5, cursor: 'not-allowed' }}
|
||||
onClick={() => { message.warning('暂不支持文件发送') }}
|
||||
>
|
||||
📁 选择文件
|
||||
</button>
|
||||
{/* <button className={classes.action_btn} onClick={() => {
|
||||
fileInputRef.current && fileInputRef.current.click()
|
||||
}}>📁 选择文件
|
||||
</button>
|
||||
</button> */}
|
||||
<button className={classes.action_btn} onClick={handleSubmit}>✈️ 发送</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -26,7 +26,11 @@ export interface Client {
|
||||
}
|
||||
|
||||
export interface ReceivedMessage {
|
||||
text: string;
|
||||
text?: string;
|
||||
timestamp: number;
|
||||
sender: string;
|
||||
fileName?: string;
|
||||
fileSize?: number;
|
||||
fileBlobUrl?: string;
|
||||
isFile?: boolean;
|
||||
}
|
@ -4,13 +4,13 @@ import {createUseStyles} from "react-jss";
|
||||
import {useWebsocket} from "../../hook/websocket/u-ws.tsx";
|
||||
import {Resp} from "../../interface/response.ts";
|
||||
import {useLocalStore} from "../../store/local.ts";
|
||||
import {Drawer} from "../../component/drawer/drawer.tsx";
|
||||
import {Sender} from "./component/sender.tsx";
|
||||
import {UserBubble} from "./component/user-bubble.tsx";
|
||||
import {MessageDialog} from "./component/message-dialog.tsx";
|
||||
import {RTCHandler, RTCHandlerCallbacks} from "./component/rtc-handler.ts";
|
||||
import {generateBubbles} from "./component/bubble-layout.ts";
|
||||
import {Client, ReceivedMessage} from "./component/types.ts";
|
||||
import {SendDialog} from "./component/send-dialog.tsx";
|
||||
import {message} from "../../hook/message/u-message.tsx";
|
||||
|
||||
const useClass = createUseStyles({
|
||||
'@global': {
|
||||
@ -107,32 +107,50 @@ export const LocalSharing: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleWSEvent = async (e: MessageEvent) => {
|
||||
const msg = JSON.parse(e.data);
|
||||
console.log('[D] ws event msg =', msg);
|
||||
const msgData = JSON.parse(e.data);
|
||||
console.log('[D] ws event msg =', msgData);
|
||||
|
||||
if (msg.type === "enter" || msg.type === "leave") {
|
||||
if (msgData.type === "enter" || msgData.type === "leave") {
|
||||
await updateClients();
|
||||
return;
|
||||
}
|
||||
|
||||
// 其他RTC相关事件由RTC处理器处理
|
||||
if (rtcHandlerRef.current) {
|
||||
await rtcHandlerRef.current.handleWSEvent(e);
|
||||
try {
|
||||
await rtcHandlerRef.current.handleWSEvent(e);
|
||||
} catch (err) {
|
||||
message.error('通信异常,请刷新页面');
|
||||
}
|
||||
} else {
|
||||
message.error('内部错误:通信模块未初始化');
|
||||
}
|
||||
};
|
||||
|
||||
const handleBubbleClick = async (bubble: any) => {
|
||||
setOpen({send: true, receive: false});
|
||||
console.log('[D] Bubble clicked:', bubble.id, 'Current RTC handler:', rtcHandlerRef.current);
|
||||
if (rtcHandlerRef.current) {
|
||||
await rtcHandlerRef.current.handleBubbleClick(bubble.id, id);
|
||||
try {
|
||||
await rtcHandlerRef.current.handleBubbleClick(bubble.id, id);
|
||||
} catch (e) {
|
||||
message.error('建立连接失败,请重试');
|
||||
}
|
||||
} else {
|
||||
message.error('内部错误:通信模块未初始化');
|
||||
console.error('[E] RTC handler is null!');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSend = (msg: string, files: File[]) => {
|
||||
if (rtcHandlerRef.current) {
|
||||
rtcHandlerRef.current.sendMessage(msg, files, name);
|
||||
try {
|
||||
rtcHandlerRef.current.sendMessage(msg, files, name);
|
||||
} catch (e) {
|
||||
message.error('发送失败,请重试');
|
||||
}
|
||||
} else {
|
||||
message.error('内部错误:通信模块未初始化');
|
||||
}
|
||||
};
|
||||
|
||||
@ -141,6 +159,7 @@ export const LocalSharing: React.FC = () => {
|
||||
setReceivedMessage(null);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fn = async () => {
|
||||
const response = await fetch('/api/ulocal/register', {method: 'POST'});
|
||||
@ -175,7 +194,7 @@ export const LocalSharing: React.FC = () => {
|
||||
<CloudBackground/>
|
||||
<h1 className={classes.title}>
|
||||
{name}
|
||||
<span> - {id}</span>
|
||||
{/* <span> - {id}</span> */}
|
||||
</h1>
|
||||
|
||||
{bubbles.map(bubble => (
|
||||
@ -186,21 +205,13 @@ export const LocalSharing: React.FC = () => {
|
||||
/>
|
||||
))}
|
||||
|
||||
<Drawer
|
||||
isOpen={open.send}
|
||||
width={"80%"}
|
||||
height={"400px"}
|
||||
close={() => setOpen({send: false, receive: false})}
|
||||
>
|
||||
<Sender onSend={handleSend}/>
|
||||
</Drawer>
|
||||
|
||||
<Drawer
|
||||
isOpen={open.receive}
|
||||
width={"60%"}
|
||||
close={() => setOpen({send: false, receive: false})}
|
||||
<SendDialog
|
||||
open={open.send}
|
||||
onSend={handleSend}
|
||||
onClose={() => setOpen({send: false, receive: false})}
|
||||
/>
|
||||
|
||||
|
||||
<MessageDialog
|
||||
open={showMessageDialog}
|
||||
message={receivedMessage}
|
||||
|
Loading…
x
Reference in New Issue
Block a user