Commit 4696abaa authored by Robert Knight's avatar Robert Knight

Add option to prevent auto-dismissal of certain toasts

For certain error messages, we do not want the notice to be dismissed
automatically as either the issue prevents further usage of the client
(eg. a problem acquiring an access token when third-party accounts are
in use) or the problem is likely to happen while the user is not
interacting with the client.

Add an `autoDismiss` option to `toastMessenger` message methods and use
it when handling access token fetching errors on pages that use
third-party accounts.
parent 0a353103
...@@ -57,7 +57,9 @@ export default function auth( ...@@ -57,7 +57,9 @@ export default function auth(
* Show an error message telling the user that the access token has expired. * Show an error message telling the user that the access token has expired.
*/ */
function showAccessTokenExpiredErrorMessage(message) { function showAccessTokenExpiredErrorMessage(message) {
toastMessenger.error(`Hypothesis login lost: ${message}`); toastMessenger.error(`Hypothesis login lost: ${message}`, {
autoDismiss: false,
});
} }
/** /**
......
...@@ -155,9 +155,12 @@ describe('sidebar.oauth-auth', function () { ...@@ -155,9 +155,12 @@ describe('sidebar.oauth-auth', function () {
it('shows an error message to the user', function () { it('shows an error message to the user', function () {
return assertThatAccessTokenPromiseWasRejectedAnd(function () { return assertThatAccessTokenPromiseWasRejectedAnd(function () {
assert.calledOnce(fakeToastMessenger.error); assert.calledOnce(fakeToastMessenger.error);
assert.equal( assert.calledWith(
fakeToastMessenger.error.firstCall.args[0], fakeToastMessenger.error,
'Hypothesis login lost: You must reload the page to annotate.' 'Hypothesis login lost: You must reload the page to annotate.',
{
autoDismiss: false,
}
); );
}); });
}); });
......
...@@ -100,6 +100,19 @@ describe('toastMessenger', function () { ...@@ -100,6 +100,19 @@ describe('toastMessenger', function () {
assert.calledOnce(fakeStore.getToastMessage); assert.calledOnce(fakeStore.getToastMessage);
assert.notCalled(fakeStore.updateToastMessage); assert.notCalled(fakeStore.updateToastMessage);
}); });
it('does not dismiss the message if `autoDismiss` is false', () => {
fakeStore.hasToastMessage.returns(false);
fakeStore.getToastMessage.returns(undefined);
service.error('boo', { autoDismiss: false });
// Move to the first scheduled timeout.
clock.next();
assert.notCalled(fakeStore.getToastMessage);
assert.notCalled(fakeStore.updateToastMessage);
});
}); });
describe('#dismiss', () => { describe('#dismiss', () => {
......
...@@ -10,6 +10,14 @@ const MESSAGE_DISPLAY_TIME = 5000; ...@@ -10,6 +10,14 @@ const MESSAGE_DISPLAY_TIME = 5000;
// Delay before removing the message entirely (allows animations to complete) // Delay before removing the message entirely (allows animations to complete)
const MESSAGE_DISMISS_DELAY = 500; const MESSAGE_DISMISS_DELAY = 500;
/**
* Additional control over the display of a particular message.
*
* @typedef {Object} MessageOptions
* @prop {boolean} autoDismiss - Whether the toast message automatically disappears.
* Defaults to true.
*/
// @ngInject // @ngInject
export default function toastMessenger(store) { export default function toastMessenger(store) {
/** /**
...@@ -38,8 +46,9 @@ export default function toastMessenger(store) { ...@@ -38,8 +46,9 @@ export default function toastMessenger(store) {
* *
* @param {('error'|'success')} type * @param {('error'|'success')} type
* @param {string} message - The message to be rendered * @param {string} message - The message to be rendered
* @param {MessageOptions} [options]
*/ */
const addMessage = (type, message) => { const addMessage = (type, message, { autoDismiss = true } = {}) => {
// Do not add duplicate messages (messages with the same type and text) // Do not add duplicate messages (messages with the same type and text)
if (store.hasToastMessage(type, message)) { if (store.hasToastMessage(type, message)) {
return; return;
...@@ -49,26 +58,34 @@ export default function toastMessenger(store) { ...@@ -49,26 +58,34 @@ export default function toastMessenger(store) {
store.addToastMessage({ type, message, id, isDismissed: false }); store.addToastMessage({ type, message, id, isDismissed: false });
if (autoDismiss) {
// Attempt to dismiss message after a set time period. NB: The message may // Attempt to dismiss message after a set time period. NB: The message may
// have been removed by other mechanisms at this point; do not assume its // have been removed by other mechanisms at this point; do not assume its
// presence. // presence.
setTimeout(() => { setTimeout(() => {
dismiss(id); dismiss(id);
}, MESSAGE_DISPLAY_TIME); }, MESSAGE_DISPLAY_TIME);
}
}; };
/** /**
* Add an error toast message with `messageText` * Add an error toast message with `messageText`
*
* @param {string} messageText
* @param {MessageOptions} options
*/ */
const error = messageText => { const error = (messageText, options) => {
addMessage('error', messageText); addMessage('error', messageText, options);
}; };
/** /**
* Add a success toast message with `messageText` * Add a success toast message with `messageText`
*
* @param {string} messageText
* @param {MessageOptions} options
*/ */
const success = messageText => { const success = (messageText, options) => {
addMessage('success', messageText); addMessage('success', messageText, options);
}; };
return { return {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment