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 _ $ */
L.Control.DownloadProgress = L.Control.extend({
options: {
position: 'bottomright'
snackbarTimeout: 20000
},
initialize: function (options) {
L.setOptions(this, options);
},
onAdd: function () {
this._initLayout();
return this._container;
},
// 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);
onAdd: function (map) {
this._map = map;
this._started = false;
this._complete = false;
this._closed = false;
},
show: function () {
@ -73,8 +26,11 @@ L.Control.DownloadProgress = L.Control.extend({
this._started = false;
this._complete = 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 () {
@ -88,11 +44,11 @@ L.Control.DownloadProgress = L.Control.extend({
currentStatus: function () {
if (this._closed)
return 'closed';
if (this._content.contains(this._downloadButton))
if (!this._started && !this._complete)
return 'downloadButton';
if (this._content.contains(this._progress))
if (this._started)
return 'progress';
if (this._content.contains(this._confirmPasteButton))
if (this._complete)
return 'confirmPasteButton';
},
@ -102,8 +58,7 @@ L.Control.DownloadProgress = L.Control.extend({
},
setValue: function (value) {
this._bar.style.width = Math.round(value) + '%';
this._value.innerHTML = Math.round(value) + '%';
this._map.uiManager.setSnackbarProgress(Math.round(value));
},
_setProgressCursor: function() {
@ -117,9 +72,9 @@ L.Control.DownloadProgress = L.Control.extend({
startProgressMode: function() {
this._setProgressCursor();
this._started = true;
this._map.uiManager.showProgressBar(
'Downloading clipboard content', 'Cancel', this._onClose.bind(this));
this.setValue(0);
this._content.removeChild(this._downloadButton);
this._content.appendChild(this._progress);
},
_onStartDownload: function () {
@ -143,10 +98,12 @@ L.Control.DownloadProgress = L.Control.extend({
return;
this._setNormalCursor();
this._complete = true;
if (this._content.contains(this._progress))
this._content.removeChild(this._progress);
this._content.style.width = '150px';
this._content.appendChild(this._confirmPasteButton);
this._started = false;
var msg = _('Confirm copy to clipboard');
// TODO: on Mac Alt == Option
this._map.uiManager.showSnackbar(msg, _('Confirm') + ' (Alt + C)',
this._onConfirmCopyAction.bind(this), this.options.snackbarTimeout);
},
_onConfirmCopyAction: function () {
@ -155,16 +112,17 @@ L.Control.DownloadProgress = L.Control.extend({
},
_onClose: function () {
if (this._userAlreadyWarned())
this._map.uiManager.closeSnackbar();
this._started = false;
this._complete = false;
this._closed = true;
this._setNormalCursor();
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)
this._map.focus();
this.remove();
this._closed = true;
},
_download: function () {
@ -186,7 +144,12 @@ L.Control.DownloadProgress = L.Control.extend({
// TODO: failure to parse ? ...
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
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) {
if (!app.socket)
return;
var closeJson = {
id: 'snackbar',
jsontype: 'dialog',
type: 'snackbar',
action: 'fadeout'
};
app.socket._onMessage({textMsg: 'jsdialog: ' + JSON.stringify(closeJson)});
this.closeSnackbar();
var json = {
id: 'snackbar',
@ -948,6 +946,7 @@ L.Control.UIManager = L.Control.extend({
]
};
var that = this;
var builderCallback = function(objectType, eventType, object, 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)
callback();
var closeMessage = { id: 'snackbar', jsontype: 'dialog', type: 'snackbar', action: 'close' };
app.socket._onMessage({ textMsg: 'jsdialog: ' + JSON.stringify(closeMessage) });
that.closeSnackbar();
}
};

View file

@ -207,7 +207,7 @@ L.Clipboard = L.Class.extend({
var request = new XMLHttpRequest();
// 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._downloadProgress.startProgressMode();
}
@ -802,33 +802,31 @@ L.Clipboard = L.Class.extend({
_startProgress: function() {
if (!this._downloadProgress) {
this._downloadProgress = L.control.downloadProgress();
}
if (!this._downloadProgress.isVisible()) {
this._downloadProgress.addTo(this._map);
this._map.addControl(this._downloadProgress);
}
this._downloadProgress.show();
},
_onDownloadOnLargeCopyPaste: function () {
if (!this._downloadProgress || this._downloadProgress.isClosed()) {
this._warnFirstLargeCopyPaste();
this._startProgress();
}
else if (this._downloadProgress.isStarted()) {
if (this._downloadProgress && this._downloadProgress.isStarted()) {
// 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.
this._warnLargeCopyPasteAlreadyStarted();
}
else {
this._warnFirstLargeCopyPaste();
this._startProgress();
}
},
_downloadProgressStatus: function() {
if (this._downloadProgress && this._downloadProgress.isVisible())
if (this._downloadProgress)
return this._downloadProgress.currentStatus();
},
// Download button is still shown after selection changed -> user has changed their mind...
_scheduleHideDownload: function(s) {
if (!this._downloadProgress || !this._downloadProgress.isVisible())
if (!this._downloadProgress || this._downloadProgress.isClosed())
return;
// If no other copy/paste things occurred then ...
@ -847,9 +845,7 @@ L.Clipboard = L.Class.extend({
clearTimeout(this._hideDownloadTimer);
this._hideDownloadTimer = null;
if (!this._downloadProgress ||
!this._downloadProgress.isVisible() ||
this._downloadProgress.isClosed())
if (!this._downloadProgress || this._downloadProgress.isClosed())
return;
this._downloadProgress._onClose();
},