Greasy Fork is available in English.
Displays viewed/total file count on GitLab merge request pages
// ==UserScript==
// @name GitLab MR Viewed File Counter
// @namespace http://tampermonkey.net/
// @version 1.0.1
// @description Displays viewed/total file count on GitLab merge request pages
// @match https://gitlab.com/*/-/merge_requests/*
// @grant none
// @run-at document-idle
// @author bzly
// @license GPL-3.0-or-later
// ==/UserScript==
(function () {
'use strict';
const badge = document.querySelector('.js-changes-tab-count');
let total = badge.textContent.trim();
const mrPath = window.location.pathname.match(/.*\/merge_requests\/\d+/)?.[0];
const mrKey = `code-review-${mrPath}`;
const pill = document.querySelector('.diffs-tab .gl-badge');
function getViewedCount() {
try {
const viewed = JSON.parse(localStorage.getItem(mrKey));
return Array.isArray(viewed) ? viewed.length : 0;
} catch {
return 0;
}
}
function updateBadge() {
const viewed = getViewedCount();
const pct = total > 0 ? Math.round((viewed / total) * 100) : 0;
badge.textContent = `${viewed}/${total}`;
if (viewed > 0) {
// when any files are marked as viewed, gradually turn from the GitLab 'closed' colour to the 'merged' colour.
pill.style.backgroundColor = `color-mix(in srgb, var(--gl-badge-success-background-color-default) ${pct}%, var(--gl-badge-danger-background-color-default))`;
} else {
pill.style.backgroundColor = 'var(--gl-badge-neutral-background-color-default)';
}
}
// update count when GitLab changes the element back at random times
new MutationObserver(() => {
if (!badge.textContent.includes('/')) {
total = badge.textContent.trim();
updateBadge();
}
}).observe(badge, { childList: true, characterData: true, subtree: true });
// update count on same-tab changes
const _setItem = localStorage.setItem.bind(localStorage);
localStorage.setItem = function (key, value) {
_setItem(key, value);
if (key === mrKey) updateBadge();
};
// update count when going back to 0 viewed
const _removeItem = localStorage.removeItem.bind(localStorage);
localStorage.removeItem = function (key) {
_removeItem(key);
if (key === mrKey) updateBadge();
};
// update count on different-tab change (i.e. you are working across multiple tabs)
window.addEventListener('storage', (e) => {
if (e.key === mrKey) updateBadge();
});
updateBadge();
})();