Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch sponsors at build time, show ALL non-skeevy sponsors; closes #4271 #4272

Merged
merged 8 commits into from May 22, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .nvmrc
@@ -0,0 +1 @@
12
21 changes: 21 additions & 0 deletions docs/_data/blacklist.json
@@ -0,0 +1,21 @@
[
"cheap-writing-service",
"emailmarketingservices-io",
"device-tricks1",
"my-true-media",
"yiannakis-ttafounas-ttafounas",
"writerseperhour",
"casinotop-com",
"casino-topp",
"casinoutanreg",
"supercazino-ro",
"igor-noskov",
"blue-link-seo",
"casino-online",
"domywriting",
"writemypaper4me",
"trust-my-paper",
"seowebsitetraffic-net",
"pfannen-test",
"mochajs"
]
133 changes: 133 additions & 0 deletions docs/_data/supporters.js
@@ -0,0 +1,133 @@
#!/usr/bin/env node
'use strict';

const debug = require('debug')('mocha:docs:data:supporters');
const needle = require('needle');
const imageSize = require('image-size');
const blacklist = new Set(require('./blacklist.json'));

const API_ENDPOINT = 'https://api.opencollective.com/graphql/v2';

const query = `query account($limit: Int, $offset: Int, $slug: String) {
account(slug: $slug) {
orders(limit: $limit, offset: $offset) {
limit
offset
totalCount
nodes {
fromAccount {
name
slug
website
imgUrlMed: imageUrl(height:64)
imgUrlSmall: imageUrl(height:32)
type
}
totalDonations {
value
}
createdAt
}
}
}
}`;

const graphqlPageSize = 1000;

const nodeToSupporter = node => ({
name: node.fromAccount.name,
slug: node.fromAccount.slug,
website: node.fromAccount.website,
imgUrlMed: node.fromAccount.imgUrlMed,
imgUrlSmall: node.fromAccount.imgUrlSmall,
firstDonation: node.createdAt,
totalDonations: node.totalDonations.value * 100,
type: node.fromAccount.type
});

/**
* Retrieves donation data from OC
*
* Handles pagination
* @param {string} slug - Collective slug to get donation data from
* @returns {Promise<Object[]>} Array of raw donation data
*/
const getAllOrders = async (slug = 'mochajs') => {
let allOrders = [];
const variables = {limit: graphqlPageSize, offset: 0, slug};

// Handling pagination if necessary (2 pages for ~1400 results in May 2019)
while (true) {
const result = await needle(
'post',
API_ENDPOINT,
{query, variables},
{json: true}
);
const orders = result.body.data.account.orders.nodes;
allOrders = [...allOrders, ...orders];
variables.offset += graphqlPageSize;
if (orders.length < graphqlPageSize) {
debug('retrieved %d orders', allOrders.length);
return allOrders;
} else {
debug(
'loading page %d of orders...',
Math.floor(variables.offset / graphqlPageSize)
);
}
}
};

module.exports = async () => {
const orders = await getAllOrders();
// Deduplicating supporters with multiple orders
const uniqueSupporters = new Map();

const supporters = orders
.map(nodeToSupporter)
.filter(supporter => !blacklist.has(supporter.slug))
.reduce((supporters, supporter) => {
if (uniqueSupporters.has(supporter.slug)) {
// aggregate donation totals
uniqueSupporters.get(supporter.slug).totalDonations +=
supporter.totalDonations;
return supporters;
}
uniqueSupporters.set(supporter.slug, supporter);
return [...supporters, supporter];
}, [])
.sort((a, b) => b.totalDonations - a.totalDonations)
.reduce(
(supporters, supporter) => {
if (supporter.type === 'INDIVIDUAL') {
supporters.backers.push({
...supporter,
avatar: supporter.imgUrlSmall
});
} else {
supporters.sponsors.push({...supporter, avatar: supporter.imgUrlMed});
}
return supporters;
},
{sponsors: [], backers: []}
);

// Fetch images for sponsors and save their image dimensions
await Promise.all(
supporters.sponsors.map(async sponsor => {
for await (const chunk of needle.get(sponsor.avatar)) {
sponsor.dimensions = imageSize(chunk);
break;
}
})
);

debug(
'found %d valid backers and %d valid sponsors (%d total)',
supporters.backers.length,
supporters.sponsors.length,
supporters.backers.length + supporters.sponsors.length
);
return supporters;
};
7 changes: 0 additions & 7 deletions docs/_includes/backers.md

This file was deleted.

15 changes: 5 additions & 10 deletions docs/_includes/default.html → docs/_includes/default.liquid
Expand Up @@ -4,12 +4,6 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>{{ title }}</title>
<link
rel="preload"
href="https://opencollective.com/static/images/user.svg"
as="image"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/normalize.css" />
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/prism.css" />
Expand All @@ -22,7 +16,7 @@
<header id="_header">
<h1>
<a href="/">
<img id="mocha-logo" src="/images/mocha-logo.svg" alt="Mocha logo" />
<img id="mocha-logo" loading="eager" src="/images/mocha-logo.svg" alt="Mocha logo" />
</a>
</h1>
<p id="tag"><em>simple</em>, <em>flexible</em>, <em>fun</em></p>
Expand All @@ -36,7 +30,7 @@ <h1>
rel="external noopener"
title="Mocha is sponsored by Matomo"
>
<img src="images/matomo-logo.png?trim" alt="Matomo logo" />
<img src="images/matomo-logo.png?trim" loading="lazy" alt="Matomo logo" />
</a>
<a
title="Mocha is an OpenJS Foundation Project"
Expand All @@ -45,6 +39,7 @@ <h1>
>
<img
src="/images/openjsf-logo.svg"
loading="lazy"
width="300"
height="94"
alt="OpenJS Foundation Logo"
Expand All @@ -56,7 +51,7 @@ <h1>
title="Mocha is sponsored by Wallaby"
>
<figure id="wallaby-logo">
<img src="images/wallaby-logo.png?trim" alt="Wallaby logo" />
<img src="images/wallaby-logo.png?trim" loading="lazy" alt="Wallaby logo" />
<figcaption>Wallaby</figcaption>
</figure>
</a>
Expand Down Expand Up @@ -153,7 +148,7 @@ <h1>
<div class="netlify-badge">
<a href="https://www.netlify.com">
<img
src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg"
src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" loading="lazy"
/>
</a>
</div>
Expand Down
10 changes: 0 additions & 10 deletions docs/_includes/sponsors.md

This file was deleted.

68 changes: 68 additions & 0 deletions docs/_includes/supporters.md
@@ -0,0 +1,68 @@
## Sponsors

Use Mocha at Work? Ask your manager or marketing team if they'd help [support](https://opencollective.com/mochajs#support) our project. Your company's logo will also be displayed on [npmjs.com](http://npmjs.com/package/mocha) and our [GitHub repository](https://github.com/mochajs/mocha#sponsors).

<ul class="image-list" id="sponsors">
{%- for supporter in supporters.sponsors -%}
<li>
{%- if supporter.website -%}
<a href="{{ supporter.website }}" target="_blank" rel="noopener" title="{{ supporter.name }}">
{%- endif -%}
<img src="{{ supporter.avatar }}" width="{{ supporter.dimensions.width }}" height="{{ supporter.dimensions.height }}" alt="{{ supporter.name }}" />
{%- if supporter.website -%}
</a>
{%- endif -%}
</li>
{%- endfor -%}
</ul>

## Backers

Find Mocha helpful? Become a [backer](https://opencollective.com/mochajs#support) and support Mocha with a monthly donation.

<ul class="image-list faded-images" id="backers">
{%- for supporter in supporters.backers -%}
<li>
{%- if supporter.website -%}
<a href="{{ supporter.website }}" target="_blank" rel="noopener" title="{{ supporter.name }}">
{%- endif -%}
<img src="{{ supporter.avatar }}" alt="{{ supporter.name }}" />
{%- if supporter.website -%}
</a>
{%- endif -%}
</li>
{%- endfor -%}
</ul>

<script>
(function() {
Munter marked this conversation as resolved.
Show resolved Hide resolved
'use strict';

var imageLists = document.querySelectorAll('.image-list');

function getListItem(img) {
var parent = img.parentNode;
while (parent && parent.nodeName !== 'LI') {
parent = parent.parentNode;
}

return parent;
}

function onloadHandler() {
getListItem(this).classList.add('is-loaded');
}

Array.prototype.forEach.call(imageLists, function(imageList) {
var images = imageList.querySelectorAll('img');

for (var i = 0; i < images.length; i += 1) {
if (!images[i].complete) {
getListItem(images[i]).classList.add('faded-image');
images[i].onload = onloadHandler;
images[i].onerror = onloadHandler;
}
}
});
})();
</script>
55 changes: 35 additions & 20 deletions docs/css/style.css
Expand Up @@ -74,42 +74,57 @@ nav.badges a + a {
margin-left: 3px;
}

.image-list {
ul.image-list {
overflow: hidden;
text-align: center;
list-style: none;
column-count: 1;
padding: 0;
margin: 0;
}

.image-list a {
ul.image-list li {
border-bottom: none;
display: inline-block;
margin: 6px;
margin: 0 4px 0 4px;
max-height: 64px;
padding: 0;
line-height: 0;
}

.image-list a img {
ul.image-list li a {
display: inline-block;
}

ul.image-list li img {
margin: 0;
padding: 0;
display: block;
height: 64px;
max-height: 64px;
}

.faded-images {
background-color: #ddd;
border: 1px solid;
border-color: #ddd #ddd #ccc;
border-radius: 3px;
padding: 1em;
box-shadow: inset 0 0 10px #ccc;
ul#backers.image-list li img {
width: 32px;
height: 32px;
overflow: hidden;
}

.faded-images img {
opacity: 0;
transition: opacity 0.3s;
.faded-image {
background-color: rgba(0, 0, 0, 0.05);
}

.faded-images.is-loaded img {
opacity: 1;
.faded-image.is-loaded {
background-color: rgba(0, 0, 0, 0);
transition: background-color 0.3s;
}

#_backers a img {
background: url(/images/backer-background.svg?inline) center center no-repeat;
width: 64px;
.faded-image img {
opacity: 0;
transition: opacity 0.1s;
}

.faded-image.is-loaded img {
opacity: 1;
}

h2 {
Expand Down
12 changes: 0 additions & 12 deletions docs/images/backer-background.svg

This file was deleted.