Last active
July 12, 2022 14:48
-
-
Save vanadium23/d9ea3b7c2e8f3265ff209cb7358127fb to your computer and use it in GitHub Desktop.
Script for tampermonkey to add articles count in selection list (My list, archive, favorites)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Pocket Counters | |
// @namespace http://vanadium23.me/ | |
// @version 0.5 | |
// @description Add counters to pocket list | |
// @author vanadium23 | |
// @match https://app.getpocket.com/* | |
// @ran-at document-idle | |
// @grant none | |
// ==/UserScript== | |
(function (indexedDB) { | |
var observer = new MutationObserver(function (mutations) { | |
mutations.forEach(function (mutation) { | |
if (!mutation.addedNodes) return | |
for (var i = 0; i < mutation.addedNodes.length; i++) { | |
// do things to your newly added nodes here | |
var node = mutation.addedNodes[i]; | |
if (document.querySelector('ul')) { | |
observer.disconnect(); | |
setupCounters(); | |
} | |
} | |
}) | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true, | |
attributes: false, | |
characterData: true, | |
}); | |
const dbPromise = new Promise(function(resolve, reject) { | |
const idb = indexedDB.open("PocketWebData"); | |
idb.onerror = event => reject(event); | |
idb.onsuccess = event => { | |
resolve(event.target.result); | |
}; | |
}); | |
function fetchAllFromDatabase(db) { | |
return new Promise(function(resolve, reject) { | |
const transaction = db.transaction("itemList").objectStore("itemList").getAll(); | |
transaction.onerror = event => reject(event); | |
transaction.onsuccess = event => { | |
resolve(event.target.result); | |
}; | |
}); | |
} | |
function setupCounters() { | |
'use strict'; | |
let globalState = { | |
counters: { | |
queue: 0, | |
favorite: 0, | |
archive: 0, | |
}, | |
articles: { | |
queue: {}, | |
favorite: {}, | |
archive: {}, | |
} | |
}; | |
const ARTICLE_TIME_PROPERTY = { | |
queue: 'time_added', | |
favorite: 'time_favorited', | |
archive: 'time_read', | |
} | |
const ARTICLE_STATES = { | |
queue: 'section-mylist', | |
favorite: 'section-favorites', | |
archive: 'section-archive', | |
}; | |
const ARTICLE_STATES_SELECTOR = { | |
queue: 'a[href="/"]', | |
favorite: 'a[href="/favorites"]', | |
archive: 'a[href="/archive"]', | |
}; | |
const states = Object.keys(ARTICLE_STATES); | |
const COUNTER_POSTFIX = '-counter'; | |
const FAKE_COUNT_NUMBER = 1000; | |
function filterArticles(state) { | |
switch (state) { | |
case 'queue': | |
return x => x.status === '0'; | |
break; | |
case 'favorite': | |
return x => x.favorite === '1'; | |
break; | |
case 'archive': | |
return x => x.status === '1'; | |
break; | |
} | |
} | |
// fetch all articles | |
function fetchArticlesV2(state, offset = 0, count = 100) { | |
const object = { | |
"images": 1, | |
"videos": 1, | |
"tags": 1, | |
"taglist": 1, | |
"account": 1, | |
"rediscovery": 1, | |
"posts": 1, | |
"total": 1, | |
"state": state, | |
"locale_lang": "en-US", | |
"passedChunk": 12, | |
"count": count, | |
"offset": offset | |
} | |
if (state == 'favorite') { | |
object.state = ''; | |
object.favorite = 1; | |
} | |
return fetch("https://getpocket.com/v3/fetch?enable_cors=1&consumer_key=78809-9423d8c743a58f62b23ee85c", { | |
"credentials": "include", | |
"headers": { | |
"accept": "*/*", | |
"accept-language": "en-GB,en;q=0.9,en-US;q=0.8,ru;q=0.7", | |
"content-type": "application/json", | |
"x-accept": "application/json; charset=UTF8" | |
}, | |
"referrer": "https://app.getpocket.com/", | |
"referrerPolicy": "no-referrer-when-downgrade", | |
"body": JSON.stringify(object), | |
"method": "POST", | |
"mode": "cors" | |
}) | |
.then(response => response.json()); | |
} | |
function countArticles(state, offset = 0) { | |
dbPromise.then(db => { | |
return fetchAllFromDatabase(db); | |
}).then(articles => { | |
const list = articles.filter(filterArticles(state)); | |
// update storage | |
globalState.counters[state] += list.length; | |
Object.assign(globalState.articles[state], list); | |
// update UI | |
drawCounter(state); | |
}).catch(error => { | |
console.error(error); | |
return fetchArticlesV2(state, offset).then(data => { | |
// if meta result == 1000, then it is not a real count | |
let count = parseInt(data.search_meta.total_result_count); | |
let metaCount = true; | |
if (count == FAKE_COUNT_NUMBER) { | |
count = offset === 0 ? FAKE_COUNT_NUMBER : Object.keys(data.list).length; | |
metaCount = false; | |
} else if (isNaN(count)) { | |
count = parseInt(data.total); | |
metaCount = false; | |
} | |
// update storage | |
globalState.counters[state] += count; | |
Object.assign(globalState.articles[state], data.list); | |
// update UI | |
drawCounter(state); | |
}); | |
}) | |
} | |
function getMonday(date) { | |
var day = date.getDay() || 7; | |
if (day !== 1) | |
date.setHours(-24 * (day - 1)); | |
return date; | |
} | |
function drawCounter(state) { | |
for (let counterType of ['', '-today-', '-week-']) { | |
const selector = `.${ARTICLE_STATES[state]}${counterType}${COUNTER_POSTFIX}`; | |
let counter = document.querySelector(selector); | |
if (counter) { | |
let unreadCount = '?'; | |
if (counterType === '') { | |
unreadCount = globalState.counters[state]; | |
} else if (counterType === '-today-') { | |
const timeProperty = ARTICLE_TIME_PROPERTY[state]; | |
const articles = globalState.articles[state]; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
unreadCount = Object.keys(articles).filter( | |
itemId => (parseInt(articles[itemId][timeProperty]) * 1000) > today | |
).length; | |
} else if (counterType === '-week-') { | |
const timeProperty = ARTICLE_TIME_PROPERTY[state]; | |
const articles = globalState.articles[state]; | |
const monday = getMonday(new Date()); | |
monday.setHours(0, 0, 0, 0); | |
unreadCount = Object.keys(articles).filter( | |
itemId => (parseInt(articles[itemId][timeProperty]) * 1000) > monday | |
).length; | |
} | |
counter.innerHTML = ` (${unreadCount})`; | |
} | |
} | |
} | |
function setupCounter(state) { | |
const selector = ARTICLE_STATES_SELECTOR[state]; | |
let listElement = document.querySelector(selector); | |
if (listElement === null) { | |
return; | |
} | |
const counterSelector = `${ARTICLE_STATES[state]}${COUNTER_POSTFIX}`; | |
const counterElement = document.querySelector(counterSelector); | |
if (listElement && !counterElement) { | |
const detailedStatistics = ` | |
<span class="${ARTICLE_STATES[state]}${COUNTER_POSTFIX}">?</span> | |
`; | |
listElement.innerHTML = listElement.innerHTML + detailedStatistics; | |
} | |
} | |
for (let state of states) { | |
setupCounter(state); | |
countArticles(state); | |
} | |
} | |
})(window.indexedDB); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment