// I'm not a React developer. Be advised! // If you are, and want to rewrite this the "right way", go ahead and do it :) // This is plain Javascript and only requires basic calls to implement customizations // Do simple XHR requests to the API on Mastodon // make sure you control errors correctly, as a Mastodon Server might not setup CORS correctly const fediloveMastodon = { request: function(method, path, payload, callbk) { payload = payload || null; callbk = callbk || null; const ACCESS_TOKEN = window.fediloveApi.getAccessToken(); if (ACCESS_TOKEN === undefined) return callbk(undefined); const API_URL = 'https://'+fediloveApi.getCurrentInstance()+path; const oReq = new XMLHttpRequest(); oReq.addEventListener("load", function() { if (callbk) callbk(this.responseText) }); oReq.open(method, API_URL); oReq.setRequestHeader('Authorization', 'Bearer '+ACCESS_TOKEN); oReq.send(payload); }, get: function(path, payload, callbk) { return fediloveMastodon.request('GET', path, payload, callbk); }, post: function(path, payload, callbk) { return fediloveMastodon.request('POST', path, payload, callbk); }, API: { discardAccount: function(account_id, cback) { fediloveMastodon.post(`/api/v1/accounts/${account_id}/block`, null, function(data) { cback() }); fediloveMastodon.post(`/api/v1/follow_requests/${account_id}/reject`); }, matchAccount: function(account_id, cback) { fediloveMastodon.post(`/api/v1/accounts/${account_id}/follow`, {'reblogs': true}, function(data) { cback() }); fediloveMastodon.post(`/api/v1/follow_requests/${account_id}/authorize`); }, getRelationship: function(account_id, cback) { fediloveMastodon.get(`/api/v1/accounts/relationships?id=${account_id}`, null, cback); }, getMyFollowing: function(cback) { fediloveMastodon.get(`/api/v1/accounts/${fediloveData.myAccountId}/following`, null, cback); }, getMyRequestsToFollow: function(cback) { // to-do: find out how to list my own matches (follow requests) }, getMyFollowers: function(cback) { fediloveMastodon.get(`/api/v1/accounts/${fediloveData.myAccountId}/followers`, null, cback); }, getRequestsToFollowMe: function(cback) { fediloveMastodon.get('/api/v1/follow_requests', null, cback); }, statusFav: function(dom, status_id) { var dislike = false; var path = `/api/v1/statuses/${status_id}/favourite`; if (dom.getAttribute('data-liked') === "1") { path = `/api/v1/statuses/${status_id}/unfavourite`; dislike = true; } fediloveMastodon.post(path, {}, function(data) { if (!dislike) { $(dom).addClass('liked-msg'); $(dom).attr('data-liked', 1); dom.style.animation = "spin2 .5s linear 1"; } else { $(dom).removeClass('liked-msg'); $(dom).attr('aria-label', '0 '+$(dom).attr('aria-label')); $(dom).removeAttr('data-liked'); dom.style.animation = undefined; } }); }, sendMessage: function() { // the message is composed from the current chat Acct (@user@domain) + the compose textarea value const text = fediloveData.chatAvatarCache.acct + ' ' + $('div#chat-compose-global textarea').val(); //async function postStatus(realm, text, inReplyToId, mediaIds, sensitive, spoilerText, visibility, mediaDescriptions, inReplyToUuid, poll, mediaFocalPoints) const lastStatus = fediloveApi.getChatLastMessageId(true); fediloveFunctions.postStatus(lastStatus.id, text, lastStatus.id, [], false, undefined, 'direct', undefined, lastStatus.uuid, undefined, undefined); // empty the current compose box $('div#chat-compose-global textarea').val(''); // scroll to the end as all chat Apps do fediloveUI.scrollChatToLastItem(); } } }; var fediloveUI = { registerSwipeOnElementEvents: { elements: [], vars: { startX: null, startY: null, dist: null, threshold: 30, allowedTime: 200, elapsedTime: 0, startTime: null }, touchstart: function(e) { const vars = fediloveUI.registerSwipeOnElementEvents.vars; const touchobj = e.changedTouches[0]; vars.dist = 0; vars.startX = touchobj.pageX; vars.startY = touchobj.pageY; vars.startTime = new Date().getTime(); e.preventDefault(); }, touchmove: function(e) { e.preventDefault() }, touchend: function(e) { if (fediloveUI.registerSwipeOnElementEvents.touchendCallback != null) { const vars = fediloveUI.registerSwipeOnElementEvents.vars; const touchobj = e.changedTouches[0]; vars.dist = touchobj.pageX - vars.startX; vars.elapsedTime = new Date().getTime() - vars.startTime; if (Math.abs(vars.dist) < 20) { return e.preventDefault(); } const swiperightBol = (vars.elapsedTime <= vars.allowedTime && vars.dist >= vars.threshold); fediloveUI.registerSwipeOnElementEvents.touchendCallback(swiperightBol); e.preventDefault(); } }, touchendCallback: null }, unregisterSwipeOnElement: function(selector) { const element = document.querySelector(selector); if (element === null || element === undefined) return; fediloveUI.registerSwipeOnElementEvents.elements = fediloveUI.registerSwipeOnElementEvents.elements.filter( function(item) { return item !== selector }); element.removeEventListener('touchstart', fediloveUI.registerSwipeOnElementEvents.touchstart); element.removeEventListener('touchmove', fediloveUI.registerSwipeOnElementEvents.touchmove); element.removeEventListener('touchend', fediloveUI.registerSwipeOnElementEvents.touchend); }, registerSwipeOnElement: function(selector, cbackFunc) { const element = document.querySelector(selector); if (element === null || element === undefined) return; if (fediloveUI.registerSwipeOnElementEvents.elements.includes(selector)) return true; fediloveUI.registerSwipeOnElementEvents.elements.push(selector); fediloveUI.registerSwipeOnElementEvents.touchendCallback = cbackFunc; element.addEventListener('touchstart', fediloveUI.registerSwipeOnElementEvents.touchstart, false); element.addEventListener('touchmove', fediloveUI.registerSwipeOnElementEvents.touchmove, false); element.addEventListener('touchend', fediloveUI.registerSwipeOnElementEvents.touchend, false); }, meetAccountImageDirection: function(direction) { if (!window.location.pathname.startsWith('/accounts') || fediloveUI.meetAccountImageLocked) return; fediloveUI.meetAccountImageLocked = true; var dontMove = false; if (direction === 'prev') fediloveData.meetAccountCurrentImg -= 1; else if (direction === 'next') fediloveData.meetAccountCurrentImg += 1; else dontMove = true; const last = $('div.virtual-list > div.virtual-list-item').length - 1; if (fediloveData.meetAccountCurrentImg > last) { fediloveData.meetAccountCurrentImg = last > 0 ? last : 0; dontMove = true; } else if (fediloveData.meetAccountCurrentImg < 0) { fediloveData.meetAccountCurrentImg = 0; dontMove = true; } document.querySelector('#meet-navigation > #profile-nav > #next').style = ''; document.querySelector('#meet-navigation > #profile-nav > #prev').style = ''; if (fediloveData.meetAccountCurrentImg === last) document.querySelector('#meet-navigation > #profile-nav > #next').style = 'display: none'; else if (fediloveData.meetAccountCurrentImg === 0) document.querySelector('#meet-navigation > #profile-nav > #prev').style = 'display: none'; const _setSelectedVisible = function() { if ($('div.virtual-list > div.virtual-list-item')[fediloveData.meetAccountCurrentImg] !== undefined) { $('div.virtual-list > div.virtual-list-item').attr('style', 'display: none'); $('div.virtual-list > div.virtual-list-item')[fediloveData.meetAccountCurrentImg].style = ''; } fediloveUI.meetAccountImageLocked = false; }; if (dontMove === false) { if (direction === 'next') $('div.virtual-list')[0].style.animation = 'dismissMeet .25s linear 1'; else if (direction === 'prev') $('div.virtual-list')[0].style.animation = 'acceptMeet .25s linear 1'; setTimeout(function() { if ($('div.virtual-list')[0] !== undefined) { $('div.virtual-list')[0].style.animation = ''; _setSelectedVisible(); } }, 220); } else { _setSelectedVisible() } }, meetPageGoToCurrentAccount: function(isRightSwipe) { isRightSwipe = isRightSwipe || false; const accObj = fediloveApi.getMeetAccount(); if (!isRightSwipe && accObj !== undefined) { const elem2Anim = $('div.virtual-list > div.virtual-list-item.meetshown:visible')[0]; if (elem2Anim !== undefined) elem2Anim.style.animation = 'dismissMeet .25s linear 1'; setTimeout(function() { fediloveApi.redirect(`/accounts/${accObj.id}`) }, 200); } }, scrollChatToLastItem: function() { const startLen = $('div.the-list > div').length; var count = 0; var _this = setInterval(function() { count++; var newLen = $('div.the-list > div').length; if (count >= 100 || newLen != startLen) { const elem = document.querySelector('div.the-list > div:last-child'); if (elem !== null) elem.scrollIntoView(); clearInterval(_this); } }, 150); }, paintChatAvatarAndName: function(accid, acct, avatar, name) { accid = accid || null; acct = acct || null; avatar = avatar || null; name = name || null; if ((accid+acct+avatar+name) === 0) { accid = 0; avatar = '/missing.png'; name = '...'; } else { fediloveData.chatAvatarCache = { id: accid, acct: acct, avatar: avatar, name: name }; // add domain part on acct on painting it to be clear if (acct.split('@').length-1 === 1) { acct += `@${fediloveApi.getCurrentInstance()}`; } } // to-do: check is it XSS safe to add it like this :) ?? $('div#chat-party-global > div#image > img').attr('src', avatar); $('div#chat-party-global > div#name > a > span').html(name); $('div#chat-party-global > div#name > span').text(acct); if (accid != 0) $('div#chat-party-global > div#name > a').attr('href', `/accounts/${accid}`); }, fillMeetRelationships: function() { var countMax = 0; const _this = setInterval(function() { if (countMax > 100) { return clearInterval(_this); } const items = fediloveFunctions.filterItemsForFedilove(window.__store.get().timelineItemSummaries); if (items !== null && items.length > 0) { var refIds = []; for (var it of items) { if (it.accountId !== undefined && !refIds.includes(it.accountId)) refIds.push(it.accountId); } if (fediloveData.pagesLoaded['meet']) { for (var accId of refIds) { window.__database.getRelationship(fediloveApi.getCurrentInstance(), accId+"").then(function(rel) { if (rel !== undefined && fediloveData.meetRelationships[rel.id] === undefined) fediloveData.meetRelationships[rel.id] = rel; if (Object.keys(window.fediloveData.meetRelationships).length === refIds.length) fediloveData.meetRelationshipsFilled = true; }); } } else { for (var accId of refIds) { fediloveMastodon.API.getRelationship(accId+"", function(data) { const json = JSON.parse(data)[0]; if (json && fediloveData.meetRelationships[json.id] === undefined) fediloveData.meetRelationships[json.id] = json; fediloveData.meetRelationshipsFilled = true; fediloveData.pagesLoaded['meet'] = true; }); } } clearInterval(_this); } countMax++; }, 100); }, toast: function(whatToSay, time, className, renderHtml) { if (!fediloveData.toastInitialized) return; if (time !== undefined) window.fediloveData.toastTime = time; if (className !== undefined) window.fediloveData.toastClass = className; if (renderHtml) { $('#theToast #plain-text')[0].style = 'display: none'; $('#theToast #html-text').html(whatToSay); } else { $('#theToast #plain-text')[0].style = ''; $('#theToast #html-text').html(''); } window.__toast.say(whatToSay); }, animateMainContentSwipe: function(animName, cback) { $('div.virtual-list')[0].style.animation = `${animName} .25s linear 1`; setTimeout(cback, 220); }, onAccountNope: function() { const accObj = fediloveApi.getMeetAccount(); if (accObj === undefined) return; if (!fediloveData.tmpDiscardedAccounts.includes(accObj.id)) fediloveData.tmpDiscardedAccounts.push(accObj.id); fediloveMastodon.API.discardAccount(accObj.id, function() { fediloveUI.animateMainContentSwipe('nopeMeet', function() { window.history.back() }); }); }, onAccountMatch: function() { const accObj = fediloveApi.getMeetAccount(); if (accObj === undefined) return; if (fediloveData.meetRelationships[accObj.id] !== undefined) fediloveData.meetRelationships[accObj.id] = { 'id': accObj.id, 'requested': true } fediloveUI.toast(window.__intl.matchAction, 6000, 'match', true); fediloveMastodon.API.matchAccount(accObj.id, function() { }); }, onAccountMaybe: function() { const accObj = fediloveApi.getMeetAccount(); if (accObj === undefined) return; const expire = 24 * (60*60*1000); // hours. Expire the maybe after 24 hours var accounts = {}; if (window.localStorage.store_maybeAccounts !== undefined) accounts = JSON.parse(window.localStorage.store_maybeAccounts); accounts[accObj.id] = Date.now() + expire; window.localStorage.store_maybeAccounts = JSON.stringify(accounts); fediloveUI.toast(window.__intl.maybeAction, 2000, 'maybe'); fediloveUI.animateMainContentSwipe('acceptMeet', function() { window.history.back() }); } }; // objects to access from React code var fediloveApi = { redirect: function(url) { const _id = 'link-' + Date.now(); var a = document.createElement('a'); a.href = url; a.id = _id; document.body.append(a); document.getElementById(_id).click(); document.body.removeChild(a); }, getMeetAccount: function(directlyFromStore) { directlyFromStore = directlyFromStore || false; if (window.localStorage.store_meetAccounts === undefined) return undefined; if (document.getElementById(window.fediloveData.currentIDMeetTimeline) === undefined) return undefined; if (directlyFromStore) return window.__store.get().currentAccountProfile; var accId; if (window.location.pathname == '/meet') accId = $(document.getElementById(window.fediloveData.currentIDMeetTimeline)).data('account'); else if (window.location.pathname.startsWith('/accounts/')) accId = window.location.pathname.match(/^\/accounts\/(\d+)/)[1]; if (accId === undefined) return undefined; const aJson = JSON.parse(window.localStorage.store_meetAccounts); if (aJson[accId] === undefined) { if (window.__store.get().currentAccountProfile === undefined) return undefined; return window.__store.get().currentAccountProfile; } return aJson[accId]; }, getChatMessageId: function() { var parts = window.location.pathname.split('/'); return parts[parts.length-1]; }, getChatLastMessageId: function(andUuid) { andUuid = andUuid || false; const lastUuid = $('div.the-list article.status-article').last().attr('id'); if (lastUuid === undefined) { return undefined; } const parts = lastUuid.split('/'); return andUuid? { id: parts[parts.length-1], uuid: lastUuid } : parts[parts.length-1]; }, getAccessToken: function() { const instance = fediloveApi.getCurrentInstance(); if (instance === undefined) return undefined; return JSON.parse(localStorage.store_loggedInInstances)[instance].access_token; }, getCurrentInstance: function() { if (localStorage.store_currentInstance === undefined) return undefined; return JSON.parse(localStorage.store_currentInstance); }, getFedilovedName: function(nameTxt, emojis) { emojis = emojis || undefined; nameTxt = nameTxt.replace(/#fedilove/gi, '').trim(); nameTxt = nameTxt.replace(/\s+/g, ' ').trim().replace(/\s/g, ' '); if (emojis === undefined || fediloveFunctions.emojifyText === undefined) return nameTxt; return fediloveFunctions.emojifyText(nameTxt, emojis); } }; var fediloveFunctions = { filterItemsForFedilove: function(items) { if (typeof items == "string") items = JSON.parse(items); if (items === null || items === undefined) return null; let newItems = []; for (var item of items) { if (item.display_name !== undefined) { if (item.id != window.fediloveData.myAccountId) newItems.push(item); continue; } if (item.account === undefined) { if (item.accountId !== undefined && item.accountId !== window.fediloveData.myAccountId) newItems.push(item); continue; } if (item.account.display_name.toLowerCase().includes('#fedilove') && item.content.replace(/<[^<>]+>/g, '').toLowerCase().includes('#fedilove') && item.account.id != window.fediloveData.myAccountId) { newItems.push(item); } } return newItems; } }; var fediloveData = { chatAvatarCache: undefined, meetAccountCurrentImg: 0, meetAccountImageLocked: false, myAccountId: undefined, currentAccountIsEmpty: false, currentIDMeetTimeline: null, tmpDiscardedAccounts: [], meetRelationships: {}, meetRelationshipsFilled: false, gotEmojifyTextFunction: false, composeTxtKeypressEvent: false, toastTime: undefined, toastClass: undefined, toastInitialized: false, pagesLoaded: {}, tpls: {} }; var fediloveEvents = { onGotEmojifyTextFunction: function() { fediloveData.gotEmojifyTextFunction = true; }, onChatGetData: function(data) { // dont do anything if avatar and name is cached if (fediloveData.chatAvatarCache !== undefined) return; // waits for the React code to call the "onGotEmojifyTextFunction" so we can use it var waitForEmojifyAndDo = function(accid, acct, avatar, dname, emojis) { var count = 0; var _this = setInterval(function() { if (count > 100) { clearInterval(_this); return; } if (fediloveData.gotEmojifyTextFunction) { fediloveUI.paintChatAvatarAndName(accid, acct, avatar, fediloveApi.getFedilovedName(dname, emojis)); clearInterval(_this); } count++; }, 150); }; if (data) { // if the message is mine, search which account is doing it for, // and do the same with the party's data if (fediloveData.myAccountId == data.account.id) { var account_id = data.in_reply_to_account_id; if (account_id === null && data.mentions.length > 0) { account_id = data.mentions[0].id; } if (account_id === null || account_id === undefined) return; fediloveMastodon.get(`/api/v1/accounts/${account_id}`, {}, function(newData) { var json = JSON.parse(newData); if (json !== undefined) { waitForEmojifyAndDo( account_id, `@${json.acct}`, json.avatar, json.display_name, json.emojis ); } }); } else { // this means the message we are loading is from the other party, so we continue as normal waitForEmojifyAndDo( data.account.id, `@${data.account.acct}`, data.account.avatar, data.account.display_name, data.account.emojis ); } } }, onEmojiPicked: function(emoji) { var $txt = $("#chat-compose-global textarea"); var caretPos = $txt[0].selectionStart; var textAreaTxt = $txt.val(); $txt.val(textAreaTxt.substring(0, caretPos) + emoji + textAreaTxt.substring(caretPos)); }, onNewNotification: function (dataItems) { if (window.location.pathname.startsWith('/statuses/')) { if (fediloveFunctions.updateStatusAndThread !== undefined) { fediloveFunctions.updateStatusAndThread( fediloveApi.getCurrentInstance(), fediloveApi.getAccessToken(), window.location.pathname, fediloveApi.getChatMessageId() ); fediloveUI.scrollChatToLastItem(); } } } }; // this is our URL-based customizations made by JavaScript function fedilove_customization() { document.body.classList = ''; document.getElementById('sapper').style = ''; if ([undefined,"undefined"].includes(localStorage.store_currentInstance)) { $('body').addClass('not-logged-in'); if (window.location.pathname == '/') { $('body').addClass('home'); } return; } if (fediloveData.myAccountId === undefined && window.__store.get().verifyCredentials !== undefined) { var aCountMax = 0; const _a = setInterval(function() { if (aCountMax > 20) return clearInterval(_a); const theData = window.__store.get().verifyCredentials[fediloveApi.getCurrentInstance()]; if (theData) { fediloveData.myAccountId = theData ? theData.id : undefined; return clearInterval(_a); } aCountMax++; }, 500); } if (!fediloveData.toastInitialized) { var aCountMax = 0; const _a = setInterval(function() { if (aCountMax > 20) return clearInterval(_a); if (window.__toast) { window.fediloveData.toastClass = 'notshown'; window.fediloveData.toastTime = 10; window.__toast.say('...'); fediloveData.toastInitialized = true; return clearInterval(_a); } aCountMax++; }, 500); } if (document.querySelector('#main-nav > div#dummy-nav') != null) { document.querySelector('#main-nav > div#dummy-nav').remove(); document.querySelector('#main-nav > ul.main-nav-ul').style = ''; } document.querySelector('.main-content').classList = "main-content"; document.querySelector('#meet-navigation').style = 'display: none'; document.querySelector('#meet-navigation > #profile-nav').style = 'display: none'; document.querySelector('#chat-compose-global').style = 'visibility: collapse'; document.querySelector('#chat-party-hide').style = 'display: none !important'; document.querySelector('nav#main-nav > ul.main-nav-ul').style = ''; fediloveUI.unregisterSwipeOnElement('main.infinite-scroll-page'); fediloveUI.unregisterSwipeOnElement('#meet-navigation #anim-swipe'); fediloveData.currentAccountIsEmpty = false; $('nav#main-nav li.main-nav-li svg').each(function() { $(this).removeClass('active'); }); // add some animations ( i can't with sass D: ) if (document.getElementById('fedilove-animations') === null) { var style = document.createElement('style'); style.id = 'fedilove-animations'; style.innerHTML = ` @keyframes spin2 { 100% { transform:rotate(-360deg); } } @keyframes fadeOut { 0% {opacity: 1;} 100% {opacity: 0;} } @keyframes dismissMeet { 0% {position: relative; left: 0} 100% {position: relative; left: -95%} } @keyframes acceptMeet { 0% {position: relative; left: 0} 100% {position: relative; left: 95%} } @keyframes nopeMeet { 0% {position: relative; top: 0} 100% {position: relative; top: 95vh} } @keyframes yepMeet { 0% {position: relative; top: 0} 100% {position: relative; top: -95vh} } `; document.body.appendChild(style); } if (window.location.pathname === '/notifications') { $('nav#main-nav li.main-nav-li svg')[1].classList += ' active'; } else if (window.location.pathname.startsWith('/statuses/')) { $('div.main-content').addClass('chat'); $('body').addClass('chat'); document.querySelector('#chat-compose-global').style = ''; document.querySelector('#chat-party-hide').style = ''; document.querySelector('nav#main-nav > ul.main-nav-ul').style = 'display: none !important'; if (!fediloveData.composeTxtKeypressEvent) { $('div#chat-compose-global textarea').keypress(function(e) { const keycode = (e.keyCode ? e.keyCode : e.which); if (keycode == '13') { if (!e.ctrlKey && !e.shiftKey) { e.stopPropagation(); e.preventDefault(); setTimeout(function() { fediloveMastodon.API.sendMessage() }, 100); return true; } else { if (!e.shiftKey && e.ctrlKey) { $(this).val($(this).val()+'\n'); } } } }); fediloveData.composeTxtKeypressEvent = true; } // ******* // load cached avatars or paint it empty (automated process after this will fill it) if (fediloveData.chatAvatarCache !== undefined) { fediloveUI.paintChatAvatarAndName( fediloveData.chatAvatarCache.id, fediloveData.chatAvatarCache.acct, fediloveData.chatAvatarCache.avatar, fediloveData.chatAvatarCache.name ); } else { fediloveUI.paintChatAvatarAndName(); } // this function changes the css class on articles (messages) // that match the given account_id var theint = 100; const _this = setInterval(function() { // interval auto-destroyer if (!window.location.pathname.startsWith('/statuses/')) { clearInterval(_this); return; } // paint MY messages as mine if (fediloveData.myAccountId !== undefined) { $('div.main-content.chat article.status-article').each(function(i) { if ($(this).find('a.status-author-name').attr('href').endsWith(`/accounts/${fediloveData.myAccountId}`)) { $(this).addClass('mymsg'); theint = 250; } else { $(this).addClass('partymsg'); } }); } // paint LIKES $('a.status-favs-reblogs.status-favs').each(function(i) { // easy, aria-label contains the times this status was fav // so, we use this as the input source to search for "0", // if no "0" found in text, we mark element as liked so we can apply styles and "undo like" function if ($(this).attr('aria-label').search(/0/) == -1) { $(this).addClass('liked-msg'); $(this).attr('data-liked', 1); } }); // remove liking functionality from our own messages $('div.the-list article.status-article.mymsg a.status-favs-reblogs').each(function(i) { $(this).attr('onclick', 'return false;'); }); // resize likes acording to the outer parent $('div.main-content.chat div.the-list article.status-article').each(function(e) { $(this).find('div.like-div').width($(this).width()); }); }, theint); } else if (window.location.pathname == '/matches') { $('div.main-content').addClass('matches'); if ($('#tpl-follow').length > 0 && fediloveData.tpls.accountMatches === undefined) { var elem = $('#tpl-follow').clone(); elem[0].removeAttribute('id'); elem[0].removeAttribute('style'); fediloveData.tpls.accountMatches = elem[0].outerHTML; } var filledOnce = false; const _fillAccountMatches = function(accounts) { if (!filledOnce) { $('#manage-matches').html(''); filledOnce = true; } var i = 0; for (var account of accounts) { var acct = account.acct; if (acct.charAt(0) !== '@') acct = `@${acct}`; if (acct.split('@').length-1 === 1) acct += `@${fediloveApi.getCurrentInstance()}`; const dname = fediloveApi.getFedilovedName(account.display_name, account.emojis); var htm = fediloveData.tpls.accountMatches; htm = htm.replace(/\[userID\]/g, account.id); htm = htm.replace(/\[userAvatar\]/g, account.avatar); htm = htm.replace(/\[originalAccountUrl\]/g, account.url); htm = htm.replace(/\[userHandle\]/g, acct); htm = htm.replace(/\[displayName\]/g, dname); $('#manage-matches').append(htm); if (i < accounts.length -1) $('#manage-matches').append('
'); else $('#manage-matches').append(''); i++; } }; fediloveMastodon.API.getRequestsToFollowMe(function(data) { const items = fediloveFunctions.filterItemsForFedilove(data); _fillAccountMatches(items); }); } else if (window.location.pathname == '/direct') { $('div.main-content').addClass('direct'); $('body').addClass('direct'); $('nav#main-nav li.main-nav-li svg')[2].classList += ' active'; } else if (window.location.pathname.startsWith('/settings')) { $('nav#main-nav li.main-nav-li svg')[3].classList += ' active'; } else if (window.location.pathname.startsWith('/accounts/') && window.location.pathname.match(/^\/accounts\/\d+$/) !== null) { $('div.main-content').addClass('account'); $('body').addClass('account'); document.querySelector('nav#main-nav > ul.main-nav-ul').style = 'display: none !important'; document.querySelector('#meet-navigation').style = ''; document.querySelector('#meet-navigation > #profile-nav').style = ''; $('#meet-navigation > #anim-swipe').html(''); // prev and next navigation const cloneBackSvg = function(selector) { $(selector).html(''); $(selector).append( $('#meet-navigation > #back > svg').clone()); }; cloneBackSvg('#meet-navigation > #profile-nav > #prev'); cloneBackSvg('#meet-navigation > #profile-nav > #next'); fediloveUI.meetAccountImageDirection(null); // clone the first element and include the account header image var countmax = 0; const _applyImageTo = function(selector) { setTimeout(function() { // set image of first item to the account header (100ms after cloning the object) const accObj = window.fediloveApi.getMeetAccount(true); if (accObj !== undefined) { if ($(selector)[0] !== undefined) $(selector)[0].src = accObj.header; } }, 100); }; const _this = setInterval(function() { if (!window.location.pathname.startsWith('/accounts')) { clearInterval(_this); return; } // max interval time: 10 seconds if (countmax > 50 || $('div.virtual-list > div.virtual-list-item').length > 0) { if ($('div.virtual-list > div.virtual-list-item').length > 0) { // clone the first element, insert it at the next position const _e = $('div.virtual-list > div.virtual-list-item:last').clone(); $(_e).insertAfter('div.virtual-list > div.virtual-list-item:last'); _applyImageTo('div.virtual-list > div.virtual-list-item:last div.status-media img'); fediloveUI.registerSwipeOnElement('main.infinite-scroll-page', function(isRightSwipe) { if (isRightSwipe) fediloveUI.meetAccountImageDirection('prev'); else fediloveUI.meetAccountImageDirection('next'); }); } clearInterval(_this); } else if ($('div.virtual-list > div.nothing-to-show').length === 1) { if ($('div.virtual-list > div.no-images-account').length === 0) $('div.virtual-list').append(''); _applyImageTo('div.virtual-list > div.no-images-account > img.fixed-size-img'); $('#meet-navigation > #profile-nav > #next')[0].style = 'display: none !important'; clearInterval(_this); } countmax++; }, 200); } else if (window.location.pathname == '/meet') { $('div.main-content').addClass('meet'); $('body').addClass('meet'); document.querySelector('nav#main-nav > ul.main-nav-ul').style = 'display: none !important'; document.querySelector('#meet-navigation').style = ''; // swipe left animation $('#meet-navigation > #anim-swipe').html(''); for (var i of [1,2,3]) { var elem = $('#meet-navigation > #back > svg').clone(); $('#meet-navigation > #anim-swipe').append(elem); setTimeout(function(it) { it.attr('style', 'animation: fadeOut .5s linear infinite'); }, (i*250), elem); } fediloveData.meetRelationshipsFilled = false; fediloveUI.fillMeetRelationships(); fediloveUI.registerSwipeOnElement('#meet-navigation #anim-swipe', function(swiperightBol) { fediloveUI.meetPageGoToCurrentAccount(swiperightBol) }); const _discardAccountUI = function(accId) { $('article[data-account='+accId+']').each(function() { $(this).parent().addClass('meetnotshown'); if ($(this)[0].id === fediloveData.currentIDMeetTimeline) fediloveData.currentIDMeetTimeline = null; }); }; // "maybe" Accounts are stored on localStorage // as there is no way to save this on Mastodon const nowDate = Date.now(); const maybeAccounts = localStorage.store_maybeAccounts !== undefined ? JSON.parse(localStorage.store_maybeAccounts) : []; // make sure only the wanted items in timeline are shown var prevItems; var itemsDrawnOnce = false; const _this = setInterval(function() { if (window.location.pathname !== '/meet') return clearInterval(_this); if (!fediloveData.meetRelationshipsFilled) return; // added when there is no more profiles to Meet if ($('div#noone-to-meet').length === 0) $('div.timeline').parent().append(' '); // remove discarded accounts from timeline (temporal, // if /meet is reloaded, blocked accounts won't show either) prevItems = $('div.virtual-list > div.virtual-list-item:visible'); // discard temporary added accounts (by "Nope" action) for (var accId of fediloveData.tmpDiscardedAccounts) _discardAccountUI(accId); // discard localStorage powered maybe accounts (by "Maybe" action) for (var accId of Object.keys(maybeAccounts)) if (maybeAccounts[accId] !== undefined && maybeAccounts[accId] > nowDate) _discardAccountUI(accId); // discard the ones we "matched" (by "Match" action for (var accId of Object.keys(fediloveData.meetRelationships)) if (fediloveData.meetRelationships[accId].requested) _discardAccountUI(accId); // paint the current Meet account, after adding classes to hide what we want const items = $('div.virtual-list > div.virtual-list-item:visible'); if (items.length > 0) { if (window.fediloveData.currentIDMeetTimeline === null || document.getElementById(window.fediloveData.currentIDMeetTimeline) === null) { const itemToId = items.first().find('article:first'); if (itemToId[0] !== undefined) { window.fediloveData.currentIDMeetTimeline = itemToId[0].id; itemToId.parent().addClass('meetshown'); } } else { items.removeClass('meetshown'); const itemToId = $(document.getElementById(window.fediloveData.currentIDMeetTimeline)); if (itemToId.length > 0) itemToId.parent().addClass('meetshown'); } } else { if (prevItems.length != items.length && $('div#noone-to-meet').length > 0) $('div#noone-to-meet')[0].style = ''; } }, 200); } else if (window.location.pathname.startsWith('/notifications/mentions')) { $('nav.notification-filters li > a.focus-fix').attr('onclick', 'return false;'); } if (!window.location.pathname.startsWith('/statuses/')) { fediloveData.chatAvatarCache = undefined; } } // we inject this script.js into the React framework at timelines.js // Watch for URL changes every 1/2 seconds (this is efficient, don't worry about it!) // and dispatch a call to "fedilove_customization" to load page customizations depending on URL context var __window_url = null; var __window_url_old = null; (function() { const _this = setInterval(function() { if (typeof $ !== "undefined") { clearInterval(_this); setInterval(function() { const newurl = window.location.pathname; if (newurl != __window_url) { __window_url_old = __window_url; __window_url = newurl; fedilove_customization(); } }, 100); } }); })();