Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,253 changes: 683 additions & 570 deletions client/web/package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions client/web/src/app/core/i18n/localizations/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"DROP_FILES_HERE": "أسقِط الملفات هنا",
"WANTS_TO_SEND_FILE": "يرغب في إرسال ملف إليك",
"ACCEPT": "قبول",
"DECLINE": "رفض",
"CANCEL": "إلغاء",

"_ROOMS_SECTION": "==== ROOMS ====",
Expand Down Expand Up @@ -132,8 +133,9 @@
"SESSION_ENDED_SUCCESS": "تم إنهاء الجلسة بنجاح.",
"FILE_UPLOAD_CANCELLED": "تم إلغاء رفع الملف من قبل المرسل",
"FILE_DOWNLOAD_CANCELLED": "تم إلغاء تحميل الملف من قبل المرسل",
"FILE_DOWNLOAD_COMPLETED": "تم تحميل {{fileName}} بنجاح.",
"FILE_UPLOAD_COMPLETED": "تم رفع {{fileName}} بنجاح.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} جاهز للحفظ.",
"FILE_UPLOAD_COMPLETED": "تم إرسال {{fileName}} بنجاح.",
"FINALIZING_TRANSFER": "جارٍ الإنهاء...",
"CONNECTION_FAILED_WITH_USER": "فشل الاتصال بالمستخدم {{userName}}.",
"CONNECTION_UNSTABLE_WITH_USER": "الاتصال بالمستخدم {{userName}} غير مستقر.",
"RECONNECTING_TO_USER": "جارٍ إعادة الاتصال بالمستخدم {{userName}}...",
Expand Down
6 changes: 4 additions & 2 deletions client/web/src/app/core/i18n/localizations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"DROP_FILES_HERE": "Drop files here",
"WANTS_TO_SEND_FILE": "wants to send you a file",
"ACCEPT": "Accept",
"DECLINE": "Decline",
"CANCEL": "Cancel",

"_ROOMS_SECTION": "==== ROOMS ====",
Expand Down Expand Up @@ -132,8 +133,9 @@
"SESSION_ENDED_SUCCESS": "Private session ended successfully.",
"FILE_UPLOAD_CANCELLED": "The sender canceled the upload.",
"FILE_DOWNLOAD_CANCELLED": "The sender canceled the download.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} downloaded successfully.",
"FILE_UPLOAD_COMPLETED": "{{fileName}} uploaded successfully.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} is ready to save.",
"FILE_UPLOAD_COMPLETED": "{{fileName}} sent successfully.",
"FINALIZING_TRANSFER": "Finalizing...",
"CONNECTION_FAILED_WITH_USER": "Failed to connect to {{userName}}.",
"CONNECTION_UNSTABLE_WITH_USER": "Connection with {{userName}} is unstable.",
"RECONNECTING_TO_USER": "Reconnecting to {{userName}}...",
Expand Down
4 changes: 3 additions & 1 deletion client/web/src/app/core/i18n/localizations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"DROP_FILES_HERE": "Suelta los archivos aquí",
"WANTS_TO_SEND_FILE": "quiere enviarte un archivo",
"ACCEPT": "Aceptar",
"DECLINE": "Rechazar",
"CANCEL": "Cancelar",

"_ROOMS_SECTION": "==== ROOMS ====",
Expand Down Expand Up @@ -132,8 +133,9 @@
"SESSION_ENDED_SUCCESS": "Sesión privada finalizada con éxito.",
"FILE_UPLOAD_CANCELLED": "El remitente canceló el envío.",
"FILE_DOWNLOAD_CANCELLED": "El remitente canceló la descarga.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} descargado correctamente.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} está listo para guardar.",
"FILE_UPLOAD_COMPLETED": "{{fileName}} enviado correctamente.",
"FINALIZING_TRANSFER": "Finalizando...",
"CONNECTION_FAILED_WITH_USER": "No se pudo conectar con {{userName}}.",
"CONNECTION_UNSTABLE_WITH_USER": "La conexión con {{userName}} es inestable.",
"RECONNECTING_TO_USER": "Reconectando con {{userName}}...",
Expand Down
4 changes: 3 additions & 1 deletion client/web/src/app/core/i18n/localizations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"DROP_FILES_HERE": "Déposez les fichiers ici",
"WANTS_TO_SEND_FILE": "veut vous envoyer un fichier",
"ACCEPT": "Accepter",
"DECLINE": "Refuser",
"CANCEL": "Annuler",

