- // ==UserScript==
- // @name DTF Enhancer
- // @namespace http://tampermonkey.net/
- // @version 0.0.5
- // @description Выводит список подписок в сайдбаре и раскрывает список комментов
- // @author You
- // @match *://dtf.ru/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=dtf.ru
- // @run-at document-end
- // @grant GM.getValue
- // @grant GM.setValue
- // @license MIT
- // ==/UserScript==
-
- (function() {
- const dict = {"Ё":"YO","Й":"I","Ц":"TS","У":"U","К":"K","Е":"E","Н":"N","Г":"G","Ш":"SH","Щ":"SCH","З":"Z","Х":"H","Ъ":"'","ё":"yo","й":"i","ц":"ts","у":"u","к":"k","е":"e","н":"n","г":"g","ш":"sh","щ":"sch","з":"z","х":"h","ъ":"'","Ф":"F","Ы":"I","В":"V","А":"A","П":"P","Р":"R","О":"O","Л":"L","Д":"D","Ж":"ZH","Э":"E","ф":"f","ы":"i","в":"v","а":"a","п":"p","р":"r","о":"o","л":"l","д":"d","ж":"zh","э":"e","Я":"Ya","Ч":"CH","С":"S","М":"M","И":"I","Т":"T","Ь":"'","Б":"B","Ю":"YU","я":"ya","ч":"ch","с":"s","м":"m","и":"i","т":"t","ь":"'","б":"b","ю":"yu"};
-
- const transliterate = (word) => word.split('').map((char) => dict[char] || char).join("");
-
- const cn = (tagName, attrs = {}, childrenList = [], parentNode = null) => {
- const node = document.createElement(tagName);
-
- if (typeof attrs === 'object') {
- for (const attrsKey in attrs) node.setAttribute(attrsKey, attrs[attrsKey]);
- }
-
- if (Array.isArray(childrenList)) {
- childrenList.forEach(child => {
- node.appendChild(typeof child === 'string' ? document.createTextNode(child) : child);
- });
- }
-
- if (parentNode) {
- parentNode.appendChild(node);
- }
-
- return node;
- };
-
- const getDomElementAsync = (selector, timerLimit = 10000) => {
- return new Promise((resolve, reject) => {
- try {
- setTimeout(() => {
- console.log(`Время ожидания DOM элемента ${selector} истекло (${timerLimit / 1000}s)`);
- resolve(null);
- }, timerLimit);
-
- let timerId;
-
- const tick = () => {
- const element = document.querySelector(selector);
-
- if (element) {
- clearTimeout(timerId);
- resolve(element);
- } else {
- timerId = setTimeout(tick, 100);
- }
- };
-
- tick();
- } catch (e) {
- reject(e);
- }
- });
- };
-
- const debounce = (func, wait) => {
- let timeout;
- return function (...args) {
- return new Promise(resolve => {
- clearTimeout(timeout);
- timeout = setTimeout(() => {
- timeout = null;
- Promise.resolve(func.apply(this, [...args])).then(resolve);
- }, wait);
- });
- };
- };
-
- const observeUrlChange = async (onChange) => {
- await GM.setValue('currentUrl', window.location.href);
-
- const onChangeHandler = async () => {
- const oldHref = await GM.getValue('currentUrl');
- const newHref = window.location.href;
-
- if (oldHref !== newHref) {
- console.log('observeUrlChange');
-
- await GM.setValue('currentUrl', newHref);
- onChange?.();
- }
- };
-
- const debouncedOnChangeHandler = debounce(onChangeHandler, 500);
-
- const observer = new MutationObserver(debouncedOnChangeHandler);
-
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- });
- };
-
- const injectStyles = () => {
- const styles = `
- .sidebar-subs {
- display: flex;
- flex-direction: column;
- overflow: auto;
- margin: 24px 0;
- }
-
- .sidebar-sibs__title {
- margin-bottom: 16px;
- padding: 0 8px;
- font-weight: 500;
- font-size: 18px;
- }
-
- .sidebar-sibs__list {
- padding: 0;
- }
-
- .sidebar-item._sub img.icon {
- width: 24px;
- border-radius: 50%;
- }
-
- .sidebar-item._sub span {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- min-width: 1px;
- }
-
- /* перебиваем стили DTF */
- .sidebar__main {
- display: flex;
- flex-direction: column;
- flex-shrink: 0;
- min-width: 1px;
- overflow: auto;
- }
-
- .sidebar-item {
- flex-shrink: 0;
- }
-
- .sidebar-editor-button {
- margin-top: 24px;
- }
-
- .sidebar-editor-buttons {
- margin-top: auto;
- margin-bottom: 16px;
- }
-
- .account-menu {
- visibility: hidden;
- }
-
- body.dtf-subs-script-inited .account-menu {
- visibility: visible;
- }
- `;
-
- document.head.insertAdjacentHTML("beforeend", `<style type="text/css" id="dtfSubsStyles">${styles}</style>`)
- };
-
- const fetchSubs = async (userId) => {
- const resp = await fetch(`https://api.dtf.ru/v2.5/subsite/subscriptions?subsiteId=${userId}`);
- const { result } = await resp.json();
-
- return result.items;
- }
-
- const getImageUrl = (uuid) => `https://leonardo.osnova.io/${uuid}/-/scale_crop/32x32/`;
-
- const createSidebarItem = (name, imageId, href) => {
- const imgEl = cn('img', { class: 'icon', src: getImageUrl(imageId) });
- const nameEl = cn('span', {}, [name]);
- const result = cn('a', { class: 'sidebar-item _sub', href: transliterate(href), alt: name }, [imgEl, nameEl]);
-
- return result;
- };
-
- const createSidebarList = (items) => {
- const sidebarItems = items.map((item) => {
- const href = item.uri || `/u/${item.id}-${item.name.toLowerCase()}`;
-
- return createSidebarItem(item.name, item.avatar.data.uuid, href);
- });
-
- const title = cn('div', { class: 'sidebar-sibs__title' }, ['Подписки:']);
-
- const listWrapper = cn('div', { class: 'sidebar-sibs__list modal-window__content' }, sidebarItems);
-
- return cn('div', { class: 'sidebar-subs' }, [title, listWrapper]);
- };
-
- const getProfileUrl = async () => {
- const userButton = await getDomElementAsync('.user');
- userButton.click();
-
- const profileMenuItem = await getDomElementAsync('.account-menu__user-card');
- userButton.click();
-
- return profileMenuItem.href;
- }
-
- const getUserId = async () => {
- const profileUrl = await getProfileUrl();
- const userId = profileUrl.split('/u/')[1].split('-')[0];
-
- return userId || null;
- };
-
- const injectSubscriptions = async () => {
- const userId = await getUserId();
-
- document.body.classList.add('dtf-subs-script-inited');
-
- if (!userId) {
- return;
- }
-
- const subs = await fetchSubs(userId);
- const list = createSidebarList(subs);
-
- const sidebarButton = await getDomElementAsync('.sidebar-editor-button');
- sidebarButton.after(list);
- };
-
- const runAutoExpandComments = () => {
- const expandComments = async () => {
- const expandCommentsButton = await getDomElementAsync('.comments-limit__expand');
- expandCommentsButton?.click();
- };
-
- observeUrlChange(expandComments);
-
- expandComments();
- };
-
- const init = async () => {
- injectStyles();
- injectSubscriptions();
- runAutoExpandComments();
- };
-
-
- init();
- })();