clipboard: use snackbar for interaction with user

Instead of leaflet popup we use now snackbar.
- control is not removed and added again for next copy-paste
  but we keep single instance for all the time
- added download error message
- simplified close snackbar code

Signed-off-by: Szymon Kłos <szymon.klos@collabora.com>
Change-Id: I804a82c4f589b029a42fc2800958ff2b46b7df50
This commit is contained in:
Szymon Kłos 2023-10-04 19:41:42 +02:00 committed by pedropintosilva
parent 585e00a1ee
commit 689c6fa063
3 changed files with 54 additions and 97 deletions

View file

@ -5,65 +5,18 @@
/* global _ $ */ /* global _ $ */
L.Control.DownloadProgress = L.Control.extend({ L.Control.DownloadProgress = L.Control.extend({
options: { options: {
position: 'bottomright' snackbarTimeout: 20000
}, },
initialize: function (options) { initialize: function (options) {
L.setOptions(this, options); L.setOptions(this, options);
}, },
onAdd: function () { onAdd: function (map) {
this._initLayout(); this._map = map;
return this._container; this._started = false;
}, this._complete = false;
this._closed = false;
// we really don't want mouse and other events propagating
// to the parent map - since they affect the context.
_ignoreEvents: function(elem) {
L.DomEvent.on(elem, 'mousedown mouseup mouseover mouseout mousemove',
function(e) {
L.DomEvent.stopPropagation(e);
return false;
}, this);
},
_initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-control-layers');
this._container.style.visibility = 'hidden';
this._ignoreEvents(this._container);
var closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button download-popup', this._container);
closeButton.href = '#close';
closeButton.innerHTML = '&#215;';
L.DomEvent.on(closeButton, 'click', this._onClose, this);
var wrapper = L.DomUtil.create('div', 'leaflet-popup-content-wrapper', this._container);
var content = this._content = L.DomUtil.create('div', 'leaflet-popup-content', wrapper);
content.style.width = '100px';
// start download button
var startDownload = this._downloadButton = document.createElement('a');
startDownload.href = '#start';
startDownload.innerHTML = _('Start download');
startDownload.style.font = '13px/11px Tahoma, Verdana, sans-serif';
startDownload.style.alignment = 'center';
startDownload.style.height = 20 + 'px';
L.DomEvent.on(startDownload, 'click', this._onStartDownload, this);
// download progress bar
this._progress = L.DomUtil.create('div', 'leaflet-paste-progress', null);
this._bar = L.DomUtil.create('span', '', this._progress);
this._value = L.DomUtil.create('span', '', this._bar);
L.DomUtil.setStyle(this._value, 'line-height', '14px');
// confirm button
var confirmCopy = this._confirmPasteButton = document.createElement('a');
confirmCopy.href = '#complete';
confirmCopy.innerHTML = _('Confirm copy to clipboard');
confirmCopy.style.font = '13px/11px Tahoma, Verdana, sans-serif';
confirmCopy.style.alignment = 'center';
confirmCopy.style.height = 20 + 'px';
L.DomEvent.on(confirmCopy, 'click', this._onConfirmCopyAction, this);
}, },
show: function () { show: function () {
@ -73,8 +26,11 @@ L.Control.DownloadProgress = L.Control.extend({
this._started = false; this._started = false;
this._complete = false; this._complete = false;
this._closed = false; this._closed = false;
this._content.appendChild(this._downloadButton);
this._container.style.visibility = ''; var msg = _('Start download') + ' (Alt + C)'; // TODO: on Mac Alt == Option
this._map.uiManager.showSnackbar(
_('To copy outside, you have to download the content'),
msg, this._onStartDownload.bind(this), this.options.snackbarTimeout);
}, },
isClosed: function () { isClosed: function () {
@ -88,11 +44,11 @@ L.Control.DownloadProgress = L.Control.extend({
currentStatus: function () { currentStatus: function () {
if (this._closed) if (this._closed)
return 'closed'; return 'closed';
if (this._content.contains(this._downloadButton)) if (!this._started && !this._complete)
return 'downloadButton'; return 'downloadButton';
if (this._content.contains(this._progress)) if (this._started)
return 'progress'; return 'progress';
if (this._content.contains(this._confirmPasteButton)) if (this._complete)
return 'confirmPasteButton'; return 'confirmPasteButton';
}, },
@ -102,8 +58,7 @@ L.Control.DownloadProgress = L.Control.extend({
}, },
setValue: function (value) { setValue: function (value) {
this._bar.style.width = Math.round(value) + '%'; this._map.uiManager.setSnackbarProgress(Math.round(value));
this._value.innerHTML = Math.round(value) + '%';
}, },
_setProgressCursor: function() { _setProgressCursor: function() {
@ -117,9 +72,9 @@ L.Control.DownloadProgress = L.Control.extend({
startProgressMode: function() { startProgressMode: function() {
this._setProgressCursor(); this._setProgressCursor();
this._started = true; this._started = true;
this._map.uiManager.showProgressBar(
'Downloading clipboard content', 'Cancel', this._onClose.bind(this));
this.setValue(0); this.setValue(0);
this._content.removeChild(this._downloadButton);
this._content.appendChild(this._progress);
}, },
_onStartDownload: function () { _onStartDownload: function () {
@ -143,10 +98,12 @@ L.Control.DownloadProgress = L.Control.extend({
return; return;
this._setNormalCursor(); this._setNormalCursor();
this._complete = true; this._complete = true;
if (this._content.contains(this._progress)) this._started = false;
this._content.removeChild(this._progress);
this._content.style.width = '150px'; var msg = _('Confirm copy to clipboard');
this._content.appendChild(this._confirmPasteButton); // TODO: on Mac Alt == Option
this._map.uiManager.showSnackbar(msg, _('Confirm') + ' (Alt + C)',
this._onConfirmCopyAction.bind(this), this.options.snackbarTimeout);
}, },
_onConfirmCopyAction: function () { _onConfirmCopyAction: function () {
@ -155,16 +112,17 @@ L.Control.DownloadProgress = L.Control.extend({
}, },
_onClose: function () { _onClose: function () {
if (this._userAlreadyWarned())
this._map.uiManager.closeSnackbar();
this._started = false;
this._complete = false;
this._closed = true;
this._setNormalCursor(); this._setNormalCursor();
this._cancelDownload(); this._cancelDownload();
if (this._content.contains(this._confirmPasteButton))
this._content.removeChild(this._confirmPasteButton);
if (this._content.contains(this._progress))
this._content.removeChild(this._progress);
if (this._map) if (this._map)
this._map.focus(); this._map.focus();
this.remove();
this._closed = true;
}, },
_download: function () { _download: function () {
@ -186,7 +144,12 @@ L.Control.DownloadProgress = L.Control.extend({
// TODO: failure to parse ? ... // TODO: failure to parse ? ...
reader.readAsText(response); reader.readAsText(response);
}, },
function(progress) { return progress/2; } function(progress) { return progress/2; },
function () {
that._onClose();
that._map.uiManager.showSnackbar(
_('Download failed'), '', null,this.options.snackbarTimeout);
}
); );
}, },

View file

@ -917,18 +917,16 @@ L.Control.UIManager = L.Control.extend({
// Snack bar // Snack bar
closeSnackbar: function() {
var closeMessage = { id: 'snackbar', jsontype: 'dialog', type: 'snackbar', action: 'close' };
app.socket._onMessage({ textMsg: 'jsdialog: ' + JSON.stringify(closeMessage) });
},
showSnackbar: function(label, action, callback, timeout, hasProgress) { showSnackbar: function(label, action, callback, timeout, hasProgress) {
if (!app.socket) if (!app.socket)
return; return;
var closeJson = { this.closeSnackbar();
id: 'snackbar',
jsontype: 'dialog',
type: 'snackbar',
action: 'fadeout'
};
app.socket._onMessage({textMsg: 'jsdialog: ' + JSON.stringify(closeJson)});
var json = { var json = {
id: 'snackbar', id: 'snackbar',
@ -948,6 +946,7 @@ L.Control.UIManager = L.Control.extend({
] ]
}; };
var that = this;
var builderCallback = function(objectType, eventType, object, data) { var builderCallback = function(objectType, eventType, object, data) {
window.app.console.debug('control: \'' + objectType + '\' id:\'' + object.id + '\' event: \'' + eventType + '\' state: \'' + data + '\''); window.app.console.debug('control: \'' + objectType + '\' id:\'' + object.id + '\' event: \'' + eventType + '\' state: \'' + data + '\'');
@ -955,8 +954,7 @@ L.Control.UIManager = L.Control.extend({
if (callback) if (callback)
callback(); callback();
var closeMessage = { id: 'snackbar', jsontype: 'dialog', type: 'snackbar', action: 'close' }; that.closeSnackbar();
app.socket._onMessage({ textMsg: 'jsdialog: ' + JSON.stringify(closeMessage) });
} }
}; };

View file

@ -207,7 +207,7 @@ L.Clipboard = L.Class.extend({
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
// avoid to invoke the following code if the download widget depends on user interaction // avoid to invoke the following code if the download widget depends on user interaction
if (!that._downloadProgress || !that._downloadProgress.isVisible()) { if (!that._downloadProgress || that._downloadProgress.isClosed()) {
that._startProgress(); that._startProgress();
that._downloadProgress.startProgressMode(); that._downloadProgress.startProgressMode();
} }
@ -802,33 +802,31 @@ L.Clipboard = L.Class.extend({
_startProgress: function() { _startProgress: function() {
if (!this._downloadProgress) { if (!this._downloadProgress) {
this._downloadProgress = L.control.downloadProgress(); this._downloadProgress = L.control.downloadProgress();
} this._map.addControl(this._downloadProgress);
if (!this._downloadProgress.isVisible()) {
this._downloadProgress.addTo(this._map);
} }
this._downloadProgress.show(); this._downloadProgress.show();
}, },
_onDownloadOnLargeCopyPaste: function () { _onDownloadOnLargeCopyPaste: function () {
if (!this._downloadProgress || this._downloadProgress.isClosed()) { if (this._downloadProgress && this._downloadProgress.isStarted()) {
this._warnFirstLargeCopyPaste();
this._startProgress();
}
else if (this._downloadProgress.isStarted()) {
// Need to show this only when a download is really in progress and we block it. // Need to show this only when a download is really in progress and we block it.
// Otherwise, it's easier to flash the widget or something. // Otherwise, it's easier to flash the widget or something.
this._warnLargeCopyPasteAlreadyStarted(); this._warnLargeCopyPasteAlreadyStarted();
} }
else {
this._warnFirstLargeCopyPaste();
this._startProgress();
}
}, },
_downloadProgressStatus: function() { _downloadProgressStatus: function() {
if (this._downloadProgress && this._downloadProgress.isVisible()) if (this._downloadProgress)
return this._downloadProgress.currentStatus(); return this._downloadProgress.currentStatus();
}, },
// Download button is still shown after selection changed -> user has changed their mind... // Download button is still shown after selection changed -> user has changed their mind...
_scheduleHideDownload: function(s) { _scheduleHideDownload: function(s) {
if (!this._downloadProgress || !this._downloadProgress.isVisible()) if (!this._downloadProgress || this._downloadProgress.isClosed())
return; return;
// If no other copy/paste things occurred then ... // If no other copy/paste things occurred then ...
@ -847,9 +845,7 @@ L.Clipboard = L.Class.extend({
clearTimeout(this._hideDownloadTimer); clearTimeout(this._hideDownloadTimer);
this._hideDownloadTimer = null; this._hideDownloadTimer = null;
if (!this._downloadProgress || if (!this._downloadProgress || this._downloadProgress.isClosed())
!this._downloadProgress.isVisible() ||
this._downloadProgress.isClosed())
return; return;
this._downloadProgress._onClose(); this._downloadProgress._onClose();
}, },