"_ROOMS_SECTION": "==== ROOMS ====",
Expand Down Expand Up @@ -132,8 +133,9 @@
"SESSION_ENDED_SUCCESS": "Session privée terminée avec succès.",
"FILE_UPLOAD_CANCELLED": "L'expéditeur a annulé l'envoi.",
"FILE_DOWNLOAD_CANCELLED": "L'expéditeur a annulé le téléchargement.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} téléchargé avec succès.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} est prêt à être enregistré.",
"FILE_UPLOAD_COMPLETED": "{{fileName}} envoyé avec succès.",
"FINALIZING_TRANSFER": "Finalisation...",
"CONNECTION_FAILED_WITH_USER": "Impossible de se connecter à {{userName}}.",
"CONNECTION_UNSTABLE_WITH_USER": "La connexion avec {{userName}} est instable.",
"RECONNECTING_TO_USER": "Reconnexion à {{userName}}...",
Expand Down
4 changes: 3 additions & 1 deletion client/web/src/app/core/i18n/localizations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"DROP_FILES_HERE": "Перетащите файлы сюда",
"WANTS_TO_SEND_FILE": "хочет отправить вам файл",
"ACCEPT": "Принять",
"DECLINE": "Отклонить",
"CANCEL": "Отмена",

"_ROOMS_SECTION": "==== ROOMS ====",
Expand Down Expand Up @@ -132,8 +133,9 @@
"SESSION_ENDED_SUCCESS": "Приватная сессия успешно завершена.",
"FILE_UPLOAD_CANCELLED": "Отправитель отменил загрузку.",
"FILE_DOWNLOAD_CANCELLED": "Отправитель отменил скачивание.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} успешно скачан.",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} готов к сохранению.",
"FILE_UPLOAD_COMPLETED": "{{fileName}} успешно отправлен.",
"FINALIZING_TRANSFER": "Завершение...",
"CONNECTION_FAILED_WITH_USER": "Не удалось подключиться к {{userName}}.",
"CONNECTION_UNSTABLE_WITH_USER": "Соединение с {{userName}} нестабильно.",
"RECONNECTING_TO_USER": "Переподключение к {{userName}}...",
Expand Down
6 changes: 4 additions & 2 deletions client/web/src/app/core/i18n/localizations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"DROP_FILES_HERE": "将文件拖放到此处",
"WANTS_TO_SEND_FILE": "想向您发送一个文件",
"ACCEPT": "接受",
"DECLINE": "拒绝",
"CANCEL": "取消",

