Merge branch '32916-browser-notifications-for-pipeline-running-in-a-mr-is-gone' into 'master'
Resolve "Browser notifications for pipeline running in a MR is gone" Closes #32916 See merge request !11734
This commit is contained in:
commit
ebd6e5f351
|
@ -1,25 +1,23 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, no-param-reassign, max-len */
|
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, no-param-reassign, max-len */
|
||||||
|
|
||||||
(function() {
|
function notificationGranted(message, opts, onclick) {
|
||||||
(function(w) {
|
|
||||||
var notificationGranted, notifyMe, notifyPermissions;
|
|
||||||
notificationGranted = function(message, opts, onclick) {
|
|
||||||
var notification;
|
var notification;
|
||||||
notification = new Notification(message, opts);
|
notification = new Notification(message, opts);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
return notification.close();
|
|
||||||
// Hide the notification after X amount of seconds
|
// Hide the notification after X amount of seconds
|
||||||
|
return notification.close();
|
||||||
}, 8000);
|
}, 8000);
|
||||||
if (onclick) {
|
|
||||||
return notification.onclick = onclick;
|
return notification.onclick = onclick || notification.close;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
notifyPermissions = function() {
|
function notifyPermissions() {
|
||||||
if ('Notification' in window) {
|
if ('Notification' in window) {
|
||||||
return Notification.requestPermission();
|
return Notification.requestPermission();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
notifyMe = function(message, body, icon, onclick) {
|
|
||||||
|
function notifyMe(message, body, icon, onclick) {
|
||||||
var opts;
|
var opts;
|
||||||
opts = {
|
opts = {
|
||||||
body: body,
|
body: body,
|
||||||
|
@ -27,7 +25,6 @@
|
||||||
};
|
};
|
||||||
// Let's check if the browser supports notifications
|
// Let's check if the browser supports notifications
|
||||||
if (!('Notification' in window)) {
|
if (!('Notification' in window)) {
|
||||||
|
|
||||||
// do nothing
|
// do nothing
|
||||||
} else if (Notification.permission === 'granted') {
|
} else if (Notification.permission === 'granted') {
|
||||||
// If it's okay let's create a notification
|
// If it's okay let's create a notification
|
||||||
|
@ -40,8 +37,12 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
w.notify = notifyMe;
|
|
||||||
return w.notifyPermissions = notifyPermissions;
|
const notify = {
|
||||||
})(window);
|
notificationGranted,
|
||||||
}).call(window);
|
notifyPermissions,
|
||||||
|
notifyMe,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default notify;
|
||||||
|
|
|
@ -56,7 +56,6 @@ import './lib/utils/animate';
|
||||||
import './lib/utils/bootstrap_linked_tabs';
|
import './lib/utils/bootstrap_linked_tabs';
|
||||||
import './lib/utils/common_utils';
|
import './lib/utils/common_utils';
|
||||||
import './lib/utils/datetime_utility';
|
import './lib/utils/datetime_utility';
|
||||||
import './lib/utils/notify';
|
|
||||||
import './lib/utils/pretty_time';
|
import './lib/utils/pretty_time';
|
||||||
import './lib/utils/text_utility';
|
import './lib/utils/text_utility';
|
||||||
import './lib/utils/url_utility';
|
import './lib/utils/url_utility';
|
||||||
|
|
|
@ -41,3 +41,4 @@ export { default as getStateKey } from './stores/get_state_key';
|
||||||
export { default as mrWidgetOptions } from './mr_widget_options';
|
export { default as mrWidgetOptions } from './mr_widget_options';
|
||||||
export { default as stateMaps } from './stores/state_maps';
|
export { default as stateMaps } from './stores/state_maps';
|
||||||
export { default as SquashBeforeMerge } from './components/states/mr_widget_squash_before_merge';
|
export { default as SquashBeforeMerge } from './components/states/mr_widget_squash_before_merge';
|
||||||
|
export { default as notify } from '../lib/utils/notify';
|
||||||
|
|
|
@ -4,6 +4,8 @@ import {
|
||||||
} from './dependencies';
|
} from './dependencies';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
gl.mrWidgetData.gitlabLogo = gon.gitlab_logo;
|
||||||
|
|
||||||
const vm = new Vue(mrWidgetOptions);
|
const vm = new Vue(mrWidgetOptions);
|
||||||
|
|
||||||
window.gl.mrWidget = {
|
window.gl.mrWidget = {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
eventHub,
|
eventHub,
|
||||||
stateMaps,
|
stateMaps,
|
||||||
SquashBeforeMerge,
|
SquashBeforeMerge,
|
||||||
|
notify,
|
||||||
} from './dependencies';
|
} from './dependencies';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -77,8 +78,10 @@ export default {
|
||||||
this.service.checkStatus()
|
this.service.checkStatus()
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.handleNotification(res);
|
||||||
this.mr.setData(res);
|
this.mr.setData(res);
|
||||||
this.setFavicon();
|
this.setFavicon();
|
||||||
|
|
||||||
if (cb) {
|
if (cb) {
|
||||||
cb.call(null, res);
|
cb.call(null, res);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +139,15 @@ export default {
|
||||||
new Flash('Something went wrong. Please try again.'); // eslint-disable-line
|
new Flash('Something went wrong. Please try again.'); // eslint-disable-line
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleNotification(data) {
|
||||||
|
if (data.ci_status === this.mr.ciStatus) return;
|
||||||
|
|
||||||
|
const label = data.pipeline.details.status.label;
|
||||||
|
const title = `Pipeline ${label}`;
|
||||||
|
const message = `Pipeline ${label} for "${data.title}"`;
|
||||||
|
|
||||||
|
notify.notifyMe(title, message, this.mr.gitlabLogo);
|
||||||
|
},
|
||||||
resumePolling() {
|
resumePolling() {
|
||||||
this.pollingInterval.resume();
|
this.pollingInterval.resume();
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,6 +5,8 @@ export default class MergeRequestStore {
|
||||||
|
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
this.sha = data.diff_head_sha;
|
this.sha = data.diff_head_sha;
|
||||||
|
this.gitlabLogo = data.gitlabLogo;
|
||||||
|
|
||||||
this.setData(data);
|
this.setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# rubocop:disable Metrics/AbcSize
|
||||||
|
|
||||||
module Gitlab
|
module Gitlab
|
||||||
module GonHelper
|
module GonHelper
|
||||||
def add_gon_variables
|
def add_gon_variables
|
||||||
|
@ -13,6 +15,7 @@ module Gitlab
|
||||||
gon.sentry_dsn = current_application_settings.clientside_sentry_dsn if current_application_settings.clientside_sentry_enabled
|
gon.sentry_dsn = current_application_settings.clientside_sentry_dsn if current_application_settings.clientside_sentry_enabled
|
||||||
gon.gitlab_url = Gitlab.config.gitlab.url
|
gon.gitlab_url = Gitlab.config.gitlab.url
|
||||||
gon.revision = Gitlab::REVISION
|
gon.revision = Gitlab::REVISION
|
||||||
|
gon.gitlab_logo = ActionController::Base.helpers.asset_path('gitlab_logo.png')
|
||||||
|
|
||||||
if current_user
|
if current_user
|
||||||
gon.current_user_id = current_user.id
|
gon.current_user_id = current_user.id
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
||||||
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
|
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
|
||||||
import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options';
|
import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options';
|
||||||
import eventHub from '~/vue_merge_request_widget/event_hub';
|
import eventHub from '~/vue_merge_request_widget/event_hub';
|
||||||
|
import notify from '~/lib/utils/notify';
|
||||||
import mockData from './mock_data';
|
import mockData from './mock_data';
|
||||||
|
|
||||||
const createComponent = () => {
|
const createComponent = () => {
|
||||||
|
@ -107,6 +108,8 @@ describe('mrWidgetOptions', () => {
|
||||||
it('should tell service to check status', (done) => {
|
it('should tell service to check status', (done) => {
|
||||||
spyOn(vm.service, 'checkStatus').and.returnValue(returnPromise(mockData));
|
spyOn(vm.service, 'checkStatus').and.returnValue(returnPromise(mockData));
|
||||||
spyOn(vm.mr, 'setData');
|
spyOn(vm.mr, 'setData');
|
||||||
|
spyOn(vm, 'handleNotification');
|
||||||
|
|
||||||
let isCbExecuted = false;
|
let isCbExecuted = false;
|
||||||
const cb = () => {
|
const cb = () => {
|
||||||
isCbExecuted = true;
|
isCbExecuted = true;
|
||||||
|
@ -117,6 +120,7 @@ describe('mrWidgetOptions', () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(vm.service.checkStatus).toHaveBeenCalled();
|
expect(vm.service.checkStatus).toHaveBeenCalled();
|
||||||
expect(vm.mr.setData).toHaveBeenCalled();
|
expect(vm.mr.setData).toHaveBeenCalled();
|
||||||
|
expect(vm.handleNotification).toHaveBeenCalledWith(mockData);
|
||||||
expect(isCbExecuted).toBeTruthy();
|
expect(isCbExecuted).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
}, 333);
|
}, 333);
|
||||||
|
@ -254,6 +258,39 @@ describe('mrWidgetOptions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('handleNotification', () => {
|
||||||
|
const data = {
|
||||||
|
ci_status: 'running',
|
||||||
|
title: 'title',
|
||||||
|
pipeline: { details: { status: { label: 'running-label' } } },
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(notify, 'notifyMe');
|
||||||
|
|
||||||
|
vm.mr.ciStatus = 'failed';
|
||||||
|
vm.mr.gitlabLogo = 'logo.png';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call notifyMe', () => {
|
||||||
|
vm.handleNotification(data);
|
||||||
|
|
||||||
|
expect(notify.notifyMe).toHaveBeenCalledWith(
|
||||||
|
'Pipeline running-label',
|
||||||
|
'Pipeline running-label for "title"',
|
||||||
|
'logo.png',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call notifyMe if the status has not changed', () => {
|
||||||
|
vm.mr.ciStatus = data.ci_status;
|
||||||
|
|
||||||
|
vm.handleNotification(data);
|
||||||
|
|
||||||
|
expect(notify.notifyMe).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('resumePolling', () => {
|
describe('resumePolling', () => {
|
||||||
it('should call stopTimer on pollingInterval', () => {
|
it('should call stopTimer on pollingInterval', () => {
|
||||||
spyOn(vm.pollingInterval, 'resume');
|
spyOn(vm.pollingInterval, 'resume');
|
||||||
|
|
Loading…
Reference in New Issue