启用动态加载,只有需要渲染时才导入,减小初始包体积
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import RichTextEditor from "@/components/Editor/RichTextEditor";
|
||||
import {
|
||||
ReadClipboardButton,
|
||||
WriteClipboardButton,
|
||||
@@ -14,6 +14,19 @@ import type { CustomFile, FileMeta } from "@/types/webrtc";
|
||||
import type { ProgressState } from "@/hooks/useWebRTCConnection";
|
||||
import type WebRTC_Initiator from "@/lib/webrtc_Initiator";
|
||||
|
||||
// Dynamically import the RichTextEditor
|
||||
const RichTextEditor = dynamic(
|
||||
() => import("@/components/Editor/RichTextEditor"),
|
||||
{
|
||||
ssr: false, // This component is client-side only
|
||||
loading: () => (
|
||||
<div className="p-4 border rounded-lg min-h-[200px] md:min-h-[400px] bg-gray-50 flex items-center justify-center">
|
||||
Loading Editor...
|
||||
</div>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
interface SendTabPanelProps {
|
||||
messages: Messages;
|
||||
shareRoomStatusText: string;
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Copy, Download, Check } from 'lucide-react';
|
||||
import { WriteClipboardButton } from '../common/clipboard_btn';
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Copy, Download, Check } from "lucide-react";
|
||||
import { WriteClipboardButton } from "../common/clipboard_btn";
|
||||
|
||||
import { getDictionary } from '@/lib/dictionary';
|
||||
import { useLocale } from '@/hooks/useLocale';
|
||||
import type { Messages } from '@/types/messages';
|
||||
import { getDictionary } from "@/lib/dictionary";
|
||||
import { useLocale } from "@/hooks/useLocale";
|
||||
import type { Messages } from "@/types/messages";
|
||||
interface ShareCardProps {
|
||||
RoomID: string;
|
||||
shareLink: string;
|
||||
}
|
||||
|
||||
const ShareCard: React.FC<ShareCardProps> = ({ RoomID,shareLink }) => {
|
||||
const QRCodeSVG = dynamic(
|
||||
() => import("qrcode.react").then((mod) => mod.QRCodeSVG),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="w-[128px] h-[128px] bg-gray-200 animate-pulse rounded-lg"></div>
|
||||
),
|
||||
}
|
||||
);
|
||||
const ShareCard: React.FC<ShareCardProps> = ({ RoomID, shareLink }) => {
|
||||
const locale = useLocale();
|
||||
const [messages, setMessages] = useState<Messages | null>(null);
|
||||
const qrRef = useRef<HTMLDivElement>(null);
|
||||
@@ -23,12 +31,12 @@ const ShareCard: React.FC<ShareCardProps> = ({ RoomID,shareLink }) => {
|
||||
if (!qrRef.current) return;
|
||||
|
||||
try {
|
||||
const svgElement = qrRef.current.querySelector('svg');
|
||||
const svgElement = qrRef.current.querySelector("svg");
|
||||
if (!svgElement) return;
|
||||
|
||||
const svgData = new XMLSerializer().serializeToString(svgElement);
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
const img = new Image();
|
||||
@@ -36,36 +44,38 @@ const ShareCard: React.FC<ShareCardProps> = ({ RoomID,shareLink }) => {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const pngFile = await new Promise<Blob>((resolve) => canvas.toBlob((blob) => resolve(blob!), 'image/png'));
|
||||
const pngFile = await new Promise<Blob>((resolve) =>
|
||||
canvas.toBlob((blob) => resolve(blob!), "image/png")
|
||||
);
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'image/png': pngFile
|
||||
})
|
||||
"image/png": pngFile,
|
||||
}),
|
||||
]);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
};
|
||||
img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
|
||||
img.src = "data:image/svg+xml;base64," + btoa(svgData);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy QR code: ', err);
|
||||
alert('Failed to copy QR code. Please try again.');
|
||||
console.error("Failed to copy QR code: ", err);
|
||||
alert("Failed to copy QR code. Please try again.");
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
getDictionary(locale)
|
||||
.then(dict => setMessages(dict))
|
||||
.catch(error => console.error('Failed to load messages:', error));
|
||||
.then((dict) => setMessages(dict))
|
||||
.catch((error) => console.error("Failed to load messages:", error));
|
||||
}, [locale]);
|
||||
|
||||
const downloadQRCode = () => {
|
||||
if (!qrRef.current) return;
|
||||
|
||||
const svgElement = qrRef.current.querySelector('svg');
|
||||
const svgElement = qrRef.current.querySelector("svg");
|
||||
if (!svgElement) return;
|
||||
|
||||
const svgData = new XMLSerializer().serializeToString(svgElement);
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
const img = new Image();
|
||||
@@ -73,40 +83,44 @@ const ShareCard: React.FC<ShareCardProps> = ({ RoomID,shareLink }) => {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const pngFile = canvas.toDataURL('image/png');
|
||||
const downloadLink = document.createElement('a');
|
||||
downloadLink.download = 'qrcode.png';
|
||||
const pngFile = canvas.toDataURL("image/png");
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.download = "qrcode.png";
|
||||
downloadLink.href = pngFile;
|
||||
downloadLink.click();
|
||||
};
|
||||
img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
|
||||
img.src = "data:image/svg+xml;base64," + btoa(svgData);
|
||||
};
|
||||
if (messages === null) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
return (
|
||||
<div className="bg-blue-100 p-4 rounded-md">
|
||||
<p className="text-blue-700 mb-4">
|
||||
{messages.text.RetrieveMethod.P}
|
||||
</p>
|
||||
|
||||
<p className="text-blue-700 mb-4">{messages.text.RetrieveMethod.P}</p>
|
||||
|
||||
{/* 使用 flex-col 替代 list,更好控制移动端布局 */}
|
||||
<div className="flex flex-col space-y-4">
|
||||
{/* RoomID 部分 */}
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span>{messages.text.RetrieveMethod.RoomId_tips+RoomID}</span>
|
||||
<WriteClipboardButton title={messages.text.RetrieveMethod.copyRoomId_tips} textToCopy={RoomID} />
|
||||
<span>{messages.text.RetrieveMethod.RoomId_tips + RoomID}</span>
|
||||
<WriteClipboardButton
|
||||
title={messages.text.RetrieveMethod.copyRoomId_tips}
|
||||
textToCopy={RoomID}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* URL 部分 */}
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div className="break-all">
|
||||
{messages.text.RetrieveMethod.url_tips+shareLink}
|
||||
{messages.text.RetrieveMethod.url_tips + shareLink}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<WriteClipboardButton title={messages.text.RetrieveMethod.copyUrl_tips} textToCopy={shareLink} />
|
||||
<WriteClipboardButton
|
||||
title={messages.text.RetrieveMethod.copyUrl_tips}
|
||||
textToCopy={shareLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -114,35 +128,45 @@ const ShareCard: React.FC<ShareCardProps> = ({ RoomID,shareLink }) => {
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div>{messages.text.RetrieveMethod.scanQR_tips}</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button onClick={copyToClipboard} variant="outline" className="w-full sm:w-auto">
|
||||
<Button
|
||||
onClick={copyToClipboard}
|
||||
variant="outline"
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
{isCopied ? (
|
||||
<>
|
||||
<Check className="w-4 h-4 mr-2" />
|
||||
{messages.text.RetrieveMethod.Copied_dis}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className="mr-2 h-4 w-4" /> {messages.text.RetrieveMethod.Copy_QR_dis}
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
<Copy className="mr-2 h-4 w-4" />{" "}
|
||||
{messages.text.RetrieveMethod.Copy_QR_dis}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button onClick={downloadQRCode} variant="outline" className="w-full sm:w-auto">
|
||||
<Download className="mr-2 h-4 w-4" /> {messages.text.RetrieveMethod.download_QR_dis}
|
||||
<Button
|
||||
onClick={downloadQRCode}
|
||||
variant="outline"
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
<Download className="mr-2 h-4 w-4" />{" "}
|
||||
{messages.text.RetrieveMethod.download_QR_dis}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* QR Code 显示区域 */}
|
||||
<div className="mt-4 flex justify-center">
|
||||
<div className="inline-block border-2 p-4 bg-white rounded-lg">
|
||||
<div ref={qrRef}>
|
||||
<QRCodeSVG value={shareLink} />
|
||||
{/* QR Code 显示区域 */}
|
||||
<div className="mt-4 flex justify-center">
|
||||
<div className="inline-block border-2 p-4 bg-white rounded-lg">
|
||||
<div ref={qrRef}>
|
||||
<QRCodeSVG value={shareLink} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShareCard;
|
||||
export default ShareCard;
|
||||
|
||||
Reference in New Issue
Block a user