loginDialog,unlockDialog: Give user time to read messages

Right now, if multiple messages come in, they just sort of
clobber each other.

This commit sets up a message queue, and introduces pauses
long enough for the user to hopefully be able to read those
messages.

https://bugzilla.gnome.org/show_bug.cgi?id=694688
This commit is contained in:
Ray Strode 2013-03-18 00:59:56 -04:00
parent f72f501931
commit d097327bd8
3 changed files with 127 additions and 17 deletions

View File

@ -930,7 +930,7 @@ const LoginDialog = new Lang.Class({
return batch.run(); return batch.run();
}, },
_onSessionOpened: function(client, serviceName) { _startSession: function(serviceName) {
Tweener.addTween(this.dialogLayout, Tweener.addTween(this.dialogLayout,
{ opacity: 0, { opacity: 0,
time: _FADE_ANIMATION_TIME, time: _FADE_ANIMATION_TIME,
@ -953,6 +953,18 @@ const LoginDialog = new Lang.Class({
onCompleteScope: this }); onCompleteScope: this });
}, },
_onSessionOpened: function(client, serviceName) {
if (!this._userVerifier.hasPendingMessages) {
this._startSession(serviceName);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._startSession(serviceName);
}));
}
},
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
let item = this._userList.getItemFromUserName(userName); let item = this._userList.getItemFromUserName(userName);

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Signals = imports.signals; const Signals = imports.signals;
@ -27,6 +28,9 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
const LOGO_KEY = 'logo'; const LOGO_KEY = 'logo';
const DISABLE_USER_LIST_KEY = 'disable-user-list'; const DISABLE_USER_LIST_KEY = 'disable-user-list';
// Give user 16ms to read each character of a PAM message
const USER_READ_TIME = 16
function fadeInActor(actor) { function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible) if (actor.opacity == 255 && actor.visible)
return null; return null;
@ -114,6 +118,9 @@ const ShellUserVerifier = new Lang.Class({
this._fprintManager = new Fprint.FprintManager(); this._fprintManager = new Fprint.FprintManager();
this._realmManager = new Realmd.Manager(); this._realmManager = new Realmd.Manager();
this._messageQueue = [];
this._messageQueueTimeoutId = 0;
this.hasPendingMessages = false;
this._failCounter = 0; this._failCounter = 0;
}, },
@ -153,13 +160,73 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.run_dispose(); this._userVerifier.run_dispose();
this._userVerifier = null; this._userVerifier = null;
} }
this._clearMessageQueue();
}, },
answerQuery: function(serviceName, answer) { answerQuery: function(serviceName, answer) {
// Clear any previous message if (!this._userVerifier.hasPendingMessages) {
this.emit('show-message', null, null); this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
}
},
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); _getIntervalForMessage: function(message) {
// We probably could be smarter here
return message.length * USER_READ_TIME;
},
finishMessageQueue: function() {
if (!this.hasPendingMessages)
return;
this._messageQueue = [];
this.hasPendingMessages = false;
this.emit('no-more-messages');
},
_queueMessageTimeout: function() {
if (this._messageQueue.length == 0) {
this.finishMessageQueue();
return;
}
if (this._messageQueueTimeoutId != 0)
return;
let message = this._messageQueue.shift();
this.emit('show-message', message.text, message.iconName);
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
message.interval,
Lang.bind(this, function() {
this._messageQueueTimeoutId = 0;
this._queueMessageTimeout();
}));
},
_queueMessage: function(message, iconName) {
let interval = this._getIntervalForMessage(message);
this.hasPendingMessages = true;
this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
this._queueMessageTimeout();
},
_clearMessageQueue: function() {
this.finishMessageQueue();
if (this._messageQueueTimeoutId != 0) {
GLib.source_remove(this._messageQueueTimeoutId);
this._messageQueueTimeoutId = 0;
}
this.emit('show-message', null, null);
}, },
_checkForFingerprintReader: function() { _checkForFingerprintReader: function() {
@ -179,7 +246,7 @@ const ShellUserVerifier = new Lang.Class({
logError(error, where); logError(error, where);
this._hold.release(); this._hold.release();
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning'); this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
this._verificationFailed(false); this._verificationFailed(false);
}, },
@ -298,7 +365,7 @@ const ShellUserVerifier = new Lang.Class({
// to indicate the user can swipe their finger instead // to indicate the user can swipe their finger instead
this.emit('show-login-hint', _("(or swipe finger)")); this.emit('show-login-hint', _("(or swipe finger)"));
} else if (serviceName == PASSWORD_SERVICE_NAME) { } else if (serviceName == PASSWORD_SERVICE_NAME) {
this.emit('show-message', info, 'login-dialog-message-info'); this._queueMessage(info, 'login-dialog-message-info');
} }
}, },
@ -307,7 +374,7 @@ const ShellUserVerifier = new Lang.Class({
// users who haven't enrolled their fingerprint. // users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME) if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
this.emit('show-message', problem, 'login-dialog-message-warning'); this._queueMessage(problem, 'login-dialog-message-warning');
}, },
_showRealmLoginHint: function() { _showRealmLoginHint: function() {
@ -356,6 +423,15 @@ const ShellUserVerifier = new Lang.Class({
this.emit('verification-complete'); this.emit('verification-complete');
}, },
_cancelAndReset: function() {
this.cancel();
this._onReset();
},
_retry: function() {
this.begin(this._userName, new Batch.Hold());
},
_verificationFailed: function(retry) { _verificationFailed: function(retry) {
// For Not Listed / enterprise logins, immediately reset // For Not Listed / enterprise logins, immediately reset
// the dialog // the dialog
@ -367,15 +443,25 @@ const ShellUserVerifier = new Lang.Class({
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) { if (canRetry) {
this.clear(); if (!this._userVerifier.hasPendingMessages) {
this.begin(this._userName, new Batch.Hold()); this._retry();
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._retry();
}));
}
} else { } else {
// Allow some time to see the message, then reset everything if (!this._userVerifier.hasPendingMessages) {
Mainloop.timeout_add(3000, Lang.bind(this, function() { this._cancelAndReset();
this.cancel(); } else {
let signalId = this._userVerifier.connect('no-more-messages',
this._onReset(); Lang.bind(this, function() {
})); this._userVerifier.disconnect(signalId);
this._cancelAndReset();
}));
}
} }
this.emit('verification-failed'); this.emit('verification-failed');

View File

@ -250,12 +250,24 @@ const UnlockDialog = new Lang.Class({
this._userVerifier.answerQuery(query, this._promptEntry.text); this._userVerifier.answerQuery(query, this._promptEntry.text);
}, },
_onVerificationComplete: function() { _finishUnlock: function() {
this._userVerified = true;
this._userVerifier.clear(); this._userVerifier.clear();
this.emit('unlocked'); this.emit('unlocked');
}, },
_onVerificationComplete: function() {
this._userVerified = true;
if (!this._userVerifier.hasPendingMessages) {
this._finishUnlock();
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._finishUnlock();
}));
}
},
_onReset: function() { _onReset: function() {
if (!this._userVerified) { if (!this._userVerified) {
this._userVerifier.clear(); this._userVerifier.clear();