chore:Remove the redundant safety save button; Use English comments

This commit is contained in:
david_bai
2025-09-14 23:25:16 +08:00
parent d0ba2eb9c4
commit 95331cb8e6
16 changed files with 32 additions and 106 deletions
-2
View File
@@ -55,7 +55,6 @@ const ClipboardApp = () => {
requestFolder,
setReceiverDirectoryHandle,
getReceiverSaveType,
manualSafeSave,
} = useWebRTCConnection({
messages,
putMessageInMs,
@@ -219,7 +218,6 @@ const ClipboardApp = () => {
getReceiverSaveType={getReceiverSaveType}
retrieveMessage={retrieveMessage}
handleLeaveRoom={handleLeaveReceiverRoom}
manualSafeSave={manualSafeSave}
/>
)}
</CardContent>
@@ -44,7 +44,6 @@ interface FileListDisplayProps {
onRequest?: (item: FileMeta) => void; // Request file
onDelete?: (item: FileMeta) => void;
onLocationPick?: () => Promise<boolean>;
onSafeSave?: () => void; // New prop for safe save functionality
saveType?: { [fileId: string]: boolean }; // File stored on disk or in memory
largeFileThreshold?: number;
}
@@ -66,7 +65,6 @@ const FileListDisplay: React.FC<FileListDisplayProps> = ({
onRequest,
onDelete,
onLocationPick,
onSafeSave,
saveType,
largeFileThreshold = 500 * 1024 * 1024, // 500MB default
}) => {
@@ -459,29 +457,6 @@ const FileListDisplay: React.FC<FileListDisplayProps> = ({
{messages.text.FileListDisplay.chooseSavePath_dis}
</Button>
)}
{/* Safe Save Button - only show when location is picked and files are saved to disk */}
{onSafeSave &&
pickedLocation &&
(isAnyFileTransferring ||
(saveType &&
Object.values(saveType).some(
(isSavedToDisk) => isSavedToDisk
))) && (
<Tooltip
content={messages.text.FileListDisplay.safeSave_tooltip}
>
<Button
onClick={() => {
onSafeSave();
}}
variant="outline"
size="sm"
className="mr-2 text-green-600 border-green-600 hover:bg-green-50"
>
{messages.text.FileListDisplay.safeSave_dis}
</Button>
</Tooltip>
)}
</div>
</div>
)}
@@ -30,7 +30,6 @@ interface RetrieveTabPanelProps {
directoryHandle: FileSystemDirectoryHandle
) => Promise<void>;
getReceiverSaveType: () => { [fileId: string]: boolean } | undefined;
manualSafeSave: () => void; // Add manual safe save function
retrieveMessage: string;
handleLeaveRoom: () => void;
}
@@ -47,7 +46,6 @@ export function RetrieveTabPanel({
requestFolder,
setReceiverDirectoryHandle,
getReceiverSaveType,
manualSafeSave,
retrieveMessage,
handleLeaveRoom,
}: RetrieveTabPanelProps) {
@@ -168,7 +166,6 @@ export function RetrieveTabPanel({
onDownload={handleDownloadFile}
onRequest={handleFileRequestFromPanel} // Use the panel's own handler
onLocationPick={onLocationPick} // Use the panel's own handler
onSafeSave={manualSafeSave} // Add safe save handler
saveType={getReceiverSaveType()}
/>
{retrieveMessage && (
+2 -6
View File
@@ -256,11 +256,6 @@ export const de: Messages = {
chooseSavePath_tips:
"Speichern Sie große Dateien oder Ordner direkt in einem ausgewählten Verzeichnis. 👉",
chooseSavePath_dis: "Speicherort auswählen",
safeSave_dis: "Sicheres Speichern",
safeSave_tooltip:
"Keine Angst vor Verbindungsunterbrechung, klicken Sie hier, um Dateien sicher zu speichern für die nächste Fortsetzung",
safeSaveSuccessMsg:
"Dateien wurden sicher auf der Festplatte gespeichert, sicher die Seite zu schließen, unterstützt Wiederaufnahme der Übertragung!",
},
RetrieveMethod: {
P: "Glückwunsch 🎉 Freigegebene Inhalte warten darauf, abgerufen zu werden:",
@@ -316,7 +311,8 @@ export const de: Messages = {
noFilesForFolderMsg: "Keine Dateien im Ordner '{folderName}' gefunden.",
zipError: "Fehler beim Erstellen der ZIP-Datei.",
fileNotFoundMsg: "Datei '{fileName}' zum Herunterladen nicht gefunden.",
confirmLeaveWhileTransferring: "Dateien werden derzeit übertragen. Das Verlassen wird die Übertragung unterbrechen. Sind Sie sicher?",
confirmLeaveWhileTransferring:
"Dateien werden derzeit übertragen. Das Verlassen wird die Übertragung unterbrechen. Sind Sie sicher?",
leaveWhileTransferringSuccess: "Raum verlassen, Übertragung unterbrochen",
html: {
senderTab: "Senden",
-5
View File
@@ -253,11 +253,6 @@ export const en: Messages = {
chooseSavePath_tips:
"Save large files or folders directly to a selected directory. 👉",
chooseSavePath_dis: "Choose save location",
safeSave_dis: "Safe Save",
safeSave_tooltip:
"Don't worry about connection interruption, click here to safely save files for next resume",
safeSaveSuccessMsg:
"Files have been safely saved to disk, safe to close page, supports resume transfer!",
},
RetrieveMethod: {
P: "Congrats 🎉 Share content is waiting to be retrieved:",
+4 -7
View File
@@ -254,11 +254,6 @@ export const es: Messages = {
chooseSavePath_tips:
"Guarda archivos grandes o carpetas directamente en un directorio seleccionado. 👉",
chooseSavePath_dis: "Elegir ubicación de guardado",
safeSave_dis: "Guardar Seguro",
safeSave_tooltip:
"No te preocupes por la interrupción de la conexión, haz clic aquí para guardar archivos de forma segura para la próxima reanudación",
safeSaveSuccessMsg:
"Los archivos se han guardado de forma segura en el disco, es seguro cerrar la página, ¡admite transferencia de reanudación!",
},
RetrieveMethod: {
P: "¡Felicitaciones 🎉 El contenido compartido está esperando ser recuperado:",
@@ -310,8 +305,10 @@ export const es: Messages = {
"No se encontraron archivos en la carpeta '{folderName}'.",
zipError: "Error al crear el archivo ZIP.",
fileNotFoundMsg: "Archivo '{fileName}' no encontrado para descargar.",
confirmLeaveWhileTransferring: "Los archivos se están transfiriendo actualmente. Salir interrumpirá la transferencia. ¿Estás seguro?",
leaveWhileTransferringSuccess: "Saliste de la sala, transferencia interrumpida",
confirmLeaveWhileTransferring:
"Los archivos se están transfiriendo actualmente. Salir interrumpirá la transferencia. ¿Estás seguro?",
leaveWhileTransferringSuccess:
"Saliste de la sala, transferencia interrumpida",
html: {
senderTab: "Enviar",
retrieveTab: "Recuperar",
+2 -6
View File
@@ -257,11 +257,6 @@ export const fr: Messages = {
chooseSavePath_tips:
"Enregistrez des fichiers volumineux ou des dossiers directement dans un répertoire sélectionné. 👉",
chooseSavePath_dis: "Choisir l'emplacement de sauvegarde",
safeSave_dis: "Sauvegarde Sécurisée",
safeSave_tooltip:
"N'ayez pas peur de l'interruption de connexion, cliquez ici pour sauvegarder les fichiers en toute sécurité pour la prochaine reprise",
safeSaveSuccessMsg:
"Les fichiers ont été sauvegardés en toute sécurité sur le disque, sûr de fermer la page, prend en charge la reprise du transfert !",
},
RetrieveMethod: {
P: "Félicitations 🎉 Le contenu partagé attend d'être récupéré :",
@@ -318,7 +313,8 @@ export const fr: Messages = {
zipError: "Erreur lors de la création du fichier ZIP.",
fileNotFoundMsg:
"Fichier '{fileName}' introuvable pour le téléchargement.",
confirmLeaveWhileTransferring: "Des fichiers sont actuellement en cours de transfert. Quitter interrompra le transfert. Êtes-vous sûr?",
confirmLeaveWhileTransferring:
"Des fichiers sont actuellement en cours de transfert. Quitter interrompra le transfert. Êtes-vous sûr?",
leaveWhileTransferringSuccess: "Salle quittée, transfert interrompu",
html: {
senderTab: "Envoyer",
-3
View File
@@ -249,9 +249,6 @@ export const ja: Messages = {
chooseSavePath_tips:
"大きなファイルやフォルダを選択したディレクトリに直接保存します。👉",
chooseSavePath_dis: "保存場所を選択",
safeSave_dis: "安全保存",
safeSave_tooltip: "接続の中断を恐れる必要はありません。ここをクリックして、次回の再開のためにファイルを安全に保存してください",
safeSaveSuccessMsg: "ファイルが安全にディスクに保存されました。ページを安全に閉じることができ、転送の再開をサポートします!",
},
RetrieveMethod: {
P: "おめでとう 🎉 共有コンテンツが取得待ちです:",
-3
View File
@@ -247,9 +247,6 @@ export const ko: Messages = {
chooseSavePath_tips:
"큰 파일이나 폴더를 선택한 디렉터리에 직접 저장합니다. 👉",
chooseSavePath_dis: "저장 위치 선택",
safeSave_dis: "안전 저장",
safeSave_tooltip: "연결 중단을 두려워하지 마세요. 다음 재개를 위해 파일을 안전하게 저장하려면 여기를 클릭하세요",
safeSaveSuccessMsg: "파일이 디스크에 안전하게 저장되었습니다. 페이지를 안전하게 닫을 수 있으며 전송 재개를 지원합니다!",
},
RetrieveMethod: {
P: "축하합니다 🎉 공유된 콘텐츠가 검색을 기다리고 있습니다:",
-4
View File
@@ -235,10 +235,6 @@ export const zh: Messages = {
"我们建议选择一个保存目录来直接将文件保存到磁盘。这样可以更方便地传输大文件和同步文件夹。",
chooseSavePath_tips: "大文件或文件夹可直接保存到指定目录 👉",
chooseSavePath_dis: "选择保存位置",
safeSave_dis: "安全保存",
safeSave_tooltip: "连接中断不要怕,点击这里安全保存文件,方便下次续传",
safeSaveSuccessMsg:
"文件已安全保存到磁盘,可以安全关闭页面,支持断点续传!",
},
RetrieveMethod: {
P: "恭喜 🎉 共享内容等待接收:",
+1 -3
View File
@@ -18,8 +18,7 @@ interface UseWebRTCConnectionProps {
}
export function useWebRTCConnection({
messages,
putMessageInMs,
// Retaining interface compatibility but these are no longer used
}: UseWebRTCConnectionProps) {
// Get state from store
const {
@@ -64,7 +63,6 @@ export function useWebRTCConnection({
setReceiverDirectoryHandle:
webrtcService.setReceiverDirectoryHandle.bind(webrtcService),
getReceiverSaveType: webrtcService.getReceiverSaveType.bind(webrtcService),
manualSafeSave: webrtcService.manualSafeSave.bind(webrtcService),
// Reset connection methods
resetSenderConnection: () => webrtcService.leaveRoom(true),
+5 -7
View File
@@ -2,8 +2,6 @@ import { EmbeddedChunkMeta } from "@/types/webrtc";
import { ReceptionConfig } from "./ReceptionConfig";
import { postLogToBackend } from "@/app/config/api";
const developmentEnv = process.env.NODE_ENV;
/**
* 🚀 Chunk processing result interface
*/
@@ -148,7 +146,7 @@ export class ChunkProcessor {
const startChunkIndex = ReceptionConfig.getChunkIndexFromOffset(initialOffset); // Resume start index
const relativeChunkIndex = absoluteChunkIndex - startChunkIndex; // Relative index in chunks array
// 🎯 简化调试:只在边界chunk时记录索引映射
// 🎯 Simplify debugging: Only record index mapping when boundary chunk
if (ReceptionConfig.DEBUG_CONFIG.ENABLE_CHUNK_LOGGING && (absoluteChunkIndex <= 2 || relativeChunkIndex <= 2)) {
postLogToBackend(
`[INDEX-MAP] abs:${absoluteChunkIndex}, start:${startChunkIndex}, rel:${relativeChunkIndex}`
@@ -196,7 +194,7 @@ export class ChunkProcessor {
const startChunkIndex = ReceptionConfig.getChunkIndexFromOffset(initialOffset);
const calculatedExpected = chunkMeta.totalChunks - startChunkIndex;
// 🎯 简化日志:只在数量不匹配时记录关键信息
// 🎯 Simplify logging: Only record critical information when the number does not match
if (chunkMeta.totalChunks !== expectedChunksCount && calculatedExpected !== expectedChunksCount) {
if (ReceptionConfig.DEBUG_CONFIG.ENABLE_CHUNK_LOGGING) {
postLogToBackend(
@@ -233,12 +231,12 @@ export class ChunkProcessor {
return;
}
// 🎯 简化日志:只记录边界chunk和异常情况
// 🎯 Simplify logging: Only record boundary chunk and abnormal cases
const { chunkMeta, absoluteChunkIndex, relativeChunkIndex } = result;
const isFirstFew = absoluteChunkIndex <= 3;
const isLastFew = relativeChunkIndex >= expectedChunksCount - 3;
// 🔧 修复:SequencedWriter期望的是绝对索引,不是相对索引
// 🔧 Fix: SequencedWriter expects absolute index, not relative index
const hasIndexMismatch = writerExpectedIndex !== undefined && absoluteChunkIndex !== writerExpectedIndex;
if (isFirstFew || isLastFew || hasIndexMismatch) {
@@ -311,7 +309,7 @@ export class ChunkProcessor {
const { sequencedCount, expectedChunksCount, currentTotalSize, expectedSize, isDataComplete } = stats;
// 🎯 关键日志3:只在完成时打印最终检查结果
// 🎯 Critical log 3: Only print final check results when complete
if (isDataComplete) {
const startChunkIndex = ReceptionConfig.getChunkIndexFromOffset(initialOffset);
const missingChunks = [];
@@ -2,10 +2,9 @@ import WebRTC_Recipient from "../webrtc_Recipient";
import { CustomFile, fileMetadata } from "@/types/webrtc";
import { ReceptionStateManager } from "./ReceptionStateManager";
import { MessageProcessor, MessageProcessorDelegate } from "./MessageProcessor";
import { ChunkProcessor, ChunkProcessingResult } from "./ChunkProcessor";
import { ChunkProcessor } from "./ChunkProcessor";
import {
StreamingFileWriter,
SequencedDiskWriter,
StreamingFileWriter
} from "./StreamingFileWriter";
import { FileAssembler } from "./FileAssembler";
import { ProgressReporter, ProgressCallback } from "./ProgressReporter";
@@ -13,8 +12,6 @@ import { ReceptionConfig } from "./ReceptionConfig";
import { ChunkRangeCalculator } from "@/lib/utils/ChunkRangeCalculator";
import { postLogToBackend } from "@/app/config/api";
const developmentEnv = process.env.NODE_ENV;
/**
* 🚀 File receive orchestrator
* Main coordinator that integrates all reception modules
@@ -145,7 +142,7 @@ export class FileReceiveOrchestrator implements MessageProcessorDelegate {
);
if (ReceptionConfig.DEBUG_CONFIG.ENABLE_CHUNK_LOGGING) {
// 🎯 关键日志2:接收端总结信息 - 使用统一的chunk范围计算逻辑
// 🎯 Critical log 2: Summary information for receiver - using unified chunk range calculation logic
const chunkRange = ChunkRangeCalculator.getChunkRange(
fileInfo.size,
offset,
@@ -395,14 +392,14 @@ export class FileReceiveOrchestrator implements MessageProcessorDelegate {
// Handle disk writing if needed
if (reception.sequencedWriter) {
// 🔧 修复:SequencedWriter使用绝对索引,确保传递正确的索引
// 🔧 Fix: SequencedWriter uses absolute index, ensuring correct index is passed
this.chunkProcessor.logChunkDetails(
result,
reception.expectedChunksCount,
reception.sequencedWriter.expectedIndex
);
// ✅ 正确使用绝对索引进行磁盘写入
// ✅ Correctly use absolute index for disk writing
await reception.sequencedWriter.writeChunk(
result.absoluteChunkIndex,
result.chunkData
+11 -15
View File
@@ -40,7 +40,7 @@ export class StreamingFileReader {
// Global state
private totalFileOffset = 0; // Current position in the entire file
private startChunkIndex = 0; // 🔧 记录传输起始的chunk索引
private startChunkIndex = 0; // 🔧 Record the chunk index at the start of transmission
private isFinished = false;
private isReading = false; // Prevent concurrent reading
@@ -48,15 +48,14 @@ export class StreamingFileReader {
this.file = file;
this.totalFileSize = file.size;
this.totalFileOffset = startOffset;
// 🔧 修复:续传时currentBatchStartOffset应该从startOffset开始
// 🔧 Fix: When resuming, currentBatchStartOffset should start from startOffset
this.currentBatchStartOffset = startOffset;
this.fileReader = new FileReader();
// 🔧 记录传输的起始chunk索引,用于边界检测
// 🔧 Record the starting chunk index of the transfer, used for boundary detection
this.startChunkIndex = Math.floor(startOffset / this.NETWORK_CHUNK_SIZE);
if (developmentEnv === "development") {
// 🎯 关键日志1:发送端总结信息 - 使用统一的chunk范围计算逻辑
const chunkRange = ChunkRangeCalculator.getChunkRange(
this.totalFileSize,
startOffset,
@@ -96,11 +95,9 @@ export class StreamingFileReader {
// 4. Update state
this.updateChunkState(networkChunk);
// 🎯 关键日志:边界chunk验证(临时保留用于验证修复效果)
if (developmentEnv === "development") {
const totalChunks = this.calculateTotalNetworkChunks();
// 🔧 修复:使用简化的边界检测逻辑
const isFirst = globalChunkIndex === this.startChunkIndex;
const expectedLastChunk = Math.floor(
(this.totalFileSize - 1) / this.NETWORK_CHUNK_SIZE
@@ -188,9 +185,9 @@ export class StreamingFileReader {
const batchStartOffset = this.totalFileOffset;
this.currentBatchStartOffset = batchStartOffset;
// 🔧 修复:简化batch内索引计算逻辑
// 由于calculateGlobalChunkIndex现在直接基于totalFileOffset计算,
// batch内索引只需要基于当前batch的起始位置计算即可
// 🔧 Fix: Simplify index calculation logic within batch
// Since calculateGlobalChunkIndex now directly calculates based on totalFileOffset
// Indexing within a batch only needs to be calculated based on the starting position of the current batch
const chunkOffsetInBatch =
batchStartOffset -
Math.floor(batchStartOffset / this.BATCH_SIZE) * this.BATCH_SIZE;
@@ -248,14 +245,14 @@ export class StreamingFileReader {
/**
* ✂️ Slice 64KB network chunk from 32MB batch
* 🆕 修复:直接基于offset在batch中的位置计算,避免复杂的batch内索引计算
* 🆕 Fix: Calculate directly based on the position of offset in the batch, avoiding complex batch internal index calculations
*/
private sliceNetworkChunkFromBatch(): ArrayBuffer {
if (!this.currentBatch) {
throw new Error("No current batch available for slicing");
}
// 🆕 直接基于offset在batch中的位置计算,避免batch内索引计算错误
// 🆕 Calculated directly based on the position of offset in the batch to avoid index calculation errors within the batch
const offsetInBatch = this.totalFileOffset - this.currentBatchStartOffset;
const remainingInBatch = this.currentBatch.byteLength - offsetInBatch;
const chunkSize = Math.min(this.NETWORK_CHUNK_SIZE, remainingInBatch);
@@ -278,8 +275,7 @@ export class StreamingFileReader {
* 🔧 Simplified logic: directly calculate based on file offset to avoid batch boundary errors
*/
private calculateGlobalChunkIndex(): number {
// 🎯 核心修复:直接基于当前文件偏移量计算chunk索引,避免复杂的batch计算
// 这确保了与接收端ReceptionConfig.getChunkIndexFromOffset()完全一致的计算逻辑
// Calculate chunk index directly based on current file offset, avoiding complex batch calculations, consistent with receiver
return Math.floor(this.totalFileOffset / this.NETWORK_CHUNK_SIZE);
}
@@ -357,9 +353,9 @@ export class StreamingFileReader {
this.isFinished = false;
this.isReading = false;
this.currentBatch = null;
// 🔧 修复:reset时也要正确设置currentBatchStartOffset
// 🔧 Fix: Reset also needs to correctly set currentBatchStartOffset
this.currentBatchStartOffset = startOffset;
this.currentChunkIndexInBatch = 0; // 重置为0loadNextBatch会重新计算
this.currentChunkIndexInBatch = 0; // Reset to 0, loadNextBatch will recalculate
if (developmentEnv === "development") {
postLogToBackend(
+2 -6
View File
@@ -60,7 +60,7 @@ class WebRTCService {
}
};
this.sender.onDataChannelOpen = (peerId) => {
this.sender.onDataChannelOpen = (_peerId) => {
useFileTransferStore.getState().setIsSenderInRoom(true);
// Automatically broadcast current content
this.broadcastDataToAllPeers();
@@ -215,10 +215,6 @@ class WebRTCService {
return this.fileReceiver.saveType;
}
public manualSafeSave(): void {
this.fileReceiver.gracefulShutdown();
}
private handleConnectionDisconnect(peerId: string, isSender: boolean, reason: string): void {
console.log(`[WebRTC Service] Connection disconnect: ${reason}, peer: ${peerId}, sender: ${isSender}`);
@@ -256,7 +252,7 @@ class WebRTCService {
}
// update connection state
private updateConnectionState(peerId: string, isSender: boolean): void {
private updateConnectionState(_peerId: string, isSender: boolean): void {
const store = useFileTransferStore.getState();
if (isSender) {
-3
View File
@@ -193,9 +193,6 @@ export type FileListDisplay = {
PopupDialog_description: string;
chooseSavePath_tips: string;
chooseSavePath_dis: string;
safeSave_dis: string;
safeSave_tooltip: string;
safeSaveSuccessMsg: string;
};
export type RetrieveMethod = {