"_ROOMS_SECTION": "==== ROOMS ====",
Expand Down Expand Up @@ -132,8 +133,9 @@
"SESSION_ENDED_SUCCESS": "私密会话已成功结束。",
"FILE_UPLOAD_CANCELLED": "发送方已取消上传。",
"FILE_DOWNLOAD_CANCELLED": "发送方已取消下载。",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} 下载完成。",
"FILE_UPLOAD_COMPLETED": "{{fileName}} 上传完成。",
"FILE_DOWNLOAD_COMPLETED": "{{fileName}} 已可保存。",
"FILE_UPLOAD_COMPLETED": "{{fileName}} 发送完成。",
"FINALIZING_TRANSFER": "正在完成……",
"CONNECTION_FAILED_WITH_USER": "无法连接到 {{userName}}。",
"CONNECTION_UNSTABLE_WITH_USER": "与 {{userName}} 的连接不稳定。",
"RECONNECTING_TO_USER": "正在重新连接到 {{userName}}……",
Expand Down
1 change: 1 addition & 0 deletions client/web/src/app/core/interfaces/webrtc.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface IWebRTCService {
fileResponses$: Subject<{ accepted: boolean; fromUser: string; fileId: string }>;
fileUploadCancelled$: Subject<{ fromUser: string; fileId: string }>;
fileDownloadCancelled$: Subject<{ fromUser: string; fileId: string }>;
fileReceived$: Subject<{ fromUser: string; fileId: string }>;
bufferedAmountLow$: Subject<string>;
incomingFileChunk$: Subject<{
fromUser: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class WebRTCCommunicationService {
public fileResponses$ = new Subject<{ accepted: boolean; fromUser: string; fileId: string }>();
public fileUploadCancelled$ = new Subject<{ fromUser: string; fileId: string }>();
public fileDownloadCancelled$ = new Subject<{ fromUser: string; fileId: string }>();
public fileReceived$ = new Subject<{ fromUser: string; fileId: string }>();
public bufferedAmountLow$ = new Subject<string>();
public incomingFileChunk$ = new Subject<{
fromUser: string;
Expand Down Expand Up @@ -432,6 +433,18 @@ export class WebRTCCommunicationService {
});
break;
}
case FILE_TRANSFER_MESSAGE_TYPES.FILE_RECEIVED: {
this.logger.info(
'handleDataChannelMessage',
`Received file assembly confirmation from ${targetUser}`
);
const fileReceivedPayload = message.payload as { fileId: string };
this.fileReceived$.next({
fromUser: targetUser,
fileId: fileReceivedPayload.fileId,
});
break;
}
default:
this.logger.warn('handleDataChannelMessage', `Unknown message type: ${message.type}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ export class WebRTCService implements IWebRTCService {
return this.communicationService.fileDownloadCancelled$;
}

/**
* Gets the file received subject
*/
public get fileReceived$(): Subject<{ fromUser: string; fileId: string }> {
return this.communicationService.fileReceived$;
}

/**
* Gets the buffered amount low subject
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ export class WebSocketConnectionService implements OnDestroy {
}
};

socket.onerror = (_event) => {
socket.onerror = () => {
if (socket !== this.socket) return;
this.logger.warn('connect', 'WebSocket connection error (will attempt reconnect)');
this.isConnecting = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,16 @@ export class FileDownloadService extends FileTransferBaseService {

this.toaster.success(this.translate.instant('FILE_DOWNLOAD_COMPLETED', { fileName }));

this.sendData(
{
type: FILE_TRANSFER_MESSAGE_TYPES.FILE_RECEIVED,
payload: {
fileId: fileDownload.fileId,
Comment thread
SloMR marked this conversation as resolved.
},
},
fromUser
);

this.finishReceiveSpan(fromUser, fileDownload.fileId, 'completed');

// Cleanup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ export class FileTransferService implements IFileTransferService {
`Download cancelled by receiver ${fromUser}, stopping upload ${fileId}`
);
});

this.webrtcService.fileReceived$.subscribe(async ({ fromUser, fileId }) => {
await this.fileUploadService.completeFileUpload(fromUser, fileId);
this.logger.debug(
'FileTransferService',
`File received by ${fromUser}, finishing upload ${fileId}`
);
});
}

// =============== Properties ===============
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class FileUploadService extends FileTransferBaseService {
isPaused: false,
targetUser,
progress: 0,
phase: 'sending',
};

userMap.set(fileId, fileTransfer);
Expand Down Expand Up @@ -114,6 +115,9 @@ export class FileUploadService extends FileTransferBaseService {
return;
}

fileTransfer.phase = 'sending';
await this.setFileTransfers(targetUser, userMap);

// Add to queue for sequential processing
this.enqueueFileForUser(targetUser, fileId);
const queueLength = this.userFileQueues.get(targetUser)?.length ?? 0;
Expand Down Expand Up @@ -287,6 +291,34 @@ export class FileUploadService extends FileTransferBaseService {
}
}

/**
* Completes an upload once the receiver confirms the file was assembled.
*/
public async completeFileUpload(targetUser: string, fileId: string): Promise<void> {
const userMap = await this.getFileTransfers(targetUser);
const fileTransfer = userMap?.get(fileId);

if (!userMap || !fileTransfer) {
this.logger.debug(
'completeFileUpload',
`No pending upload found for ${targetUser} and fileId=${fileId}`
);
return;
}

const key = this.getOrCreateStatusKey(targetUser, fileId);
await this.setFileTransferStatus(key, FileTransferStatus.COMPLETED);

this.toaster.success(
this.translate.instant('FILE_UPLOAD_COMPLETED', { fileName: fileTransfer.file.name })
);

userMap.delete(fileId);
await this.setFileTransfers(targetUser, userMap);
await this.updateActiveUploads();
await this.checkAllUsersResponded();
}

/**
* Resumes a paused file transfer when buffer is available
*/
Expand Down Expand Up @@ -579,26 +611,19 @@ export class FileUploadService extends FileTransferBaseService {
if (fileTransfer.currentOffset >= fileTransfer.file.size) {
this.logger.info(
'sendFileChunks',
`Completed ${fileTransfer.fileId} to ${fileTransfer.targetUser}`
`Queued all chunks for ${fileTransfer.fileId} to ${fileTransfer.targetUser}`
);
span.setAttribute('outcome', 'completed');
span.setAttribute('outcome', 'queued_all_chunks');
span.setStatus({ code: 1, message: 'ok' });
fileTransfer.progress = 100;

const key = this.getOrCreateStatusKey(fileTransfer.targetUser, fileTransfer.fileId);
await this.setFileTransferStatus(key, FileTransferStatus.COMPLETED);

this.toaster.success(
this.translate.instant('FILE_UPLOAD_COMPLETED', { fileName: fileTransfer.file.name })
);
fileTransfer.phase = 'finalizing';

const userMap = await this.getFileTransfers(fileTransfer.targetUser);
if (userMap) {
Comment thread
SloMR marked this conversation as resolved.
userMap.delete(fileTransfer.fileId);
userMap.set(fileTransfer.fileId, fileTransfer);
await this.setFileTransfers(fileTransfer.targetUser, userMap);
}
await this.updateActiveUploads();
await this.checkAllUsersResponded();
}
} finally {
this.processingQueues.set(transferId, false);
Expand Down
Loading
Loading