Your IP : 216.73.216.138


Current Path : /home/ereika83/www/wp-content/plugins/wp-hummingbird/_src/js/modules/
Upload File :
Current File : /home/ereika83/www/wp-content/plugins/wp-hummingbird/_src/js/modules/admin-notifications.js

/* global WPHB_Admin */
/* global SUI */
/* global ajaxurl */
/* global wphb */
/* global _ */
/* global wphbMixPanel */

/**
 * External dependencies
 */
import 'core-js/features/array/find-index';

/**
 * Internal dependencies
 */
import HBFetcher from '../utils/fetcher';
import { getString } from '../utils/helpers';

/**
 * Notifications module.
 *
 * @since 3.1.1
 */
( function( $ ) {
	'use strict';

	WPHB_Admin.notifications = {
		module: 'notifications',
		exclude: [],
		edit: false,
		settings: {
			view: 'schedule',
			module: '',
			type: '',
			schedule: {
				frequency: 7,
				time: '',
				weekDay: '',
				monthDay: '',
				threshold: 0,
			},
			recipients: [],
			performance: {
				device: 'both',
				metrics: true,
				audits: true,
				fieldData: true,
			},
			uptime: {
				showPing: true,
			},
			database: {
				revisions: true,
				drafts: true,
				trash: true,
				spam: true,
				trashComment: true,
				expiredTransients: true,
				transients: false,
			},
		},
		moduleData: {},

		/**
		 * Initialize the module.
		 *
		 * @since 3.1.1
		 * @param {Object} settings
		 * @return {WPHB_Admin.notifications}  Notifications module.
		 */
		init( settings ) {
			this.moduleData = settings;

			$( '.wphb-disable-notification' ).on( 'click', this.disable );
			$( '.wphb-enable-notification' ).on( 'click', ( e ) =>
				this.renderTemplate( e, 'add' )
			);

			$( '.wphb-configure-notification' ).on( 'click', ( e ) =>
				this.renderTemplate( e, 'edit' )
			);

			this.maybeOpenModal();

			return this;
		},

		/**
		 * Handle opening modals from other pages via hash.
		 *
		 * @since 3.2.0
		 */
		maybeOpenModal() {
			let hash = window.location.hash;
			if ( 0 === hash.length ) {
				return;
			}

			hash = hash.substring( 1 );
			hash = hash.split( '-' );
			if ( 2 !== hash.length ) {
				return;
			}

			let el = $(
				'button.wphb-configure-notification[data-id="' +
					hash[ 0 ] +
					'"][data-type="' +
					hash[ 1 ] +
					'"]'
			);

			if ( 0 === el.length ) {
				el = $(
					'.wphb-enable-notification[data-id="' +
						hash[ 0 ] +
						'"][data-type="' +
						hash[ 1 ] +
						'"]'
				);
			}

			if ( 0 !== el.length ) {
				el.trigger( 'click' );
			}
		},

		/**
		 * Render template.
		 *
		 * @since 3.1.1
		 * @param {Object} e
		 * @param {string} id Template ID.
		 */
		renderTemplate( e, id ) {
			e.preventDefault();

			this.settings.module = e.currentTarget.dataset.id;
			this.settings.type = e.currentTarget.dataset.type;

			const view = e.currentTarget.dataset.view;
			this.settings.view = 'schedule';
			if ( 'edit' === id && 'recipients' === view ) {
				this.settings.view = view;
			}

			// Just in case - make sure we have the correct data.
			if ( this.moduleData.hasOwnProperty( this.settings.type ) ) {
				let data = this.moduleData[ this.settings.type ];

				if ( data.hasOwnProperty( this.settings.module ) ) {
					data = data[ this.settings.module ];

					// Add schedule.
					if ( data.hasOwnProperty( 'schedule' ) ) {
						this.settings.schedule = data.schedule;
					}

					// Add settings.
					if ( data.hasOwnProperty( 'settings' ) ) {
						this.settings[ this.settings.module ] = data.settings;
					}

					if ( data.hasOwnProperty( 'recipients' ) ) {
						this.settings.recipients = data.recipients;
						// Populate exclude IDs list based on user IDs.
						this.exclude = data.recipients.reduce(
							( o, r ) => (
								0 < r.id && o.push( parseInt( r.id ) ), o
							),
							[]
						);
					}
				}
			}

			this.loadUsers();

			const template = WPHB_Admin.notifications.template(
				id + '-notifications-content'
			);
			const content = template( this.settings );

			if ( content ) {
				$( '#notification-modal' ).html( content );

				this.initSUI();
				this.mapActions();

				if ( 'edit' === id ) {
					this.edit = true;
					this.addSelections();
				} else {
					this.toggleUserNotice();
				}

				SUI.openModal( 'notification-modal', $( this ) );
			}
		},

		/**
		 * Ajax call to fetch load 10 WordPress users.
		 */
		loadUsers() {
			const self = this;

			HBFetcher.notifications
				.getUsers( this.exclude )
				.then( ( response ) => {
					if ( 'undefined' === typeof response ) {
						return;
					}

					let i = 0;
					response.forEach( function( user ) {
						self.addToUsersList( user, 0 === i++ );
					} );

					// Fix overflow for user selectors.
					this.fixRecipientCSS( $( '#modal-wp-user-list' ) );
				} )
				.catch( ( error ) => {
					window.console.log( error );
				} );
		},

		/**
		 * Process schedule selections.
		 *
		 * @since 3.1.1
		 */
		processScheduleSettings() {
			const threshold =
				'uptime' === this.settings.module &&
				'notifications' === this.settings.type;

			if ( threshold ) {
				const select = $( 'select#report-threshold' );
				this.settings.schedule.threshold = select.val();
			} else {
				const frequency = $( 'input[name="report-frequency"]:checked' );
				this.settings.schedule = {
					frequency: frequency.val(),
					time: $( 'select#report-time' ).val(),
					weekDay: $( 'select#report-day' ).val(),
					monthDay: $( 'select#report-day-month' ).val(),
					threshold: '',
				};
			}
		},

		/**
		 * Process additional settings tab.
		 *
		 * @since 3.1.1
		 */
		processAdditionalSettings() {
			if ( 'reports' !== this.settings.type ) {
				return;
			}

			if ( 'performance' === this.settings.module ) {
				const device = $( 'input[name="report-type"]:checked' ).val();
				const metrics = $( 'input#metrics' ).is( ':checked' );
				const audits = $( 'input#audits' ).is( ':checked' );
				const fieldData = $( 'input#field-data' ).is( ':checked' );

				this.settings.performance = {
					device,
					metrics,
					audits,
					fieldData,
				};

				return;
			}

			if ( 'uptime' === this.settings.module ) {
				const showPing = $( 'input#show_ping' ).is( ':checked' );
				this.settings.uptime = { showPing };
				return;
			}

			if ( 'database' === this.settings.module ) {
				const revisions = $( 'input#revisions' ).is( ':checked' );
				const drafts = $( 'input#drafts' ).is( ':checked' );
				const trash = $( 'input#trash' ).is( ':checked' );
				const spam = $( 'input#spam' ).is( ':checked' );
				const trashComment = $( 'input#trashComment' ).is( ':checked' );
				const expiredTransients = $( 'input#expiredTransients' ).is( ':checked' );
				const transients = $( 'input#transients' ).is( ':checked' );

				this.settings.database = {
					revisions,
					drafts,
					trash,
					spam,
					trashComment,
					expiredTransients,
					transients,
				};
			}
		},

		/**
		 * Update settings in reports (during activate and edit).
		 *
		 * @since 3.1.1
		 * @param {boolean} processSettings
		 */
		update( processSettings = false ) {
			const btn = event.target;
			btn.classList.add( 'sui-button-onload-text' );

			this.processScheduleSettings();
			if ( processSettings ) {
				this.processAdditionalSettings();
			}

			HBFetcher.notifications
				.enable( this.settings, this.edit )
				.then( ( response ) => {
					history.pushState( "", document.title, window.location.pathname + window.location.search );
					window.location.search += '&status=' + response.code;
				} )
				.catch( ( error ) => {
					window.console.log( error );
				} );
		},

		/**
		 * Activate the reports module.
		 *
		 * @since 3.1.1
		 * @param {boolean} processSettings
		 */
		activate( processSettings = false ) {
			const moduleName = this.getModuleName();
			if ( '' !== moduleName ) {
				wphbMixPanel.enableFeature( moduleName );
			}

			this.update( processSettings );
		},

		/**
		 * Map modal actions.
		 *
		 * @since 3.1.1
		 */
		mapActions() {
			this.initUserSelects();
			this.toggleAddButton();

			$( '#add-recipient-button' ).on( 'click', () => {
				this.handleAddButtonClick();
			} );

			const frequency = $( 'input[name="report-frequency"]' );
			frequency.on( 'change', this.handleFrequencySelect );
		},

		/**
		 * When editing notifications, make sure we have the proper selections for schedule data.
		 *
		 * @since 3.1.1
		 */
		addSelections() {
			$( '#report-time' )
				.val( this.settings.schedule.time )
				.trigger( 'change' );

			if ( 7 === this.settings.schedule.frequency ) {
				$( '#report-day' )
					.val( this.settings.schedule.weekDay )
					.trigger( 'change' );
			}

			if ( 30 === this.settings.schedule.frequency ) {
				$( '#report-day-month' )
					.val( this.settings.schedule.monthDay )
					.trigger( 'change' );
			}

			$( '#report-threshold' )
				.val( this.settings.schedule.threshold )
				.trigger( 'change' );
		},

		/**
		 * Due to how we load the template, we need to re-initialize the SUI related modules.
		 *
		 * @since 3.1.1
		 */
		initSUI() {
			$( '.sui-select' ).each( function() {
				const select = $( this );
				if ( 'icon' === select.data( 'theme' ) ) {
					SUI.select.initIcon( select );
				} else if ( 'color' === select.data( 'theme' ) ) {
					SUI.select.initColor( select );
				} else if ( 'search' === select.data( 'theme' ) ) {
					SUI.select.initSearch( select );
				} else {
					SUI.select.init( select );
				}
			} );

			SUI.modalDialog();
			SUI.tabs();
			SUI.notice();

			$( '.sui-side-tabs label.sui-tab-item input' ).each( function() {
				SUI.sideTabs( this );
			} );
		},

		/**
		 * Process frequency change in modal.
		 *
		 * @since 3.1.1
		 */
		handleFrequencySelect() {
			const freq = $( this ).val();
			const scheduleBox = $( '.schedule-box' );

			const weekDiv = scheduleBox.find( '[data-type="week"]' );
			const monthDiv = scheduleBox.find( '[data-type="month"]' );

			weekDiv.toggleClass( 'sui-hidden', '30' === freq || '1' === freq );
			monthDiv.toggleClass( 'sui-hidden', '7' === freq || '1' === freq );
		},

		/**
		 * Get module name string for tracking.
		 * Do not translate these strings, they are used in Mixpanel tracking.
		 *
		 * @since 3.1.1
		 * @return {string}  Module name.
		 */
		getModuleName() {
			let moduleName = '';

			if (
				'performance' === this.settings.module &&
				'reports' === this.settings.type
			) {
				moduleName = 'Performance Reports';
			} else if (
				'uptime' === this.settings.module &&
				'reports' === this.settings.type
			) {
				moduleName = 'Uptime Reports';
			} else if (
				'uptime' === this.settings.module &&
				'notifications' === this.settings.type
			) {
				moduleName = 'Uptime Notifications';
			}

			return moduleName;
		},

		/**
		 * Disable notification.
		 *
		 * @since 3.1.1
		 */
		disable() {
			event.preventDefault();

			const id = event.target.dataset.id;
			const type = event.target.dataset.type;

			if ( 'undefined' === typeof id || 'undefined' === typeof type ) {
				return;
			}

			const moduleName = WPHB_Admin.notifications.getModuleName();
			if ( '' !== moduleName ) {
				wphbMixPanel.disableFeature( moduleName );
			}

			HBFetcher.notifications
				.disable( id, type )
				.then( () => {
					window.location.search += '&status=disabled';
				} )
				.catch( ( error ) => {
					window.console.log( error );
				} );
		},

		/**
		 * Toggle Add Recipient button based on input. Disable if fields not filled.
		 *
		 * @since 3.1.1
		 */
		toggleAddButton() {
			const inputs = $(
				'#notifications-invite-users-content input[id^="recipient-"]'
			);

			inputs.on( 'keyup', function() {
				let empty = false;
				inputs.each( function() {
					if ( '' === $( this ).val() ) {
						empty = true;
					}
				} );

				if ( empty ) {
					$( '#add-recipient-button' ).attr( 'disabled', 'disabled' );
				} else {
					$( '#add-recipient-button' ).attr( 'disabled', false );
				}
			} );
		},

		/**
		 * Sanitize the user input string.
		 *
		 * @since 3.4.3
		 * @param {string} string
		 */
		 sanitizeString( string ) {
			let str = String(string).replace(/[&\/\\#^+()$~%.'":*?<>{}!@]/g, '');
			return str.trim();
		},

		/**
		 * Handle "Add recipient" button click on "Add by email" section of the modal.
		 *
		 * @since 3.1.1
		 */
		handleAddButtonClick() {
			const btn = event.target;
			btn.classList.add( 'sui-button-onload-text' );

			const name_input = $( 'input#recipient-name' );
			const email = $( 'input#recipient-email' );
			const err = $( '#error-recipient-email' );
			const name = this.sanitizeString( name_input.val() );
			const name_err = $( '#error-recipient-name' );

			if ( '' === name ) {
				name_err.html( wphb.strings.errorEmptyName );
				name_err.parents().addClass( 'sui-form-field-error' );
				btn.classList.remove( 'sui-button-onload-text' );
				return;
			} else {
				name_err.html( '' );
				name_err.parents().removeClass( 'sui-form-field-error' );
			}

			HBFetcher.notifications
				.getAvatar( email.val() )
				.then( ( avatar ) => {
					err.html( '' );
					err.parents().removeClass( 'sui-form-field-error' );

					const user = {
						name: name,
						email: email.val(),
						role: '',
						avatar,
						id: 0,
					};

					this.confirmSubscription( user ).then( ( response ) => {
						if ( undefined !== response ) {
							user.is_pending = response.pending;
							user.is_subscribed = response.subscribed;
							user.is_can_resend_confirmation =
								response.canResend;
						}
						this.addUser( user, 'email' );

						// Reset inputs.
						name_input.val( '' ).trigger( 'keyup' );
						email.val( '' ).trigger( 'keyup' );
						btn.classList.remove( 'sui-button-onload-text' );
					} );
				} )
				.catch( ( error ) => {
					err.html( error );
					err.parents().addClass( 'sui-form-field-error' );
					btn.classList.remove( 'sui-button-onload-text' );
				} );
		},

		/**
		 * Initialize the search user select.
		 *
		 * @since 3.1.1
		 */
		initUserSelects() {
			const userSelect = $( '#search-users' );
			const self = this;

			userSelect.SUIselect2( {
				minimumInputLength: 3,
				maximumSelectionLength: 1,
				ajax: {
					url: ajaxurl,
					method: 'POST',
					dataType: 'json',
					delay: 250,
					data( params ) {
						return {
							action: 'wphb_pro_search_users',
							nonce: wphb.nonces.HBFetchNonce,
							query: params.term,
							exclude: self.exclude,
						};
					},
					processResults( data ) {
						return {
							results: jQuery.map(
								data.data,
								function( item, index ) {
									return {
										text: item.name,
										id: index,
										user: {
											name: item.name,
											email: item.email,
											role: item.role,
											avatar: item.avatar,
											id: item.id,
										},
									};
								}
							),
						};
					},
				},
			} );

			userSelect.on( 'select2:select', function( e ) {
				self.add( e.params.data.user );
				userSelect.val( null ).trigger( 'change' );
			} );
		},

		/**
		 * Send out a confirmation email to the user.
		 *
		 * @since 3.1.1
		 * @param {Object} user
		 */
		async confirmSubscription( user ) {
			if (
				'uptime' !== this.settings.module ||
				'notifications' !== this.settings.type
			) {
				return;
			}

			return HBFetcher.notifications.sendConfirmationEmail(
				user.name,
				user.email
			);
		},

		/**
		 * Resend invite.
		 *
		 * @since 3.1.1
		 * @param {string} name
		 * @param {string} email
		 */
		resendInvite( name, email ) {
			const self = $( this );
			self.attr( 'disabled', 'disabled' );
			HBFetcher.notifications
				.resendConfirmationEmail( name, email )
				.then( ( response ) => {
					const notice = $( '.notifications-resend-notice' );
					notice.find( 'p' ).html( response.message );
					notice.removeClass( 'sui-hidden' );
					self.attr( 'disabled', false );
				} );
		},

		/**
		 * Add a user recipient row to modal UI.
		 *
		 * @since 3.1.1
		 * @param {Object} user User object.
		 * @param {string} type Accepts: user ("Add users" section), email ("Add by email" section).
		 */
		addUser( user, type = 'user' ) {
			// Check if recipient already exists.
			const index = this.settings.recipients.findIndex(
				( r ) => user.email === r.email
			);
			if ( index > -1 ) {
				this.toggleUserNotice( true );
				return;
			}

			const recipientList = $( '#modal-' + type + '-recipients-list' );
			const tooltip = getString( 'removeRecipient' );
			const role = '' === user.role ? user.email : user.role;

			let subClass = '';
			if ( 'undefined' !== typeof user.is_pending ) {
				if (
					! user.is_pending &&
					'undefined' !== typeof user.is_subscribed &&
					! user.is_subscribed
				) {
					subClass = 'unsubscribed';
				} else {
					subClass = user.is_pending ? 'pending' : 'confirmed';
				}
			}

			let img = `<img src="${ user.avatar }" alt="${ user.email }">`;
			let confirmBtn = ``;
			if ( 'pending' === subClass || 'unsubscribed' === subClass ) {
				const confirmTooltip = getString( 'awaitingConfirmation' );
				const resendTooltip = getString( 'resendInvite' );
				img = `<span class="sui-tooltip" data-tooltip="${ confirmTooltip }">${ img }</span>`;
				confirmBtn = `<button type="button" class="resend-invite sui-button-icon sui-tooltip" data-tooltip="${ resendTooltip }"
					onclick="WPHB_Admin.notifications.resendInvite( '${ user.name }', '${ user.email }' )">
					<span class="sui-icon-send" aria-hidden="true"></span>
				</button>`;
			}

			const row = `
				<div class="sui-recipient" data-id="${ user.id }" data-email="${ user.email }">
					<span class="sui-recipient-name">
						<span class="subscriber ${ subClass }">${ img }</span>
						<span class="wphb-recipient-name">${ user.name }</span>
					</span>
					<span class="sui-recipient-email">${ role }</span>
					${ confirmBtn }
					<button type="button" class="sui-button-icon sui-tooltip" data-tooltip="${ tooltip }"
						onclick="WPHB_Admin.notifications.removeUser( ${ user.id }, '${ user.email }', '${ type }' )">
						<span class="sui-icon-trash" aria-hidden="true"></span>
					</button>
				</div>
			`;

			recipientList.append( row );

			// Add to the recipients and exclude arrays.
			this.settings.recipients.push( user );
			if ( 'user' === type ) {
				this.exclude.push( user.id );
			}

			this.toggleRecipientList( recipientList );
		},

		/**
		 * Populate "Users" list.
		 *
		 * @since 3.1.1
		 * @param {Object}  user
		 * @param {boolean} first
		 */
		addToUsersList( user, first = false ) {
			const recipientList = $( '#modal-wp-user-list' );

			const tooltipClass = first
				? 'sui-tooltip-bottom-right'
				: 'sui-tooltip-top-right';

			const row = `
				<div class="sui-recipient" data-id="${ user.id }" data-email="${ user.email }">
					<span class="sui-recipient-name">
						<span class="subscriber">
							<img src="${ user.avatar }" alt="${ user.email }">
						</span>
						<span class="wphb-recipient-name">${ user.name }</span>
					</span>
					<span class="sui-recipient-email">${ user.role }</span>
					<button type="button" class="sui-button-icon sui-tooltip ${ tooltipClass }"
						data-tooltip="${ getString( 'addRecipient' ) }"
						onclick='WPHB_Admin.notifications.add( ${ JSON.stringify( user ) } )'>
						<span class="sui-icon-plus" aria-hidden="true"></span>
					</button>
				</div>
			`;

			recipientList.append( row );
			this.toggleRecipientList( recipientList );
		},

		/**
		 * Remove a user recipient row from modal UI.
		 *
		 * @since 3.1.1
		 * @param {number} id    User ID to remove.
		 * @param {string} email User email.
		 * @param {string} type  Accepts: user ("Add users" section), email ("Add by email" section).
		 */
		removeUser( id, email, type = 'user' ) {
			const recipientList = $( '#modal-' + type + '-recipients-list' );
			const el = '.sui-recipient[data-email="' + email + '"]';

			// Remove Div.
			const row = recipientList.find( el );
			row.remove();

			// Remove from exclude list.
			let index;
			if ( 'user' === type ) {
				index = this.exclude.indexOf( id );
				if ( index > -1 ) {
					this.exclude.splice( index, 1 );
				}
				this.returnToList( row );
			}

			// Remove from recipients array.
			index = this.settings.recipients.findIndex(
				( r ) => id === parseInt( r.id ) && email === r.email
			);
			if ( index > -1 ) {
				this.settings.recipients.splice( index, 1 );
			}

			// Hide title if no more elements.
			this.toggleRecipientList( recipientList );
		},

		/**
		 * Add user from the "Users" list.
		 *
		 * @since 3.1.1
		 * @param {Object} user
		 */
		add( user ) {
			this.confirmSubscription( user ).then( ( response ) => {
				if ( undefined !== response ) {
					user.is_pending = response.pending;
					user.is_subscribed = response.subscribed;
					user.is_can_resend_confirmation = response.canResend;
				}

				this.addUser( user );

				const recipientList = $( '#modal-wp-user-list' );
				const el = '.sui-recipient[data-email="' + user.email + '"]';

				// Remove Div.
				recipientList.find( el ).remove();

				this.fixRecipientCSS( recipientList );
				this.toggleRecipientList( recipientList, false );
			} );
		},

		/**
		 * Return user back to "Users" list.
		 *
		 * @since 3.1.1
		 * @param {Object} el User row.
		 */
		returnToList( el ) {
			const recipientList = $( '#modal-wp-user-list' );

			const user = {
				id: el.data( 'id' ),
				name: el.find( '.wphb-recipient-name' ).text(),
				email: el.data( 'email' ),
				role: el.find( '.sui-recipient-email' ).text(),
				avatar: el.find( 'img' ).attr( 'src' ),
			};

			const onClickFunction =
				'WPHB_Admin.notifications.add(' + JSON.stringify( user ) + ')';

			// Remove the resend icon.
			el.find( '.resend-invite' ).remove();

			el.find( '.sui-icon-trash' )
				.removeClass( 'sui-icon-trash' )
				.addClass( 'sui-icon-plus' );

			el.find( 'button' )
				.attr( 'onclick', onClickFunction )
				.attr( 'data-tooltip', getString( 'addRecipient' ) )
				.addClass( 'sui-tooltip-top-right' );

			recipientList.append( el );

			this.fixRecipientCSS( recipientList );
			this.toggleRecipientList( recipientList, false );
		},

		/**
		 * Fix recipient overflow.
		 *
		 * @since 3.2.0
		 * @param {Object} list
		 */
		fixRecipientCSS( list ) {
			const val = list.children().length > 1 ? 'hidden' : 'unset';
			list.css( 'overflow-x', val );

			list.find( '.sui-recipient:first-of-type .sui-tooltip' )
				.removeClass( 'sui-tooltip-top-right' )
				.addClass( 'sui-tooltip-bottom-right' );
		},

		/**
		 * Show/hide recipient list based on child items.
		 *
		 * @since 3.1.1
		 * @param {Object}  el         Recipient list element.
		 * @param {boolean} userNotice Show user notice when no recipients.
		 */
		toggleRecipientList( el, userNotice = true ) {
			const hasItems = 0 === el.html().trim().length;

			el.parent( 'div' )
				.toggleClass( 'sui-hidden', hasItems )
				.toggleClass( 'sui-margin-top', ! hasItems );

			if ( userNotice ) {
				this.toggleUserNotice();
			}
		},

		/**
		 * Do not allow saving settings if no users are added to a notification.
		 *
		 * @since 3.1.1
		 * @param {boolean} recipientExists Show the recipient already exists notice.
		 */
		toggleUserNotice( recipientExists = false ) {
			const notice = $( '.notifications-recipients-notice' );
			const btn = $( '#notification-modal .sui-button.sui-button-blue' );
			const continueButton = $( '.notification-next-buttons' );

			let text = getString( 'noRecipients' );
			if ( recipientExists ) {
				text = getString( 'recipientExists' );
			} else if ( this.edit ) {
				text = getString( 'noRecipientDisable' );
			}

			notice.find( 'p' ).html( text );

			if ( recipientExists ) {
				notice.removeClass( 'sui-hidden' );
				setTimeout( () => notice.addClass( 'sui-hidden' ), 3000 );
			} else if ( 0 === this.settings.recipients.length ) {
				if ( ! this.edit ) {
					btn.attr( 'disabled', 'disabled' );
					continueButton.attr( 'disabled', 'disabled' );
				}
				notice.removeClass( 'sui-hidden' );
			} else {
				btn.attr( 'disabled', false );
				continueButton.attr( 'disabled', false );
				notice.addClass( 'sui-hidden' );
			}
		},
	};

	/**
	 * Template function (underscores based).
	 *
	 * @type {Function}
	 */
	WPHB_Admin.notifications.template = _.memoize( ( id ) => {
		let compiled;
		const options = {
			evaluate: /<#([\s\S]+?)#>/g,
			interpolate: /{{{([\s\S]+?)}}}/g,
			escape: /{{([^}]+?)}}(?!})/g,
			variable: 'data',
		};

		return ( data ) => {
			_.templateSettings = options;
			compiled = compiled || _.template( $( '#' + id ).html() );
			return compiled( data );
		};
	} );
} )( jQuery );