TU Berlin ISIS - Userscript

// ==UserScript== // @name TU Berlin - New posts badge // @description Shows badge for new posts next to visible courses in sidebar // @namespace https://tu-berlin.de/ // @version 0.2 // @author David // @match https://isis.tu-berlin.de/* // @grant GM_addStyle // @run-at document-idle // ==/UserScript== (function() { 'use strict'; const cachedFetch = async (url, options) => { let expiry = 10 * 60; // 10 min default if (typeof options === 'number') { expiry = options; options = undefined; } else if (typeof options === 'object') { expiry = options.seconds || expiry; } // Use the URL as the cache key to sessionStorage let cacheKey = url; let cached = localStorage.getItem(cacheKey); let whenCached = localStorage.getItem(cacheKey + ':ts'); if (cached !== null && whenCached !== null) { let age = (Date.now() - whenCached) / 1000; if (age < expiry) { let response = new Response(new Blob([cached])); return Promise.resolve(response); } else { // We need to clean up this old key localStorage.removeItem(cacheKey); localStorage.removeItem(cacheKey + ':ts'); } } return fetch(url, options).then(response => { if (response.status === 200) { let ct = response.headers.get('Content-Type'); if (ct && (ct.match(/application\/json/i) || ct.match(/text\//i))) { response.clone().text().then(content => { localStorage.setItem(cacheKey, content); localStorage.setItem(cacheKey + ':ts', Date.now()); }); } } return response; }); }; const deleteFetchedItem = (url) => localStorage.removeItem(url) && localStorage.removeItem(url + ':ts'); // Add some custom CSS GM_addStyle(`#nav-drawer .list-group .list-group-item.custom-course-alert { background-color: rgba(0, 0, 0, 0.1); } #nav-drawer .list-group .list-group-item.custom-course-alert:hover { background-color: #c50e1f; color: #ffffff; } #nav-drawer .list-group .list-group-item.custom-course-alert .media-body:after { content: " \uD83D\uDD14(" attr(data-unread-count) ")"; font-size: smaller; white-space: nowrap; }`); // DOM selectors const selectors = { courses: '#nav-drawer a.list-group-item[data-parent-key="mycourses"]', courseLink: '#page-navbar .breadcrumb-item:last-child a', unreadNotificationLink: '#page-content .activityinstance .unread a', courseLinkFooter: '#page-footer .homelink a', unreadLinks: '#region-main thead .replies a, #region-main tbody .replies span.unread' }; // Fetch all courses and process it let parser = new DOMParser(); let $courses = document.querySelectorAll(selectors.courses); Promise.all([...$courses].map(ele => ele.href).map(url => cachedFetch(url).then(resp => resp.text()))) .then(htmls => [...htmls].map(html => parser.parseFromString(html, 'text/html')) .filter(doc => doc.querySelector(selectors.unreadNotificationLink)) .map(doc => [ doc.querySelector(selectors.courseLink), doc.querySelectorAll(selectors.unreadNotificationLink) ]) .map(arr => [ new URLSearchParams(arr.shift().search).get('id'), [...arr.shift()].map(link => link.innerText.split(' ').shift()) .map(count => parseInt(count, 10)) .reduce((sum, n) => sum + n) ])) .then(posts => posts.forEach(post => { let $course = document.querySelector(selectors.courses + '[data-key="' + post.shift() + '"]'); $course.classList.add('custom-course-alert'); $course.querySelector('.media-body').dataset.unreadCount = post.shift(); })) .catch(err => console.error('Error:', err)); // Unread links/discussion view => Clear cache storage if (document.URL.indexOf('mod/forum/discuss') > 0) deleteFetchedItem(document.querySelector(selectors.courseLinkFooter).href); document.querySelectorAll(selectors.unreadLinks).forEach(ele => ele.addEventListener('click', deleteFetchedItem(document.querySelector(selectors.courseLinkFooter).href), false)); })();

Be the first to comment

You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.