mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-20 15:06:15 -04:00
Implement fallback to download if navigator.share() fails. Refactor ReceiveFileDialog
This commit is contained in:
parent
d8908e01ea
commit
19d33e11d8
2 changed files with 88 additions and 64 deletions
|
@ -161,7 +161,9 @@
|
||||||
"message-transfer-completed": "Message transfer completed",
|
"message-transfer-completed": "Message transfer completed",
|
||||||
"unfinished-transfers-warning": "There are unfinished transfers. Are you sure you want to close PairDrop?",
|
"unfinished-transfers-warning": "There are unfinished transfers. Are you sure you want to close PairDrop?",
|
||||||
"rate-limit-join-key": "Rate limit reached. Wait 10 seconds and try again.",
|
"rate-limit-join-key": "Rate limit reached. Wait 10 seconds and try again.",
|
||||||
"selected-peer-left": "Selected peer left"
|
"selected-peer-left": "Selected peer left",
|
||||||
|
"error-sharing-size": "Files too big to be shared. They can be downloaded instead.",
|
||||||
|
"error-sharing-default": "Error while sharing. It can be downloaded instead."
|
||||||
},
|
},
|
||||||
"document-titles": {
|
"document-titles": {
|
||||||
"file-received": "File Received",
|
"file-received": "File Received",
|
||||||
|
|
|
@ -1073,65 +1073,74 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
this.$shareBtn = this.$el.querySelector('#share-btn');
|
this.$shareBtn = this.$el.querySelector('#share-btn');
|
||||||
|
|
||||||
Events.on('files-received', e => this._onFilesReceived(e.detail.peerId, e.detail.files, e.detail.imagesOnly, e.detail.totalSize));
|
Events.on('files-received', e => this._onFilesReceived(e.detail.peerId, e.detail.files, e.detail.imagesOnly, e.detail.totalSize));
|
||||||
this._filesQueue = [];
|
this._filesDataQueue = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onFilesReceived(peerId, files, imagesOnly, totalSize) {
|
async _onFilesReceived(peerId, files, imagesOnly, totalSize) {
|
||||||
|
const descriptor = this._getDescriptor(files, imagesOnly);
|
||||||
const displayName = $(peerId).ui._displayName();
|
const displayName = $(peerId).ui._displayName();
|
||||||
const connectionHash = $(peerId).ui._connectionHash;
|
const connectionHash = $(peerId).ui._connectionHash;
|
||||||
const badgeClassName = $(peerId).ui._badgeClassName();
|
const badgeClassName = $(peerId).ui._badgeClassName();
|
||||||
|
|
||||||
this._filesQueue.push({
|
this._filesDataQueue.push({
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
displayName: displayName,
|
|
||||||
connectionHash: connectionHash,
|
|
||||||
files: files,
|
files: files,
|
||||||
imagesOnly: imagesOnly,
|
imagesOnly: imagesOnly,
|
||||||
totalSize: totalSize,
|
totalSize: totalSize,
|
||||||
|
descriptor: descriptor,
|
||||||
|
displayName: displayName,
|
||||||
|
connectionHash: connectionHash,
|
||||||
badgeClassName: badgeClassName
|
badgeClassName: badgeClassName
|
||||||
});
|
});
|
||||||
|
|
||||||
audioPlayer.playBlop();
|
audioPlayer.playBlop();
|
||||||
|
|
||||||
await this._nextFiles();
|
await this._processFiles();
|
||||||
}
|
|
||||||
|
|
||||||
async _nextFiles() {
|
|
||||||
if (this._busy || !this._filesQueue.length) return;
|
|
||||||
this._busy = true;
|
|
||||||
const {peerId, displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName} = this._filesQueue.shift();
|
|
||||||
await this._displayFiles(peerId, displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canShareFilesViaMenu(files) {
|
canShareFilesViaMenu(files) {
|
||||||
return window.isMobile && !!navigator.share && navigator.canShare({files});
|
return window.isMobile && !!navigator.share && navigator.canShare({files});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _displayFiles(peerId, displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName) {
|
async _processFiles() {
|
||||||
const descriptor = this._getDescriptor(files, imagesOnly);
|
if (this._busy || !this._filesDataQueue.length) return;
|
||||||
const documentTitleTranslation = files.length === 1
|
|
||||||
|
this._busy = true;
|
||||||
|
|
||||||
|
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'process'});
|
||||||
|
|
||||||
|
this._data = this._filesDataQueue.shift();
|
||||||
|
|
||||||
|
const documentTitleTranslation = this._data.files.length === 1
|
||||||
? `${ Localization.getTranslation("document-titles.file-received") } - PairDrop`
|
? `${ Localization.getTranslation("document-titles.file-received") } - PairDrop`
|
||||||
: `${ Localization.getTranslation("document-titles.file-received-plural", null, {count: files.length}) } - PairDrop`;
|
: `${ Localization.getTranslation("document-titles.file-received-plural", null, {count: this._data.files.length}) } - PairDrop`;
|
||||||
|
|
||||||
// If possible, share via menu - else download files
|
// If possible, share via menu - else download files
|
||||||
const shareViaMenu = this.canShareFilesViaMenu(files);
|
const shareViaMenu = this.canShareFilesViaMenu(this._data.files);
|
||||||
|
|
||||||
this._parseFileData(displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName);
|
this._parseFileData(
|
||||||
this._setTitle(descriptor);
|
this._data.displayName,
|
||||||
|
this._data.connectionHash,
|
||||||
|
this._data.files,
|
||||||
|
this._data.imagesOnly,
|
||||||
|
this._data.totalSize,
|
||||||
|
this._data.badgeClassName
|
||||||
|
);
|
||||||
|
this._setTitle(this._data.descriptor);
|
||||||
|
|
||||||
await this._addFileToPreviewBox(files[0]);
|
await this._addFileToPreviewBox(this._data.files[0]);
|
||||||
|
|
||||||
document.title = documentTitleTranslation;
|
document.title = documentTitleTranslation;
|
||||||
changeFavicon("images/favicon-96x96-notification.png");
|
changeFavicon("images/favicon-96x96-notification.png");
|
||||||
|
|
||||||
if (shareViaMenu) {
|
if (shareViaMenu) {
|
||||||
await this._setViaShareMenu(files);
|
await this._setupShareMenu();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await this._setViaDownload(peerId, files, totalSize, descriptor);
|
await this._setupDownload();
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.fire('set-progress', {peerId: peerId, progress: 1, status: "receive-complete"});
|
Events.fire('set-progress', {peerId: this._data.peerId, progress: 0, status: "receive-complete"});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDescriptor(files, imagesOnly) {
|
_getDescriptor(files, imagesOnly) {
|
||||||
|
@ -1152,6 +1161,7 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
_setTitle(descriptor) {
|
_setTitle(descriptor) {
|
||||||
this.$receiveTitle.innerText = Localization.getTranslation("dialogs.receive-title", null, {descriptor: descriptor});
|
this.$receiveTitle.innerText = Localization.getTranslation("dialogs.receive-title", null, {descriptor: descriptor});
|
||||||
}
|
}
|
||||||
|
|
||||||
createPreviewElement(file) {
|
createPreviewElement(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
@ -1200,13 +1210,22 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
}, duration);
|
}, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setShareButton(files) {
|
async _setShareButton() {
|
||||||
this.$shareBtn.onclick = _ => {
|
this.$shareBtn.onclick = _ => {
|
||||||
navigator.share({files: files})
|
navigator.share({files: this._data.files})
|
||||||
.catch(err => {
|
.catch(async err => {
|
||||||
Logger.error(err);
|
Logger.error(err);
|
||||||
// Todo: tidy up, setDownloadButton instead and show warning to user
|
|
||||||
// Differentiate: "File too big to be shared. It can be downloaded instead." and "Error while sharing. It can be downloaded instead."
|
if (err.name === 'AbortError' && err.message === 'Abort due to error while reading files.') {
|
||||||
|
Events.fire('notify-user', Localization.getTranslation("notifications.error-sharing-size"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Events.fire('notify-user', Localization.getTranslation("notifications.error-sharing-default"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to download
|
||||||
|
this._tidyUpButtons();
|
||||||
|
await this._setupDownload()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prevent clicking the button multiple times
|
// Prevent clicking the button multiple times
|
||||||
|
@ -1216,42 +1235,28 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
this.$shareBtn.removeAttribute('hidden');
|
this.$shareBtn.removeAttribute('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setDownloadButton(peerId, files, totalSize, descriptor) {
|
async _processDataAsZip() {
|
||||||
let downloadTranslation = Localization.getTranslation("dialogs.download")
|
|
||||||
let downloadSuccessfulTranslation = Localization.getTranslation("notifications.download-successful", null, {descriptor: descriptor});
|
|
||||||
|
|
||||||
this.$downloadBtn.innerText = downloadTranslation;
|
|
||||||
this.$downloadBtn.removeAttribute('disabled');
|
|
||||||
this.$downloadBtn.removeAttribute('hidden');
|
|
||||||
|
|
||||||
let zipFileUrl, zipFileName;
|
let zipFileUrl, zipFileName;
|
||||||
|
let sendAsZip = false;
|
||||||
|
|
||||||
const tooBigToZip = window.iOS && totalSize > 256000000;
|
const tooBigToZip = window.iOS && this._data.totalSize > 256000000;
|
||||||
this.sendAsZip = false;
|
if (this._data.files.length > 1 && !tooBigToZip) {
|
||||||
if (files.length > 1 && !tooBigToZip) {
|
zipFileUrl = await this._createZipFile(this._data.files, zipProgress => {
|
||||||
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'process'});
|
|
||||||
|
|
||||||
zipFileUrl = await this._createZipFile(files, zipProgress => {
|
|
||||||
Events.fire('set-progress', {
|
Events.fire('set-progress', {
|
||||||
peerId: peerId,
|
peerId: this._data.peerId,
|
||||||
progress: zipProgress / totalSize,
|
progress: zipProgress / this._data.totalSize,
|
||||||
status: 'process'
|
status: 'process'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
zipFileName = this._createZipFilename()
|
zipFileName = this._createZipFilename()
|
||||||
|
|
||||||
this.sendAsZip = !!zipFileUrl;
|
sendAsZip = !!zipFileUrl;
|
||||||
|
}
|
||||||
|
return {sendAsZip, zipFileUrl, zipFileName};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If single file or zipping failed -> download files individually -> else download zip
|
_setDownloadButtonToZip(zipFileUrl, zipFileName) {
|
||||||
if (this.sendAsZip) {
|
const downloadSuccessfulTranslation = Localization.getTranslation("notifications.download-successful", null, {descriptor: this._data.descriptor});
|
||||||
this._setDownloadButtonToZip(zipFileUrl, zipFileName, downloadSuccessfulTranslation);
|
|
||||||
} else {
|
|
||||||
this._setDownloadButtonToFiles(files, downloadSuccessfulTranslation, downloadTranslation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setDownloadButtonToZip(zipFileUrl, zipFileName, downloadSuccessfulTranslation) {
|
|
||||||
this.downloadSuccessful = false;
|
this.downloadSuccessful = false;
|
||||||
this.$downloadBtn.onclick = _ => {
|
this.$downloadBtn.onclick = _ => {
|
||||||
this._downloadFileFromUrl(zipFileUrl, zipFileName)
|
this._downloadFileFromUrl(zipFileUrl, zipFileName)
|
||||||
|
@ -1263,12 +1268,17 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_setDownloadButtonToFiles(files, downloadSuccessfulTranslation, downloadTranslation) {
|
_setDownloadButtonToFiles(files) {
|
||||||
|
const downloadTranslation = Localization.getTranslation("dialogs.download");
|
||||||
|
const downloadSuccessfulTranslation = Localization.getTranslation("notifications.download-successful", null, {descriptor: this._data.descriptor});
|
||||||
|
|
||||||
|
this.$downloadBtn.innerText = files.length === 1
|
||||||
|
? downloadTranslation
|
||||||
|
: `${downloadTranslation} 1/${files.length}`;
|
||||||
|
|
||||||
this.downloadSuccessful = false;
|
this.downloadSuccessful = false;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
this.$downloadBtn.innerText = `${downloadTranslation} ${i + 1}/${files.length}`;
|
|
||||||
|
|
||||||
this.$downloadBtn.onclick = _ => {
|
this.$downloadBtn.onclick = _ => {
|
||||||
this._disableButton(this.$shareBtn, 2000);
|
this._disableButton(this.$shareBtn, 2000);
|
||||||
|
|
||||||
|
@ -1351,21 +1361,33 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
return `PairDrop_files_${year}${month}${date}_${hours}${minutes}.zip`;
|
return `PairDrop_files_${year}${month}${date}_${hours}${minutes}.zip`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setViaShareMenu(files) {
|
async _setupShareMenu() {
|
||||||
await this._setShareButton(files);
|
await this._setShareButton();
|
||||||
|
|
||||||
// always show dialog
|
// always show dialog
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
// open share menu automatically
|
// open share menu automatically
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$shareBtn.click();
|
this.$shareBtn.click();
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setViaDownload(peerId, files, totalSize, descriptor) {
|
async _setupDownload() {
|
||||||
await this._setDownloadButton(peerId, files, totalSize, descriptor);
|
this.$downloadBtn.innerText = Localization.getTranslation("dialogs.download");
|
||||||
|
this.$downloadBtn.removeAttribute('disabled');
|
||||||
|
this.$downloadBtn.removeAttribute('hidden');
|
||||||
|
|
||||||
if (!this.sendAsZip && files.length !== 1) {
|
let {sendAsZip, zipFileUrl, zipFileName} = await this._processDataAsZip();
|
||||||
|
|
||||||
|
// If single file or zipping failed -> download files individually -> else download zip
|
||||||
|
if (sendAsZip) {
|
||||||
|
this._setDownloadButtonToZip(zipFileUrl, zipFileName);
|
||||||
|
} else {
|
||||||
|
this._setDownloadButtonToFiles(this._data.files);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendAsZip) {
|
||||||
this.show();
|
this.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1373,7 +1395,7 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
// download automatically if zipped or if only one file is received
|
// download automatically if zipped or if only one file is received
|
||||||
this.$downloadBtn.click();
|
this.$downloadBtn.click();
|
||||||
|
|
||||||
// if automatic download fails -> show dialog
|
// if automatic download fails -> show dialog after 1 s
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.downloadSuccessful) {
|
if (!this.downloadSuccessful) {
|
||||||
this.show();
|
this.show();
|
||||||
|
@ -1402,7 +1424,7 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
|
|
||||||
this._busy = false;
|
this._busy = false;
|
||||||
|
|
||||||
await this._nextFiles();
|
await this._processFiles();
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue