Browse Source

Modularise code and add build system

new-api
Luke Childs 6 years ago
parent
commit
b51bf49687
  1. 4
      .gitignore
  2. 13
      package.json
  3. 45
      src/calculate-bias.js
  4. 156
      src/content.js
  5. 23
      src/get-data.js
  6. 75
      src/inject-chart.js
  7. 2
      src/manifest.json
  8. 16
      src/preload-tweet-data.js
  9. 20
      webpack.config.js
  10. 2939
      yarn.lock

4
.gitignore

@ -0,0 +1,4 @@
dist
node_modules
.DS_Store

13
package.json

@ -0,0 +1,13 @@
{
"scripts": {
"build": "webpack src/content.js --mode production"
},
"devDependencies": {
"copy-webpack-plugin": "^5.0.1",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0"
},
"dependencies": {
"escape-html": "^1.0.3"
}
}

45
src/calculate-bias.js

@ -0,0 +1,45 @@
const calculateBias = data => {
const SUPPORTED_CURRENCIES = ['BTC', 'ETH', 'XRP', 'BCH'];
const BIAS_REDUCER = 0.5;
const BIAS_MIN_THRESHOLD = 10;
let currencies = data.clusters
.map(currency => ({
symbol: currency.abbr,
name: currency.display,
influence: currency.score
}))
.filter(currency => SUPPORTED_CURRENCIES.includes(currency.symbol))
.sort((a, b) => {
if (a.influence > b.influence) {
return -1;
}
if (a.influence < b.influence) {
return 1;
}
return 0;
})
.map((currency, index) => {
const multiplier = Math.pow(BIAS_REDUCER, index);
const bias = currency.influence * multiplier;
return {...currency, bias};
});
const totalBiasSum = currencies
.map(currency => currency.bias)
.reduce((a, b) => a + b);
currencies = currencies
.map(currency => {
let bias = (currency.bias / totalBiasSum) * 100;
bias = (bias < BIAS_MIN_THRESHOLD) ? 0 : bias;
return {...currency, bias};
});
return SUPPORTED_CURRENCIES
.map(symbol => currencies.find(currency => currency.symbol === symbol));
};
export default calculateBias;

156
src/content.js

@ -1,157 +1,7 @@
const dataCache = new Map();
import preloadTweetData from './preload-tweet-data';
import injectChart from './inject-chart';
const getData = async username => {
const url = `https://hive.one/api/top-people/${username}`;
const cachedData = dataCache.get(url);
if (typeof cachedData !== 'undefined') {
return cachedData;
}
const response = await fetch(url);
if (response.status === 404) {
dataCache.set(url, false);
return false;
}
const data = await response.json();
dataCache.set(url, data);
return data;
};
const preloadTweetData = () => {
Array.from(document.querySelectorAll('.tweet')).forEach(tweet => {
if (tweet.dataset.coinflictPreloaded) {
return false;
}
const username = tweet.dataset.screenName;
getData(username);
tweet.dataset.coinflictPreloaded = true;
});
};
const escapeHtml = string => string.replace(/["'&<>]/g, '');
const calculateBias = data => {
const SUPPORTED_CURRENCIES = ['BTC', 'ETH', 'XRP', 'BCH'];
const BIAS_REDUCER = 0.5;
const BIAS_MIN_THRESHOLD = 10;
let currencies = data.clusters
.map(currency => ({
symbol: currency.abbr,
name: currency.display,
influence: currency.score
}))
.filter(currency => SUPPORTED_CURRENCIES.includes(currency.symbol))
.sort((a, b) => {
if (a.influence > b.influence) {
return -1;
}
if (a.influence < b.influence) {
return 1;
}
return 0;
})
.map((currency, index) => {
const multiplier = Math.pow(BIAS_REDUCER, index);
const bias = currency.influence * multiplier;
return {...currency, bias};
});
const totalBiasSum = currencies
.map(currency => currency.bias)
.reduce((a, b) => a + b);
currencies = currencies
.map(currency => {
let bias = (currency.bias / totalBiasSum) * 100;
bias = (bias < BIAS_MIN_THRESHOLD) ? 0 : bias;
return {...currency, bias};
});
return SUPPORTED_CURRENCIES
.map(symbol => currencies.find(currency => currency.symbol === symbol));
}
const injectChart = async () => {
const profileHoverContainer = document.querySelector('#profile-hover-container');
const profileCard = profileHoverContainer.querySelector('.profile-card');
if (!(profileCard && !profileCard.dataset.coinflict)) {
return;
}
profileCard.dataset.coinflict = true;
const username = profileCard.querySelector('[data-screen-name]').dataset.screenName;
const data = await getData(username);
const negativeMargin = 12;
const container = document.createElement('div');
container.innerHTML = `
<div class="ProfileCardBias ProfileCardStats">
<style>
.ProfileCardBias {
margin-top: -${negativeMargin}px;
padding-bottom: 8px;
}
.ProfileCardBias .bias:not(:last-of-type) {
margin-bottom: 4px;
}
.ProfileCardBias .bias-amount-container {
position: relative;
width: 100%;
height: 8px;
background: #ccc;
border-radius: 4px;
}
.ProfileCardBias .bias-amount {
position: absolute;
top: 0;
left: 0;
height: 100%;
border-radius: 4px;
}
</style>
<div><strong>Bias</strong></div>
</div>`;
const biases = container.children[0];
if (data) {
const currencies = calculateBias(data);
currencies.forEach(currency => {
const container = document.createElement('div');
container.innerHTML = `
<div class="bias">
<span class="ProfileCardStats-statLabel u-block">${escapeHtml(currency.name)}</span>
<div class="bias-amount-container">
<div class="bias-amount u-bgUserColor" style="width: ${Number(currency.bias)}%;"></div>
</div>
</div>`;
biases.appendChild(container.children[0]);
});
} else {
biases.appendChild(document.createTextNode('No bias data available for this user.'));
}
const profileCardStats = profileCard.querySelector('.ProfileCardStats');
profileCardStats.parentNode.insertBefore(biases, profileCardStats);
const gravitySouth = profileCard.classList.contains('gravity-south');
const offset = gravitySouth ? (biases.offsetHeight - negativeMargin) : 0;
profileHoverContainer.style.transform = `translateY(-${offset}px)`;
};
observer = new MutationObserver(() => {
const observer = new MutationObserver(() => {
preloadTweetData();
injectChart();
});

23
src/get-data.js

@ -0,0 +1,23 @@
const dataCache = new Map();
const getData = async username => {
const url = `https://hive.one/api/top-people/${username}`;
const cachedData = dataCache.get(url);
if (typeof cachedData !== 'undefined') {
return cachedData;
}
const response = await fetch(url);
if (response.status === 404) {
dataCache.set(url, false);
return false;
}
const data = await response.json();
dataCache.set(url, data);
return data;
};
export default getData;

75
src/inject-chart.js

@ -0,0 +1,75 @@
import escapeHtml from 'escape-html';
import getData from './get-data';
import calculateBias from './calculate-bias';
const injectChart = async () => {
const profileHoverContainer = document.querySelector('#profile-hover-container');
const profileCard = profileHoverContainer.querySelector('.profile-card');
if (!(profileCard && !profileCard.dataset.coinflict)) {
return;
}
profileCard.dataset.coinflict = true;
const username = profileCard.querySelector('[data-screen-name]').dataset.screenName;
const data = await getData(username);
const negativeMargin = 12;
const container = document.createElement('div');
container.innerHTML = `
<div class="ProfileCardBias ProfileCardStats">
<style>
.ProfileCardBias {
margin-top: -${negativeMargin}px;
padding-bottom: 8px;
}
.ProfileCardBias .bias:not(:last-of-type) {
margin-bottom: 4px;
}
.ProfileCardBias .bias-amount-container {
position: relative;
width: 100%;
height: 8px;
background: #ccc;
border-radius: 4px;
}
.ProfileCardBias .bias-amount {
position: absolute;
top: 0;
left: 0;
height: 100%;
border-radius: 4px;
}
</style>
<div><strong>Bias</strong></div>
</div>`;
const biases = container.children[0];
if (data) {
const currencies = calculateBias(data);
currencies.forEach(currency => {
const container = document.createElement('div');
container.innerHTML = `
<div class="bias">
<span class="ProfileCardStats-statLabel u-block">${escapeHtml(currency.name)}</span>
<div class="bias-amount-container">
<div class="bias-amount u-bgUserColor" style="width: ${Number(currency.bias)}%;"></div>
</div>
</div>`;
biases.appendChild(container.children[0]);
});
} else {
biases.appendChild(document.createTextNode('No bias data available for this user.'));
}
const profileCardStats = profileCard.querySelector('.ProfileCardStats');
profileCardStats.parentNode.insertBefore(biases, profileCardStats);
const gravitySouth = profileCard.classList.contains('gravity-south');
const offset = gravitySouth ? (biases.offsetHeight - negativeMargin) : 0;
profileHoverContainer.style.transform = `translateY(-${offset}px)`;
};

2
manifest.json → src/manifest.json

@ -9,7 +9,7 @@
{
"run_at": "document_end",
"matches": ["https://twitter.com/*"],
"js": ["src/content.js"]
"js": ["content.js"]
}
]
}

16
src/preload-tweet-data.js

@ -0,0 +1,16 @@
import getData from './get-data';
const preloadTweetData = () => {
Array.from(document.querySelectorAll('.tweet')).forEach(tweet => {
if (tweet.dataset.coinflictPreloaded) {
return false;
}
const username = tweet.dataset.screenName;
getData(username);
tweet.dataset.coinflictPreloaded = true;
});
};
export default preloadTweetData;

20
webpack.config.js

@ -0,0 +1,20 @@
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = (env, argv) => ({
devtool: 'sourcemap',
stats: 'errors-only',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'content.js',
},
plugins: [
new CopyWebpackPlugin([
{
from: '*',
context: 'src',
ignore: '*.js'
},
])
],
});

2939
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save