User:Colin M/scripts/MarkAsRead.js

/* 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 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*