Implement fallback for text messages larger than the max message size to sent them in chunks via the send files API

This commit is contained in:
schlagmichdoch 2024-02-21 18:19:59 +01:00
parent 00f1a20177
commit 755d5e29f0
3 changed files with 190 additions and 97 deletions

View file

@ -93,6 +93,7 @@
"file-other-description-file": "and 1 other file", "file-other-description-file": "and 1 other file",
"file-other-description-image-plural": "and {{count}} other images", "file-other-description-image-plural": "and {{count}} other images",
"file-other-description-file-plural": "and {{count}} other files", "file-other-description-file-plural": "and {{count}} other files",
"text-message-description": "A large text message",
"title-image": "Image", "title-image": "Image",
"title-file": "File", "title-file": "File",
"title-image-plural": "Images", "title-image-plural": "Images",
@ -171,6 +172,7 @@
"file-received-plural": "{{count}} Files Received", "file-received-plural": "{{count}} Files Received",
"file-transfer-requested": "File Transfer Requested", "file-transfer-requested": "File Transfer Requested",
"image-transfer-requested": "Image Transfer Requested", "image-transfer-requested": "Image Transfer Requested",
"message-transfer-requested": "Message Transfer Requested",
"message-received": "Message Received", "message-received": "Message Received",
"message-received-plural": "{{count}} Messages Received" "message-received-plural": "{{count}} Messages Received"
}, },

View file

