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(
* Show an error message telling the user that the access token has expired.
*/
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 () {
it('shows an error message to the user', function () {
return assertThatAccessTokenPromiseWasRejectedAnd(function () {
assert.calledOnce(fakeToastMessenger.error);
assert.equal(
fakeToastMessenger.error.firstCall.args[0],
'Hypothesis login lost: You must reload the page to annotate.'
assert.calledWith(
fakeToastMessenger.error,
'Hypothesis login lost: You must reload the page to annotate.',
{
autoDismiss: false,
}
);
});
});
......
......@@ -100,6 +100,19 @@ describe('toastMessenger', function () {
assert.calledOnce(fakeStore.getToastMessage);
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', () => {
......
......@@ -10,6 +10,14 @@ const MESSAGE_DISPLAY_TIME = 5000;
// Delay before removing the message entirely (allows animations to complete)
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
export default function toastMessenger(store) {
/**
......@@ -38,8 +46,9 @@ export default function toastMessenger(store) {
*
* @param {('error'|'success')} type
* @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)
if (store.hasToastMessage(type, message)) {
return;
......@@ -49,26 +58,34 @@ export default function toastMessenger(store) {
store.addToastMessage({ type, message, id, isDismissed: false });
if (autoDismiss) {
// 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
// presence.
setTimeout(() => {
dismiss(id);
}, MESSAGE_DISPLAY_TIME);
}
};
/**
* Add an error toast message with `messageText`
*
* @param {string} messageText
* @param {MessageOptions} options
*/
const error = messageText => {
addMessage('error', messageText);
const error = (messageText, options) => {
addMessage('error', messageText, options);
};
/**
* Add a success toast message with `messageText`
*
* @param {string} messageText
* @param {MessageOptions} options
*/
const success = messageText => {
addMessage('success', messageText);
const success = (messageText, options) => {
addMessage('success', messageText, options);
};
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