diff --git a/public/index.html b/public/index.html
index 70eda5b..3396563 100644
--- a/public/index.html
+++ b/public/index.html
@@ -501,6 +501,17 @@
+
+
@@ -531,6 +542,17 @@
+
+
diff --git a/public/lang/en.json b/public/lang/en.json
index 54a05c6..885f2ef 100644
--- a/public/lang/en.json
+++ b/public/lang/en.json
@@ -70,6 +70,8 @@
"join": "Join",
"leave": "Leave",
"would-like-to-share": "would like to share",
+ "change-name": "Change name :",
+ "reset-name": "Reset Name",
"accept": "Accept",
"decline": "Decline",
"has-sent": "has sent:",
diff --git a/public/scripts/ui.js b/public/scripts/ui.js
index 42da3bc..47f3682 100644
--- a/public/scripts/ui.js
+++ b/public/scripts/ui.js
@@ -826,6 +826,10 @@ class LanguageSelectDialog extends Dialog {
}
class ReceiveDialog extends Dialog {
+ // These variables are made static so that they can be accessed and modified across child classes.
+ static fileNameInputValue = "";
+ static fileNameInputExtValue = "";
+
constructor(id) {
super(id);
this.$fileDescription = this.$el.querySelector('.file-description');
@@ -836,6 +840,13 @@ class ReceiveDialog extends Dialog {
this.$fileSize = this.$el.querySelector('.file-size');
this.$previewBox = this.$el.querySelector('.file-preview');
this.$receiveTitle = this.$el.querySelector('h2:first-of-type');
+
+ // Store reserved file names initially in an array
+ this.reservedNames = ["con", "prn", "aux", "nul"];
+ for (let i=1; i <= 9; i++) {
+ this.reservedNames.push(`com${i}`);
+ this.reservedNames.push(`lpt${i}`);
+ }
}
_formatFileSize(bytes) {
@@ -855,6 +866,24 @@ class ReceiveDialog extends Dialog {
}
}
+ resetFileNameInput(files, fileNameInput, fileNameInputExt) {
+ // Reset file name inside input text box and file name extension.
+ const fileName = files[0].name;
+ const fileNameSplit = fileName.split('.');
+ const fileExtension = fileNameSplit.length > 1
+ ? '.' + fileNameSplit[fileNameSplit.length - 1]
+ : '';
+ if (files.length === 1) {
+ fileNameInput.value = fileName.substring(0, fileName.length - fileExtension.length);
+ fileNameInputExt.innerText = fileExtension;
+ } else {
+ fileNameInput.value = "PairDrop_files_{YYYY}{MM}{DD}_{hh}{mm}";
+ fileNameInputExt.innerText = ".zip";
+ }
+ ReceiveDialog.fileNameInputValue = fileNameInput.value;
+ ReceiveDialog.fileNameInputExtValue = fileNameInputExt.innerText;
+ }
+
_parseFileData(displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName) {
let fileOther = "";
@@ -882,7 +911,48 @@ class ReceiveDialog extends Dialog {
this.$displayName.innerText = displayName;
this.$displayName.title = connectionHash;
this.$displayName.classList.remove("badge-room-ip", "badge-room-secret", "badge-room-public-id");
- this.$displayName.classList.add(badgeClassName)
+ this.$displayName.classList.add(badgeClassName);
+ }
+
+ validateFileNameInput(fileNameInput, fileNameError, btn) {
+ // File name with invalid characters ("/", "\", ":", "*", "?", """, "<" and ">") or reserved names or empty string is considered invalid.
+ // If the file name is invalid, then respective error is displayed and the button to download is disabled.
+ const fileNameInputVal = fileNameInput.value;
+ if (fileNameInputVal.includes("/")) {
+ fileNameError.innerText = "File name should not contain '/'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes("\\")) {
+ fileNameError.innerText = "File name should not contain '\\'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes(":")) {
+ fileNameError.innerText = "File name should not contain ':'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes("*")) {
+ fileNameError.innerText = "File name should not contain '*'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes("?")) {
+ fileNameError.innerText = "File name should not contain '?'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes("\"")) {
+ fileNameError.innerText = "File name should not contain '\"'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes("<")) {
+ fileNameError.innerText = "File name should not contain '<'.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.includes(">")) {
+ fileNameError.innerText = "File name should not contain '>'.";
+ btn.setAttribute('disabled', true);
+ } else if (this.reservedNames.includes(fileNameInputVal.toLowerCase())) {
+ fileNameError.innerText = "Reserved names cannot be set as file names.";
+ btn.setAttribute('disabled', true);
+ } else if (fileNameInputVal.trim() === "") {
+ fileNameError.innerText = "File name cannot be empty.";
+ btn.setAttribute('disabled', true);
+ } else {
+ ReceiveDialog.fileNameInputValue = fileNameInputVal;
+ fileNameError.innerText = "";
+ btn.removeAttribute('disabled');
+ }
}
}
@@ -893,6 +963,11 @@ class ReceiveFileDialog extends ReceiveDialog {
this.$downloadBtn = this.$el.querySelector('#download-btn');
this.$shareBtn = this.$el.querySelector('#share-btn');
+ this.$fileNameInput = this.$el.querySelector('#file-name-input-2');
+ this.$fileNameInputExt = this.$el.querySelector('#file-name-input-ext-2');
+ this.$resetNameBtn = this.$el.querySelector('#reset-name-2');
+ this.$fileNameError = this.$el.querySelector('#file-name-error-2');
+ this.$fileNameInput.addEventListener('input', _ => this.validateFileNameInput(this.$fileNameInput, this.$fileNameError, this.$downloadBtn));
Events.on('files-received', e => this._onFilesReceived(e.detail.peerId, e.detail.files, e.detail.imagesOnly, e.detail.totalSize));
this._filesQueue = [];
@@ -960,9 +1035,42 @@ class ReceiveFileDialog extends ReceiveDialog {
});
}
+ adjustFileName(fileName) {
+ // Method to replace the placeholders inside file name with respective timestamp values
+ let newFileName = fileName;
+ let now = new Date(Date.now());
+ let year = now.getFullYear().toString();
+ let month = (now.getMonth()+1).toString();
+ month = month.length < 2 ? "0" + month : month;
+ let date = now.getDate().toString();
+ date = date.length < 2 ? "0" + date : date;
+ let hours = now.getHours().toString();
+ hours = hours.length < 2 ? "0" + hours : hours;
+ let minutes = now.getMinutes().toString();
+ minutes = minutes.length < 2 ? "0" + minutes : minutes;
+ newFileName = newFileName.replaceAll("{YYYY}", year);
+ newFileName = newFileName.replaceAll("{MM}", month);
+ newFileName = newFileName.replaceAll("{DD}", date);
+ newFileName = newFileName.replaceAll("{hh}", hours);
+ newFileName = newFileName.replaceAll("{mm}", minutes);
+ console.log(newFileName);
+ return newFileName;
+ }
+
async _displayFiles(peerId, displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName) {
this._parseFileData(displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName);
+ // Adjust the input content initially
+ this.$fileNameInput.value = ReceiveDialog.fileNameInputValue;
+ this.$fileNameInputExt.innerText = ReceiveDialog.fileNameInputExtValue;
+ this.$fileNameError.innerText = "";
+
+ this.$resetNameBtn.addEventListener("click", _ => {
+ this.resetFileNameInput(files, this.$fileNameInput, this.$fileNameInputExt);
+ this.$fileNameError.innerText = "";
+ this.$downloadBtn.removeAttribute('disabled');
+ });
+
let descriptor, url, filenameDownload;
if (files.length === 1) {
descriptor = imagesOnly
@@ -1029,7 +1137,11 @@ class ReceiveFileDialog extends ReceiveDialog {
this.$downloadBtn.onclick = _ => {
if (downloadZipped) {
let tmpZipBtn = document.createElement("a");
- tmpZipBtn.download = filenameDownload;
+ if (ReceiveDialog.fileNameInputValue !== "") {
+ tmpZipBtn.download = this.adjustFileName(ReceiveDialog.fileNameInputValue)+ReceiveDialog.fileNameInputExtValue;
+ } else {
+ tmpZipBtn.download = filenameDownload;
+ }
tmpZipBtn.href = url;
tmpZipBtn.click();
}
@@ -1080,7 +1192,11 @@ class ReceiveFileDialog extends ReceiveDialog {
_downloadFilesIndividually(files) {
let tmpBtn = document.createElement("a");
for (let i=0; i this._respondToFileTransferRequest(true));
this.$declineRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(false));
+ this.$fileNameInput = this.$el.querySelector('#file-name-input-1');
+ this.$fileNameInputExt = this.$el.querySelector('#file-name-input-ext-1');
+ this.$resetNameBtn = this.$el.querySelector('#reset-name-1');
+ this.$fileNameError = this.$el.querySelector('#file-name-error-1');
+ this.$fileNameInput.addEventListener('input', _ => this.validateFileNameInput(this.$fileNameInput, this.$fileNameError, this.$acceptRequestBtn));
Events.on('files-transfer-request', e => this._onRequestFileTransfer(e.detail.request, e.detail.peerId))
Events.on('keydown', e => this._onKeyDown(e));
@@ -1143,6 +1264,15 @@ class ReceiveRequestDialog extends ReceiveDialog {
this._parseFileData(displayName, connectionHash, request.header, request.imagesOnly, request.totalSize, badgeClassName);
+ // Initially reset the file name inside input textbox
+ this.resetFileNameInput(request.header, this.$fileNameInput, this.$fileNameInputExt);
+
+ this.$resetNameBtn.addEventListener("click", _ => {
+ this.resetFileNameInput(request.header, this.$fileNameInput, this.$fileNameInputExt);
+ this.$fileNameError.innerText = "";
+ this.$acceptRequestBtn.removeAttribute('disabled');
+ });
+
if (request.thumbnailDataUrl && request.thumbnailDataUrl.substring(0, 22) === "data:image/jpeg;base64") {
let element = document.createElement('img');
element.src = request.thumbnailDataUrl;
diff --git a/public/styles/styles-main.css b/public/styles/styles-main.css
index 32ffc46..f48300e 100644
--- a/public/styles/styles-main.css
+++ b/public/styles/styles-main.css
@@ -297,6 +297,10 @@ h3 {
color: white !important;
}
+.text-red {
+ color: red !important;
+}
+
.font-body1,
body {
font-size: 14px;
@@ -678,6 +682,11 @@ button::-moz-focus-inner {
border: 0;
}
+#reset-name-1, #reset-name-2 {
+ margin: 3px;
+ height: 50px;
+}
+
/* Icon Button */
.icon-button {