@ -331,6 +331,8 @@ class Peer {
this._isCaller = isCaller; this._isCaller = isCaller;
this._peerId = peerId; this._peerId = peerId;
this._maxMessageSize = 65536; // 64 KB
this._roomIds = {}; this._roomIds = {};
this._updateRoomIds(roomType, roomId); this._updateRoomIds(roomType, roomId);
@ -352,14 +354,15 @@ class Peer {
// tidy up sender // tidy up sender
this._filesRequested = null; this._filesRequested = null;
this._requestSent = null;
this._chunker = null; this._chunker = null;
// tidy up receiver // tidy up receiver
this._pendingRequest = null; this._requestPending = null;
this._acceptedRequest = null; this._requestAccepted = null;
this._totalBytesReceived = 0; this._totalBytesReceived = 0;
this._digester = null; this._digester = null;
this._filesReceived = []; this._filesReceived = null;
// disable NoSleep if idle // disable NoSleep if idle
Events.fire('evaluate-no-sleep'); Events.fire('evaluate-no-sleep');
@ -497,7 +500,7 @@ class Peer {
await this._onState(message.state); await this._onState(message.state);
break; break;
case 'transfer-request': case 'transfer-request':
await this._onTransferRequest(message); await this._onTransferRequest(message.request);
break; break;
case 'transfer-request-response': case 'transfer-request-response':
this._onTransferRequestResponse(message); this._onTransferRequestResponse(message);
@ -624,7 +627,7 @@ class Peer {
} }
// File Sender Only // File Sender Only
async _sendFileTransferRequest(files) { async _sendFileTransferRequest(files, fileIsMessage = false) {
this._state = Peer.STATE_PREPARE; this._state = Peer.STATE_PREPARE;
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'prepare'}); Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'prepare'});
@ -642,6 +645,13 @@ class Peer {
if (files[i].type.split('/')[0] !== 'image') imagesOnly = false; if (files[i].type.split('/')[0] !== 'image') imagesOnly = false;
} }
// request type 'images', 'files' or 'message
const filesType = fileIsMessage
? 'message'
: imagesOnly
? 'images'
: 'files';
let dataUrl = ""; let dataUrl = "";
if (files[0].type.split('/')[0] === 'image') { if (files[0].type.split('/')[0] === 'image') {
try { try {
@ -654,13 +664,18 @@ class Peer {
this._state = Peer.STATE_TRANSFER_REQUEST_SENT; this._state = Peer.STATE_TRANSFER_REQUEST_SENT;
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'wait'}); Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'wait'});
this._filesRequested = files; const request = {
this._sendMessage({type: 'transfer-request',
header: header, header: header,
totalSize: totalSize, totalSize: totalSize,
imagesOnly: imagesOnly, filesType: filesType,
thumbnailDataUrl: dataUrl thumbnailDataUrl: dataUrl
};
this._filesRequested = files;
this._requestSent = request;
this._sendMessage({type: 'transfer-request',
request: request
}); });
} }
@ -675,7 +690,7 @@ class Peer {
if (message.reason === 'ram-exceed-ios') { if (message.reason === 'ram-exceed-ios') {
Events.fire('notify-user', Localization.getTranslation('notifications.ram-exceed-ios')); Events.fire('notify-user', Localization.getTranslation('notifications.ram-exceed-ios'));
} }
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: null}); Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'idle'});
this._reset(); this._reset();
return; return;
} }
@ -748,7 +763,7 @@ class Peer {
if (!message.success) { if (!message.success) {
Logger.warn('File could not be sent'); Logger.warn('File could not be sent');
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: null}); Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'idle'});
this._reset(); this._reset();
return; return;
} }
@ -760,6 +775,9 @@ class Peer {
return; return;
} }
// If files sent was message -> abort and wait for text-received message
if (this._requestSent.filesType === 'message') return;
// No more files in queue. Transfer is complete // No more files in queue. Transfer is complete
this._reset(); this._reset();
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'transfer-complete'}); Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'transfer-complete'});
@ -770,7 +788,7 @@ class Peer {
// File Receiver Only // File Receiver Only
async _onTransferRequest(request) { async _onTransferRequest(request) {
// Only accept one request at a time per peer // Only accept one request at a time per peer
if (this._pendingRequest) { if (this._requestPending) {
this._sendTransferRequestResponse(false); this._sendTransferRequestResponse(false);
return; return;
} }
@ -790,7 +808,7 @@ class Peer {
} }
this._state = Peer.STATE_TRANSFER_REQUEST_RECEIVED; this._state = Peer.STATE_TRANSFER_REQUEST_RECEIVED;
this._pendingRequest = request; this._requestPending = request;
// Automatically accept request if auto-accept is set to true via the Edit Paired Devices Dialog // Automatically accept request if auto-accept is set to true via the Edit Paired Devices Dialog
if (this._autoAccept) { if (this._autoAccept) {
@ -827,7 +845,7 @@ class Peer {
if (accepted) { if (accepted) {
this._state = Peer.STATE_RECEIVE_PROCEEDING; this._state = Peer.STATE_RECEIVE_PROCEEDING;
this._busy = true; this._busy = true;
this._acceptedRequest = this._pendingRequest; this._requestAccepted = this._requestPending;
this._lastProgress = 0; this._lastProgress = 0;
this._totalBytesReceived = 0; this._totalBytesReceived = 0;
this._filesReceived = []; this._filesReceived = [];
@ -892,7 +910,7 @@ class Peer {
// While transferring -> round progress to 4th digit. After transferring, set it to 1. // While transferring -> round progress to 4th digit. After transferring, set it to 1.
let progress = this._digester let progress = this._digester
? Math.floor(1e4 * (this._totalBytesReceived + this._digester._bytesReceived) / this._acceptedRequest.totalSize) / 1e4 ? Math.floor(1e4 * (this._totalBytesReceived + this._digester._bytesReceived) / this._requestAccepted.totalSize) / 1e4
: 1; : 1;
Events.fire('set-progress', {peerId: this._peerId, progress: progress, status: 'receive'}); Events.fire('set-progress', {peerId: this._peerId, progress: progress, status: 'receive'});
@ -913,26 +931,34 @@ class Peer {
this._singleFileReceiveComplete(file); this._singleFileReceiveComplete(file);
// If less files received than header accepted -> wait for next file // If less files received than header accepted -> wait for next file
if (this._filesReceived.length < this._acceptedRequest.header.length) return; if (this._filesReceived.length < this._requestAccepted.header.length) return;
// We are done receiving // We are done receiving
Events.fire('set-progress', {peerId: this._peerId, progress: 1, status: 'receive'}); Events.fire('set-progress', {peerId: this._peerId, progress: 1, status: 'receive'});
// If filesType is 'message' evaluate files as text
if (this._requestAccepted.filesType === 'message') {
this._textReceivedAsFile();
return;
}
// fileType is 'images' or 'files'
this._allFilesReceiveComplete(); this._allFilesReceiveComplete();
} }
_fitsAcceptedHeader(header) { _fitsAcceptedHeader(header) {
if (!this._acceptedRequest) { if (!this._requestAccepted) {
return false; return false;
} }
const positionFile = this._filesReceived.length; const positionFile = this._filesReceived.length;
if (positionFile > this._acceptedRequest.header.length - 1) { if (positionFile > this._requestAccepted.header.length - 1) {
return false; return false;
} }
// Check if file header fits // Check if file header fits
const acceptedHeader = this._acceptedRequest.header[positionFile]; const acceptedHeader = this._requestAccepted.header[positionFile];
const sameSize = header.size === acceptedHeader.size; const sameSize = header.size === acceptedHeader.size;
const sameType = header.mime === acceptedHeader.mime; const sameType = header.mime === acceptedHeader.mime;
@ -955,8 +981,11 @@ class Peer {
// Log speed from request to receive // Log speed from request to receive
Logger.log(`File received.\n\nSize: ${size} MB\tDuration: ${duration} s\tSpeed: ${speed} MB/s`); Logger.log(`File received.\n\nSize: ${size} MB\tDuration: ${duration} s\tSpeed: ${speed} MB/s`);
// include for compatibility with 'Snapdrop & PairDrop for Android' app // Prevent App from downloading message txt file
Events.fire('file-received', file); if (this._requestAccepted.filesType !== 'message') {
// include for compatibility with 'Snapdrop & PairDrop for Android' app
Events.fire('file-received', file);
}
this._filesReceived.push(file); this._filesReceived.push(file);
@ -967,39 +996,88 @@ class Peer {
Events.fire('files-received', { Events.fire('files-received', {
peerId: this._peerId, peerId: this._peerId,
files: this._filesReceived, files: this._filesReceived,
imagesOnly: this._acceptedRequest.imagesOnly, filesType: this._requestAccepted.filesType,
totalSize: this._acceptedRequest.totalSize totalSize: this._requestAccepted.totalSize
}); });
this._reset(); this._reset();
} }
// Message Sender Only // Message Sender Only
_sendText(text) {
_base64encode(text) {
return btoa(unescape(encodeURIComponent(text)));
}
async _sendText(text) {
this._state = Peer.STATE_TEXT_SENT; this._state = Peer.STATE_TEXT_SENT;
const unescaped = btoa(unescape(encodeURIComponent(text)));
this._sendMessage({ type: 'text', text: unescaped }); // Send text base64 encoded
const base64encoded = this._base64encode(text);
const message = {type: 'text', text: base64encoded};
// If text too big for connection -> send as file instead
if (JSON.stringify(message).length > this._maxMessageSize) {
await this._sendTextAsFile(text);
return;
}
this._sendMessage(message);
}
async _sendTextAsFile(text) {
// send text in chunks by using the file transfer api
const file = new File([text], "pairdrop-message.txt", { type: 'text/plain' });
await this._sendFileTransferRequest([file], true);
} }
_onTextReceiveComplete() { _onTextReceiveComplete() {
if (this._state !== Peer.STATE_TEXT_SENT) { if (this._state !== Peer.STATE_TEXT_SENT && this._state !== Peer.STATE_TRANSFER_PROCEEDING) {
this._sendState(); this._sendState();
return; return;
} }
this._reset(); this._reset();
Events.fire('set-progress', { peerId: this._peerId, progress: 0, status: 'idle' });
Events.fire('notify-user', Localization.getTranslation("notifications.message-transfer-completed")); Events.fire('notify-user', Localization.getTranslation("notifications.message-transfer-completed"));
} }
// Message Receiver Only // Message Receiver Only
_onText(message) { _base64decodeMessage(base64encoded){
if (!message.text) return; let decoded = "";
try { try {
const escaped = decodeURIComponent(escape(atob(message.text))); decoded = decodeURIComponent(escape(atob(base64encoded)));
Events.fire('text-received', { text: escaped, peerId: this._peerId });
this._sendMessage({ type: 'text-receive-complete' });
} }
catch (e) { catch (e) {
Logger.error(e); Logger.error(e);
} }
return decoded;
}
_onText(message) {
if (this._state !== Peer.STATE_IDLE) {
this._abortTransfer();
return;
}
if (!message.text) return;
const text = this._base64decodeMessage(message.text);
Events.fire('text-received', { text: text, peerId: this._peerId });
this._sendMessage({ type: 'text-receive-complete' });
}
_textReceivedAsFile() {
// Use FileReader to unpack text from file
const reader = new FileReader();
reader.addEventListener("load", _ => {
Events.fire('text-received', { text: reader.result, peerId: this._peerId });
});
reader.readAsText(this._filesReceived[0]);
Events.fire('set-progress', { peerId: this._peerId, progress: 1, status: 'idle' });
this._sendMessage({ type: 'text-receive-complete' });
this._reset();
} }
} }
@ -1114,6 +1192,9 @@ class RTCPeer extends Peer {
_onConnectionStateChange() { _onConnectionStateChange() {
Logger.debug('RTC: Connection state changed:', this._conn.connectionState); Logger.debug('RTC: Connection state changed:', this._conn.connectionState);
switch (this._conn.connectionState) { switch (this._conn.connectionState) {
case 'connected':
this._setMaxMessageSize();
break;
case 'disconnected': case 'disconnected':
this._refresh(); this._refresh();
break; break;
@ -1331,6 +1412,12 @@ class RTCPeer extends Peer {
this._server.send(message); this._server.send(message);
} }
_setMaxMessageSize() {
this._maxMessageSize = this._conn && this._conn.sctp
? this._conn.sctp.maxMessageSize
: 262144; // 256 kB
}
async _sendFile(file) { async _sendFile(file) {
this._chunker = new FileChunkerRTC( this._chunker = new FileChunkerRTC(
file, file,
@ -1389,6 +1476,8 @@ class WSPeer extends Peer {
this.rtcSupported = false; this.rtcSupported = false;
this.signalSuccessful = false; this.signalSuccessful = false;
this._maxMessageSize = 65536; // 64 KB
if (!this._isCaller) return; // we will listen for a caller if (!this._isCaller) return; // we will listen for a caller
this._sendSignal(); this._sendSignal();
@ -1607,8 +1696,8 @@ class PeersManager {
await this.peers[message.to]._sendFileTransferRequest(files); await this.peers[message.to]._sendFileTransferRequest(files);
} }
_onSendText(message) { async _onSendText(message) {
this.peers[message.to]._sendText(message.text); await this.peers[message.to]._sendText(message.text);
} }
_onPeerLeft(message) { _onPeerLeft(message) {
@ -1813,7 +1902,7 @@ class FileChunkerRTC extends FileChunker {
this._chunkSize = peerConnection && peerConnection.sctp this._chunkSize = peerConnection && peerConnection.sctp
? Math.min(peerConnection.sctp.maxMessageSize, 1048576) // 1 MB max ? Math.min(peerConnection.sctp.maxMessageSize, 1048576) // 1 MB max
: 262144; // 256 KB : 262144; // 256 kB
this._peerConnection = peerConnection; this._peerConnection = peerConnection;
this._dataChannel = dataChannel; this._dataChannel = dataChannel;

View file

@ -423,8 +423,8 @@ class PeerUI {
this._connected = false; this._connected = false;
this._currentProgress = 0; this._currentProgress = 0;
this._currentStatus = null this._currentStatus = 'idle';
this._oldStatus = null; this._oldStatus = 'idle';
this._progressQueue = []; this._progressQueue = [];
@ -604,14 +604,14 @@ class PeerUI {
// on reconnect // on reconnect
this.setStatus(this._oldStatus); this.setStatus(this._oldStatus);
this._oldStatus = null; this._oldStatus = 'idle';
this._connectionHash = connectionHash; this._connectionHash = connectionHash;
} }
else { else {
this._connected = false; this._connected = false;
if (!this._oldStatus && this._currentStatus !== "connect") { if (this._oldStatus === 'idle' && this._currentStatus !== "connect") {
// save old status when reconnecting // save old status when reconnecting
this._oldStatus = this._currentStatus; this._oldStatus = this._currentStatus;
} }
@ -787,10 +787,10 @@ class PeerUI {
clearTimeout(this.statusTimeout); clearTimeout(this.statusTimeout);
if (!status) { if (status === 'idle') {
this.$el.removeAttribute('status'); this.$el.removeAttribute('status');
this.$el.querySelector('.status').innerHTML = ''; this.$el.querySelector('.status').innerHTML = '';
this._currentStatus = null; this._currentStatus = 'idle';
return; return;
} }
@ -812,7 +812,7 @@ class PeerUI {
if (["transfer-complete", "receive-complete", "error"].includes(status)) { if (["transfer-complete", "receive-complete", "error"].includes(status)) {
this.statusTimeout = setTimeout(() => { this.statusTimeout = setTimeout(() => {
this.setProgress(0, null); this.setProgress(0, 'idle');
}, 10000); }, 10000);
} }
} }
@ -1031,30 +1031,35 @@ class ReceiveDialog extends Dialog {
} }
} }
_parseFileData(displayName, connectionHash, files, imagesOnly, totalSize, badgeClassName) { _parseFileData(displayName, connectionHash, files, filesType, totalSize, badgeClassName) {
let fileOther = ""; if (filesType === 'message') {
if (files.length === 2) { this.$fileOther.innerText = Localization.getTranslation("dialogs.text-message-description");
fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image")
: Localization.getTranslation("dialogs.file-other-description-file");
} }
else if (files.length > 2) { else {
fileOther = imagesOnly let fileOther = "";
? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1}) if (files.length === 2) {
: Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1}); fileOther = filesType === 'images'
? Localization.getTranslation("dialogs.file-other-description-image")
: Localization.getTranslation("dialogs.file-other-description-file");
}
else if (files.length > 2) {
fileOther = filesType === 'images'
? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1})
: Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1});
}
const fileName = files[0].name;
const fileNameSplit = fileName.split('.');
const fileExtension = '.' + fileNameSplit[fileNameSplit.length - 1];
const fileStem = fileName.substring(0, fileName.length - fileExtension.length);
this.$fileOther.innerText = fileOther;
this.$fileStem.innerText = fileStem;
this.$fileExtension.innerText = fileExtension;
} }
const fileName = files[0].name; this.$fileSize.innerText = this._formatFileSize(totalSize);
const fileNameSplit = fileName.split('.');
const fileExtension = '.' + fileNameSplit[fileNameSplit.length - 1];
const fileStem = fileName.substring(0, fileName.length - fileExtension.length);
const fileSize = this._formatFileSize(totalSize);
this.$fileOther.innerText = fileOther;
this.$fileStem.innerText = fileStem;
this.$fileExtension.innerText = fileExtension;
this.$fileSize.innerText = fileSize;
this.$displayName.innerText = displayName; this.$displayName.innerText = displayName;
this.$displayName.title = connectionHash; this.$displayName.title = connectionHash;
this.$displayName.classList.remove("badge-room-ip", "badge-room-secret", "badge-room-public-id"); this.$displayName.classList.remove("badge-room-ip", "badge-room-secret", "badge-room-public-id");
@ -1070,12 +1075,12 @@ class ReceiveFileDialog extends ReceiveDialog {
this.$downloadBtn = this.$el.querySelector('#download-btn'); this.$downloadBtn = this.$el.querySelector('#download-btn');
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.filesType, e.detail.totalSize));
this._filesDataQueue = []; this._filesDataQueue = [];
} }
async _onFilesReceived(peerId, files, imagesOnly, totalSize) { async _onFilesReceived(peerId, files, filesType, totalSize) {
const descriptor = this._getDescriptor(files, imagesOnly); const descriptor = this._getDescriptor(files, filesType);
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();
@ -1083,7 +1088,7 @@ class ReceiveFileDialog extends ReceiveDialog {
this._filesDataQueue.push({ this._filesDataQueue.push({
peerId: peerId, peerId: peerId,
files: files, files: files,
imagesOnly: imagesOnly, filesType: filesType,
totalSize: totalSize, totalSize: totalSize,
descriptor: descriptor, descriptor: descriptor,
displayName: displayName, displayName: displayName,
@ -1126,7 +1131,7 @@ class ReceiveFileDialog extends ReceiveDialog {
this._data.displayName, this._data.displayName,
this._data.connectionHash, this._data.connectionHash,
this._data.files, this._data.files,
this._data.imagesOnly, this._data.filesType,
this._data.totalSize, this._data.totalSize,
this._data.badgeClassName this._data.badgeClassName
); );
@ -1152,15 +1157,15 @@ class ReceiveFileDialog extends ReceiveDialog {
return window.iOS && this._data.totalSize > 250000000; return window.iOS && this._data.totalSize > 250000000;
} }
_getDescriptor(files, imagesOnly) { _getDescriptor(files, filesType) {
let descriptor; let descriptor;
if (files.length === 1) { if (files.length === 1) {
descriptor = imagesOnly descriptor = filesType === 'images'
? Localization.getTranslation("dialogs.title-image") ? Localization.getTranslation("dialogs.title-image")
: Localization.getTranslation("dialogs.title-file"); : Localization.getTranslation("dialogs.title-file");
} }
else { else {
descriptor = imagesOnly descriptor = filesType === 'images'
? Localization.getTranslation("dialogs.title-image-plural") ? Localization.getTranslation("dialogs.title-image-plural")
: Localization.getTranslation("dialogs.title-file-plural"); : Localization.getTranslation("dialogs.title-file-plural");
} }
@ -1499,15 +1504,17 @@ class ReceiveRequestDialog extends ReceiveDialog {
_showRequestDialog(request, peerId) { _showRequestDialog(request, peerId) {
this.correspondingPeerId = peerId; this.correspondingPeerId = peerId;
const transferRequestTitleTranslation = request.imagesOnly const transferRequestTitleTranslation = request.filesType === 'message'
? Localization.getTranslation('document-titles.image-transfer-requested') ? Localization.getTranslation('document-titles.message-transfer-requested')
: Localization.getTranslation('document-titles.file-transfer-requested'); : request.filesType === 'images'
? Localization.getTranslation('document-titles.image-transfer-requested')
: Localization.getTranslation('document-titles.file-transfer-requested');
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._parseFileData(displayName, connectionHash, request.header, request.imagesOnly, request.totalSize, badgeClassName); this._parseFileData(displayName, connectionHash, request.header, request.filesType, request.totalSize, badgeClassName);
this._addThumbnailToPreviewBox(request.thumbnailDataUrl); this._addThumbnailToPreviewBox(request.thumbnailDataUrl);
this.$receiveTitle.innerText = transferRequestTitleTranslation; this.$receiveTitle.innerText = transferRequestTitleTranslation;
@ -2739,7 +2746,7 @@ class Notifications {
Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId)); Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId));
Events.on('files-received', e => this._downloadNotification(e.detail.files, e.detail.imagesOnly)); Events.on('files-received', e => this._downloadNotification(e.detail.files, e.detail.filesType));
Events.on('files-transfer-request', e => this._requestNotification(e.detail.request, e.detail.peerId)); Events.on('files-transfer-request', e => this._requestNotification(e.detail.request, e.detail.peerId));
// Todo on 'files-transfer-request-abort' remove notification // Todo on 'files-transfer-request-abort' remove notification
} }
@ -2823,31 +2830,26 @@ class Notifications {
} }
_requestNotification(request, peerId) { _requestNotification(request, peerId) {
if (document.visibilityState !== 'visible') { // Do not notify user if page is visible
let imagesOnly = request.header.every(header => header.mime.split('/')[0] === 'image'); if (document.visibilityState === 'visible') return;
let displayName = $(peerId).querySelector('.name').textContent;
let descriptor; const clickToShowTranslation = Localization.getTranslation("notifications.click-to-show");
if (request.header.length === 1) { const displayName = $(peerId).querySelector('.name').textContent;
descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image")
: Localization.getTranslation("dialogs.title-file");
}
else {
descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image-plural")
: Localization.getTranslation("dialogs.title-file-plural");
}
let title = Localization const transferRequestTitleTranslation = request.filesType === 'message'
.getTranslation("notifications.request-title", null, { ? Localization.getTranslation('document-titles.message-transfer-requested')
name: displayName, : request.filesType === 'images'
count: request.header.length, ? Localization.getTranslation('document-titles.image-transfer-requested')
descriptor: descriptor.toLowerCase() : Localization.getTranslation('document-titles.file-transfer-requested');
});
const notification = this._notify(title, Localization.getTranslation("notifications.click-to-show")); let title = Localization
} .getTranslation("notifications.request-title", null, {
name: displayName,
count: request.header.length,
descriptor: transferRequestTitleTranslation.toLowerCase()
});
this._notify(title, clickToShowTranslation);
} }
_download(notification) { _download(notification) {