User:Colin M/scripts/MarkAsRead.js
Appearance
< User:Colin M | scripts
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Colin M/scripts/MarkAsRead. |
/* TODO
- make rows actually disappear when read (optionally?)
- should 'mark all' find all rows with that title and remove them too? oof.
- would be cool if the 'mark all' button could be hidden if there are no newer revisions for this page.
- theoretically, could probably do this without extra requests, just by looking at the DOM
*/
function lastRevForLine(line) {
let rev = line.dataset.mwRevid;
if (rev) return rev;
for (let row of line.querySelectorAll('tr.mw-changeslist-line')) {
if (row.dataset.mwRevid) {
return row.dataset.mwRevid;
}
}
}
const include_mark_all = true;
function addButtons(line, api) {
const revid = lastRevForLine(line);
//if (revid === undefined) return;
//const ts = parseInt(line.dataset.mwTs);
//const ts = line.dataset.mwTs;
// Hm, so setting timestamp to exactly ts seems to mark everything up to but not including that rev as read. Hack required?
// timestamps look like 20190827165313 i.e. 2019-08-27-16-53-13
// there must be some mw helper for encoding/decoding date strings like this...
const title_ele = line.querySelector('a.mw-changeslist-title');
if (!title_ele) {
// This shouldn't happen. Was previously triggered when we weren't excluding entries from move log or page curation log.
console.warn("Couldn't find a.mw-changeslist-title for line:", line);
return;
}
const href = title_ele.attributes.href.value;
const prefix = '/wiki/';
let title;
if (href.startsWith(prefix)) {
title = href.slice(prefix.length);
} else {
const pre2 = '/w/index.php?title=';
if (href.startsWith(pre2)) {
title = href.slice(pre2.length);
} else {
console.warn("Couldn't find title in...", title_ele);
return;
}
}
const row = line.querySelector('tr');
// Main watchlist entry ele (with links to article, diff, history, etc.). This is where we add the button(s).
const button_td = row.querySelector('td.mw-changeslist-line-inner');
const addButton = (label, onclick) => {
const button = document.createElement('button');
button.textContent = label;
button.addEventListener('click', onclick);
button_td.appendChild(button);
button.classList.add('markread');
button.style.padding = '0 2px';
button.style.marginLeft = '0.3em';
button.style.marginRight = '1.3em';
button.style.borderColor = '#555';
button.style.borderWidth = '2px';
return button;
};
// TODO: Guess don't need to import mw.Title anymore?
//const parsed_title = mw.Title.newFromText(title);
const markChangeRead = (evt) => {
const params = {
action: 'setnotificationtimestamp',
// Wait, then what the heck does the "revids" param do?
//torevid: parseInt(revid)+1,
newerthanrevid: revid,
// need only one of titles or revids
// But if we provide torevid/newerthanrevid WITHOUT title, the API call will fail silently. Because of course it will.
// need to get rid of any % encoding
titles: decodeURI(title)
};
console.log(params);
api.postWithEditToken(params);
line.style.opacity = 0.5;
};
const markAllRead = (evt) => {
// If no timestamp/revid is provided, default behaviour is to set timestamp to now (i.e. mark all extant revisions as read)
const params = {
action: 'setnotificationtimestamp',
titles: decodeURI(title)
};
console.log(params);
api.postWithEditToken(params);
line.style.opacity = 0.3;
};
const heavycheck = '✔';
const check = '✓';
addButton(check, markChangeRead);
if (include_mark_all) {
const markall = addButton(heavycheck, markAllRead);
markall.style.backgroundColor = '#d4b3ad';
markall.setAttribute('title', 'Mark all revisions of this page read');
}
}
if (mw.config.values.wgCanonicalSpecialPageName === 'Watchlist') {
mw.loader.using(['mediawiki.api', 'mediawiki.Title'], function () {
let api = new mw.Api();
window.wgAPI = api;
let wl = document.querySelector('.mw-changeslist');
// Exclude log lines (move log, page curation log, etc.)
let lines = wl.querySelectorAll('table.mw-changeslist-line:not(.mw-changeslist-log)');
lines.forEach((line) => addButtons(line, api));
});
}
/* re API...
So, the docs at https://www.mediawiki.org/wiki/API:Setnotificationtimestamp aren't totally clear at this, but
based on some experimentation, it seems like for each (page, user) pair, there's just one revid/timestamp stored, such
that anything newer is considered unseen, and anything older is seen.
This is totally contrary to my mental model, which was that there was a seen flag for every revision. I didn't realize that viewing the diff of one
particular rev of a page would cause all older revs to be marked as viewed.
Well this affects the implementation of this - and actually makes it a lot easier.
*/
/* Notes on watchlist DOM structure:
.mw-changeslist
<h4>date
div
table.mw-changeslist-line data-mw-revid=... data-mw-ts=...
tr
// or, for collapsed line with multiple revs (will have data-ts but not revid) data-mw-revid will be on the tr's
table.mw-chageslist-line.mw-collapsible > tbody
tr*
*/