Last active
May 28, 2018 02:23
-
-
Save iwinux/796f8b3b2da5d3aa8fc686dc4a44c8b8 to your computer and use it in GitHub Desktop.
UI / UX Tweaks for BearyChat
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 barely-tweaks | |
// @namespace http://tampermonkey.net/ | |
// @version 0.6 | |
// @description UI / UX Tweaks for BearyChat | |
// @author E.T | |
// @match https://*.bearychat.com/* | |
// ==/UserScript== | |
/* global document, fetch */ | |
/* eslint-disable no-await-in-loop, no-underscore-dangle */ | |
const FAVICON_URL = | |
'https://cdn.bearychat.com/chat/static/media/favicon.6ae2a8c8.ico' | |
function findInjectPosition() { | |
return document.querySelector('.sidePanel > :first-child') | |
} | |
function getProp(obj, keyPath) { | |
let value = obj | |
for (const key of keyPath) { | |
value = value[key] | |
if (!(value && typeof value === 'object')) { | |
break | |
} | |
} | |
return value | |
} | |
function getReactInternalProp(selector, keyPath) { | |
const elem = document.querySelector(selector) | |
const key = Object.keys(elem).find(item => | |
item.startsWith('__reactInternalInstance$') | |
) | |
if (key === undefined) { | |
console.error('cannot find React instance') | |
return null | |
} | |
const fullKeyPath = [key, '_currentElement', '_owner'].concat(keyPath) | |
const value = getProp(elem, fullKeyPath) | |
if (!value) { | |
console.warn(`no valid value found at ${fullKeyPath}`) | |
} | |
return value | |
} | |
const getReduxStore = () => | |
getReactInternalProp('[data-reactroot]', ['_context', 'store']) | |
const sleep = ms => | |
new Promise(resolve => { | |
setTimeout(resolve, ms) | |
}) | |
function* getUnreadChannels() { | |
const store = getReduxStore() | |
if (!store) { | |
console.warn('cannot find Redux store') | |
return | |
} | |
const channels = store | |
.getState() | |
.vchannels.get('vchannels') | |
.values() | |
for (const channel of channels) { | |
if (channel.get('unread_count') > 0) { | |
yield channel.get('vchannel_id') | |
} | |
} | |
} | |
const markAsRead = chanId => | |
// eslint-disable-next-line compat/compat | |
fetch(`https://ein.bearychat.com/api/vchannels/${chanId}/mark_read`, { | |
method: 'POST', | |
credentials: 'include', | |
}).then(resp => resp.json()) | |
const markAllAsRead = async () => { | |
for (const chanId of getUnreadChannels()) { | |
await markAsRead(chanId) | |
await sleep(100) | |
} | |
} | |
const makeLink = (label, onClick) => { | |
const link = document.createElement('a') | |
link.href = '#' | |
link.className = 'barely-tweaks-link' | |
link.style.marginRight = '10px' | |
link.innerText = label | |
link.addEventListener('click', evt => { | |
evt.preventDefault() | |
onClick(evt) | |
}) | |
return link | |
} | |
const makeToggleLink = (label, altLabel, onClick) => { | |
let toggle = false | |
const link = makeLink(label, ({ target }) => { | |
if (toggle) { | |
target.innerText = target.dataset.origLabel | |
} else { | |
target.innerText = target.dataset.altLabel | |
} | |
toggle = !toggle | |
onClick(target, toggle) | |
}) | |
link.dataset.origLabel = label | |
link.dataset.altLabel = altLabel | |
return link | |
} | |
const installLinks = (...links) => { | |
const injectPosition = findInjectPosition() | |
if (injectPosition.querySelector('.barely-tweaks-container')) { | |
return | |
} | |
const container = document.createElement('div') | |
for (const link of links) { | |
container.appendChild(link) | |
} | |
container.classList.add('barely-tweaks-container') | |
container.style.fontSize = '12px' | |
container.style.marginTop = '20px' | |
injectPosition.appendChild(container) | |
injectPosition.style.flexWrap = 'wrap' | |
injectPosition.style.height = 'initial' | |
injectPosition.style.paddingTop = '20px' | |
injectPosition.style.paddingBottom = '20px' | |
} | |
const main = () => { | |
let timerId | |
const linkFixFavicon = makeToggleLink( | |
'禁止变换 Favicon', | |
'恢复 Favicon 变换', | |
(target, enable) => { | |
if (!enable) { | |
clearInterval(timerId) | |
return | |
} | |
timerId = setInterval(() => { | |
const favicon = document.querySelector('link[rel="shortcut icon"]') | |
if (!favicon) { | |
return | |
} | |
favicon.href = FAVICON_URL | |
}, 1000) | |
} | |
) | |
const linkMarkAllAsRead = makeLink('全部标为已读', markAllAsRead) | |
installLinks(linkFixFavicon, linkMarkAllAsRead) | |
setInterval(() => { | |
installLinks(linkFixFavicon, linkMarkAllAsRead) | |
}, 30 * 1000) | |
} | |
const bootstrap = (delay = 300) => { | |
if (!findInjectPosition()) { | |
setTimeout(bootstrap, delay, delay * 2) | |
return | |
} | |
main() | |
} | |
if (/complete|interactive|loaded/.test(document.readyState)) { | |
bootstrap() | |
} else { | |
document.addEventListener('DOMContentLoaded', bootstrap) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment