diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION index 01e3fa0987b..1479a3ce767 100644 --- a/GITLAB_KAS_VERSION +++ b/GITLAB_KAS_VERSION @@ -1 +1 @@ -889fff0a39dc1d79dc585f345e4fd82105b57a7f +31259f5394382f015730d7bbfeb775623d69145f diff --git a/app/assets/javascripts/persistent_user_callout.js b/app/assets/javascripts/persistent_user_callout.js index 8508d63fd1d..1c2f9b3573a 100644 --- a/app/assets/javascripts/persistent_user_callout.js +++ b/app/assets/javascripts/persistent_user_callout.js @@ -4,8 +4,10 @@ import { parseBoolean } from './lib/utils/common_utils'; import { __ } from './locale'; import { visitUrl } from './lib/utils/url_utility'; -const DEFERRED_LINK_CLASS = '.deferred-link'; - +/** + * Integrates with server-side rendered callouts, adding dismissing interaction to them. + * See https://docs.gitlab.com/development/callouts/#server-side-rendered-callouts + */ export default class PersistentUserCallout { constructor(container, options = container.dataset) { const { dismissEndpoint, featureId, groupId, projectId, deferLinks } = options; @@ -21,23 +23,13 @@ export default class PersistentUserCallout { } init() { - const followLink = this.container.querySelector('.js-follow-link'); - - if (this.closeButtons.length) { - this.handleCloseButtonCallout(); - } else if (followLink) { - this.handleFollowLinkCallout(followLink); - } - } - - handleCloseButtonCallout() { this.closeButtons.forEach((closeButton) => { closeButton.addEventListener('click', this.dismiss); }); - if (this.deferLinks) { + if (this.closeButtons.length && this.deferLinks) { this.container.addEventListener('click', (event) => { - const deferredLinkEl = event.target.closest(DEFERRED_LINK_CLASS); + const deferredLinkEl = event.target.closest('.deferred-link'); if (deferredLinkEl) { const { href, target } = deferredLinkEl; @@ -47,10 +39,6 @@ export default class PersistentUserCallout { } } - handleFollowLinkCallout(followLink) { - followLink.addEventListener('click', (event) => this.registerCalloutWithLink(event)); - } - dismiss = (event, deferredLinkOptions = null) => { event.preventDefault(); @@ -80,27 +68,6 @@ export default class PersistentUserCallout { }); }; - registerCalloutWithLink(event) { - event.preventDefault(); - - const { href } = event.currentTarget; - - axios - .post(this.dismissEndpoint, { - feature_name: this.featureId, - }) - .then(() => { - window.location.assign(href); - }) - .catch(() => { - createAlert({ - message: __( - 'An error occurred while acknowledging the notification. Refresh the page and try again.', - ), - }); - }); - } - static factory(container, options) { if (!container) { return undefined; diff --git a/app/assets/javascripts/super_sidebar/components/user_menu.vue b/app/assets/javascripts/super_sidebar/components/user_menu.vue index f31482f256e..88fa5ac2927 100644 --- a/app/assets/javascripts/super_sidebar/components/user_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/user_menu.vue @@ -10,8 +10,11 @@ import { import SafeHtml from '~/vue_shared/directives/safe_html'; import { s__, __, sprintf } from '~/locale'; import Tracking from '~/tracking'; -import PersistentUserCallout from '~/persistent_user_callout'; import { SET_STATUS_MODAL_ID } from '~/set_status_modal/constants'; +import axios from '~/lib/utils/axios_utils'; +import { visitUrl } from '~/lib/utils/url_utility'; +import * as Sentry from '~/sentry/sentry_browser_wrapper'; +import { logError } from '~/lib/logger'; import { USER_MENU_TRACKING_DEFAULTS, DROPDOWN_Y_OFFSET, IMPERSONATING_OFFSET } from '../constants'; import UserMenuProfileItem from './user_menu_profile_item.vue'; @@ -128,7 +131,6 @@ export default { warningText: this.$options.i18n.oneOfGroupsRunningOutOfPipelineMinutes, href: this.data.pipeline_minutes?.buy_pipeline_minutes_path, extraAttrs: { - class: 'js-follow-link', ...USER_MENU_TRACKING_DEFAULTS, 'data-track-label': 'buy_pipeline_minutes', }, @@ -199,14 +201,6 @@ export default { 'current-clear-status-after': clearAfter || '', }; }, - buyPipelineMinutesCalloutData() { - return this.showNotificationDot - ? { - 'data-feature-id': this.data.pipeline_minutes.callout_attrs.feature_id, - 'data-dismiss-endpoint': this.data.pipeline_minutes.callout_attrs.dismiss_endpoint, - } - : {}; - }, showEnterAdminModeItem() { return ( this.data.admin_mode.user_is_admin && @@ -249,21 +243,38 @@ export default { this.$refs.userDropdown.close(); }, initBuyCIMinsCallout() { - if (this.showNotificationDot) { - PersistentUserCallout.factory(this.$refs?.buyPipelineMinutesNotificationCallout.$el); - } + const el = this.$refs?.buyPipelineMinutesNotificationCallout?.$el; + el?.addEventListener('click', this.onBuyCIMinutesItemClick); }, - /* We're not sure this event is tracked by anyone - whether it stays will depend on the outcome of this discussion: - https://gitlab.com/gitlab-org/gitlab/-/issues/402713#note_1343072135 */ - trackBuyCIMins() { - if (this.addBuyPipelineMinutesMenuItem) { - const { - 'track-action': trackAction, - 'track-label': label, - 'track-property': property, - } = this.data.pipeline_minutes.tracking_attrs; - this.track(trackAction, { label, property }); + async onBuyCIMinutesItemClick(event) { + /* NOTE: We're not sure this event is tracked by anyone + * whether it stays will depend on the outcome of this discussion: + * https://gitlab.com/gitlab-org/gitlab/-/issues/402713#note_1343072135 + */ + const { + 'track-action': trackAction, + 'track-label': label, + 'track-property': property, + } = this.data.pipeline_minutes.tracking_attrs; + this.track(trackAction, { label, property }); + + // Proceed to the URL if the notification dot is not shown + if (!this.showNotificationDot) return; + + event.preventDefault(); + const href = this.data.pipeline_minutes?.buy_pipeline_minutes_path; + const featureId = this.data.pipeline_minutes.callout_attrs.feature_id; + const dismissEndpoint = this.data.pipeline_minutes.callout_attrs.dismiss_endpoint; + + try { + // dismiss the notification dot Callout + await axios.post(dismissEndpoint, { feature_name: featureId }); + } catch (error) { + logError(error); + Sentry.captureException(error); + } finally { + // visit the URL whether the callout notification is dismissed or not + visitUrl(href); } }, trackSignOut() { @@ -344,9 +355,7 @@ export default { v-if="addBuyPipelineMinutesMenuItem" ref="buyPipelineMinutesNotificationCallout" :item="buyPipelineMinutesItem" - v-bind="buyPipelineMinutesCalloutData" data-testid="buy-pipeline-minutes-item" - @action="trackBuyCIMins" >