Thomas Lynch 3 years ago
parent 6db6be01ab
commit 60f0665ac2
No known key found for this signature in database
GPG Key ID: 36A72F7C62CF8480
  1. 178
      gulp/res/js/watchlist.js

@ -1,31 +1,92 @@
class ThreadWatcher {
//todo:
//- dont increase unread count when refreshing if already have a thread opened
//- option to hide/minimise/disable thread watcher, or not show unless there is at least 1 thread in watchlist
//- update settings export to include watched threads
//- notifications, probably in fetchthread()
//- try and deal with having more tabs open running refresh more often than necessary. maybe for this it has to
// use a serviceworker so it only has 1 thread background fetching (problems: needs https so maybe wont work on
// tor, lokinet, etc). with service worker needs to be some mechanism that only 1 client/tab send message to do
// a fetch, or maybe it only responds at most every 60 seconds since the client can share with eachother.
init() {
this.loadMap();
//todo: here, update the map and set the unread to 0 if isThread === true
//window.addEventListener('storage', () => this.loadMap()); //this complicates things, and would need to re-render list a lot
this.watchListMap = new Map(JSON.parse(localStorage.getItem('watchlist')));
window.addEventListener('storage', e => this.storageEventHandler(e));
this.settingsInput = document.getElementById('watchlist-setting');
this.clearButton = document.getElementById('watchlist-clear');
this.clearButton.addEventListener('click', () => this.clear(), false)
this.createListHtml();
this.refreshInterval = setInterval(() => this.refresh(), 3 * 1000);
const threadMatch = window.location.pathname.match(/^\/(?<threadBoard>\w+)(?:\/manage)?\/thread\/(?<threadId>\d+)\.html$/);
if (threadMatch && threadMatch.groups) {
const key = `${threadMatch.groups.threadBoard}-${threadMatch.groups.threadId}`;
const data = this.watchListMap.get(key);
if (data) {
data.unread = 0;
data.updatedDate = new Date();
this.watchListMap.set(key, data);
this.commit();
}
}
this.createList();
this.refreshInterval = setInterval(() => this.refresh(), 60 * 1000);
}
refresh() {
console.log('refreshing thread watcher');
for (let t of this.watchListMap.entries()) {
const [board, postId] = t[0].split('-');
const data = t[1];
this.fetchThread(board, postId, data); //async fetch and update
this.fetchThread(board, postId, data);
}
}
//pause() { }
//resume() { }
async fetchThread(board, postId, data) {
let res, json;
try {
res = await fetch(`/${board}/thread/${postId}.json`);
json = await res.json();
} catch (e) { /* ignore */ }
if (json && json.replies && json.replies.length > 0) {
const updatedDate = new Date(data.updatedDate);
const newPosts = json.replies.filter(r => new Date(r.date) > updatedDate);
if (newPosts.length > 0) {
data.subject = json.subject;
data.updatedDate = new Date();
data.unread += newPosts.length;
const key = `${board}-${postId}`;
this.watchListMap.set(key, data);
this.updateRow(board, postId, data);
this.commit();
}
} else if (res && res.status === 404) {
console.log('removing 404 thread from watchlist');
this.remove(board, postId);
}
}
loadMap() {
this.watchListMap = new Map(JSON.parse(localStorage.getItem('watchlist')));
storageEventHandler(e) {
if (e.storageArea === localStorage
&& e.key === 'watchlist') {
console.log('updating watchlist from another context');
const newMap = new Map(JSON.parse(e.newValue));
const deleted = [];
this.watchListMap.forEach((data, key) => {
if (!newMap.has(key)) {
const [board, postId] = key.split('-');
this.deleteRow(board, postId);
}
});
newMap.forEach((data, key) => {
const [board, postId] = key.split('-');
const oldData = this.watchListMap.get(key);
if (!oldData) {
this.addRow(board, postId, data);
} else if (oldData && (oldData.unread !== data.unread
|| oldData.subject !== data.subject)) {
this.updateRow(board, postId, data);
}
});
this.watchListMap = new Map(JSON.parse(e.newValue));
}
}
commit() {
@ -34,7 +95,7 @@ class ThreadWatcher {
this.settingsInput.value = mapSpread;
}
createListHtml() {
createList() {
const threadWatcherHtml = threadwatcher();
const footer = document.getElementById('bottom');
footer.insertAdjacentHTML('afterend', threadWatcherHtml);
@ -43,71 +104,66 @@ class ThreadWatcher {
for (let t of this.watchListMap.entries()) {
const [board, postId] = t[0].split('-');
const data = t[1];
this.add(board, postId, data, true);
this.addRow(board, postId, data);
}
}
async fetchThread(board, postId, data) {
console.log('thread watcher fetching', board, postId);
let json;
try {
json = await fetch(`/${board}/thread/${postId}.json`).then(res => res.json());
} catch (e) {
console.error(e);
}
if (json.replies.length > 0) {
const newPosts = json.replies.filter(r => new Date(r.date) > data.updatedDate);
if (newPosts.length > 0) {
console.log('new posts in watched thread', board, postId);
data.updatedDate = new Date();
data.unread += newPosts.length;
const key = `${board}-${postId}`;
this.watchListMap.set(key, data);
this.commit();
const updateRow = this.threadWatcher.querySelector(`[data-id=${key}]`);
updateRow.setAttribute('data-unread', data.unread);
//updateRow.classList.add('bold');
//todo: notification (and extra setting for if watchlist notifs)
}
add(board, postId, data) {
const key = `${board}-${postId}`;
if (this.watchListMap.has(key)) {
return;
}
this.watchListMap.set(key, data);
this.addRow(board, postId, data);
this.commit();
}
add(board, postId, data, insertOnly=false) {
const key = `${board}-${postId}`;
if (!insertOnly) {
if (this.watchListMap.has(key)) {
return; //already watching
}
console.log('adding', key, 'to watchlist');
this.watchListMap.set(key, data);
remove(board, postId) {
this.deleteRow(board, postId);
this.watchListMap.delete(`${board}-${postId}`);
this.commit();
}
clear() {
for (let t of this.watchListMap.entries()) {
const [board, postId] = t[0].split('-');
const data = t[1];
this.deleteRow(board, postId, data);
}
//todo: modify watchListItemHtml to highlight/bold, if already in selected thread
this.watchListMap = new Map();
this.commit();
}
addRow(board, postId, data) {
const watchListItemHtml = watchedthread({ watchedthread: { board, postId, ...data } });
this.threadWatcher.insertAdjacentHTML('beforeend', watchListItemHtml);
const watchedThreadElem = this.threadWatcher.lastChild;
const closeButton = watchedThreadElem.querySelector('.close');
closeButton.addEventListener('click', e => this.remove(e, key));
//watchedThreadElem.addEventListener('mouseover', () => watchedThreadElem.classList.remove('bold'))
this.commit();
closeButton.addEventListener('click', e => this.remove(board, postId));
}
remove(e, key) {
console.log('removing', key, 'from watchlist');
e.target.parentElement.remove();
this.watchListMap.delete(key);
this.commit();
deleteRow(board, postId) {
const row = this.threadWatcher.querySelector(`[data-id="${board}-${postId}"]`);
row.remove();
}
clear() {
console.log('clearing watchlist');
this.watchListMap = new Map();
Array.from(this.threadWatcher.children)
.forEach((c, i) => i > 0 && c.remove()); //remove all except first child (the draghandle)
setLocalStorage('watchlist', '[]');
this.commit();
updateRow(board, postId, data) {
const row = this.threadWatcher.querySelector(`[data-id="${board}-${postId}"]`);
if (data.unread === 0) {
row.removeAttribute('data-unread');
} else {
row.setAttribute('data-unread', data.unread);
}
}
}
const threadWatcher = new ThreadWatcher();
window.addEventListener('settingsReady', () => threadWatcher.init());
window.addEventListener('settingsReady', () => {
threadWatcher.init()
//settings shit goes here
});

Loading…
Cancel
Save