Skip to content
Snippets Groups Projects
Commit b55c9d5f authored by Olivier Goulet's avatar Olivier Goulet
Browse files

refactor: use weighted collaborations widget

parent 4f0d8838
No related branches found
No related tags found
No related merge requests found
......@@ -16,7 +16,6 @@ import 'eclipsefdn-solstice-assets/js/astro';
// Widgets
import './src/collaborations/eclipsefdn.rollover-link';
import './src/collaborations/eclipsefdn.weighted-working-groups';
import './src/eclipsefdn-promo-content'
// Other Scripts
......
/*!
* Copyright (c) 2022 Eclipse Foundation, Inc.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* Contributors:
* Olivier Goulet <olivier.goulet@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
import template from '../templates/weighted-working-groups/working-group-block.mustache'
const defaultOptions = {
count: 1,
}
// Working groups with a weight of -1 will not be displayed.
const weights = {
default: -1,
'jakarta-ee': 1,
sdv: 20,
'cloud-development-tools': 30,
adoptium: 38,
oniro: 38,
'eclipse-ide': 44,
'internet-things-iot': 44,
aice: 50,
asciidoc: 50,
'edge-native': 50,
microprofile: 50,
openmobility: 50,
osgi: 50,
sparkplug: 50,
};
// An object which uses WG aliases as keys and logo urls as value. Use this only for WGs which do not yet have a logo.
const fallbackLogos = {};
const removeDuplicates = (value, index, self) => self.indexOf(value) === index;
const getWeightedRandomIndex = (weightBuckets, categorizedArrayByWeight) => {
const largestWeight = Object.values(weights).sort().at(-1);
const randomNumber = Math.floor(Math.random() * largestWeight);
// Returns whatever number is the closest to a weight bucket
const closest = Object.values(weights).reduce((prev, curr) => {
return (Math.abs(curr - randomNumber) < Math.abs(prev - randomNumber) ? curr : prev);
})
const weightBucketSelectionIndex = weightBuckets.indexOf(closest);
const weightBucketSelection = weightBuckets[weightBucketSelectionIndex];
const selectionIndex = Math.floor(Math.random() * categorizedArrayByWeight[weightBucketSelection].length);
return categorizedArrayByWeight[weightBucketSelection][selectionIndex];
}
function getUniqueRandomWorkingGroups(workingGroupsCategorizedByWeight, weightBuckets, count) {
// Creates a set of selected working groups. This will omit duplicates.
let selectionSet = new Set();
while (selectionSet.size < count) {
const randomWorkingGroup = getWeightedRandomIndex(weightBuckets, workingGroupsCategorizedByWeight);
if (selectionSet.has(randomWorkingGroup)) continue;
selectionSet.add(randomWorkingGroup);
}
return Array.from(selectionSet);
}
async function getWeightedRandomWorkingGroups(count) {
try {
const cachedWorkingGroups = JSON.parse(sessionStorage.getItem('weighted-working-groups'));
const isCached = cachedWorkingGroups != null;
// Only return the cached working groups if the count hasn't changed since last time run
if (isCached && cachedWorkingGroups.length === count) return [cachedWorkingGroups, null];
const response = await fetch('https://api.eclipse.org/working-groups/');
if (!response.ok) throw new Error('Could not fetch from the Working Group API');
const workingGroups = await response.json();
const weightBuckets = Object
.values(weights)
.filter(removeDuplicates);
// Create an object where the key is a bucket (or weight), and the value an array of working groups tied to that bucket
const weightBucketObject = weightBuckets.reduce((acc, bucket) => ({...acc, [bucket]: [] }), {})
const workingGroupsCategorizedByWeight = workingGroups.reduce((acc, wg) => {
const weight = weights[wg.alias] || weights.default;
acc[weight].push(wg);
return acc;
}, { ...weightBucketObject });
// Retrieves weighted random working groups and stores it in session storage cache
const randomWorkingGroups = getUniqueRandomWorkingGroups(workingGroupsCategorizedByWeight, weightBuckets, count);
sessionStorage.setItem('weighted-working-groups', JSON.stringify(randomWorkingGroups));
return [randomWorkingGroups, null];
} catch (error) {
return [null, error];
}
};
const getWorkingGroupLogo = ({ alias, logo } = workingGroup) => {
const fallbackLogo = fallbackLogos[alias];
if (logo === '') return fallbackLogo || '';
return logo;
}
(async function renderWeightedWorkingGroups() {
const element = document.querySelector('.eclipsefdn-weighted-working-groups');
if (!element) return;
const options = {
...defaultOptions,
...element.dataset,
count: +element.dataset.count
};
element.innerHTML = template({ isFetching: true, items: new Array(options.count) })
const [workingGroups, error] = await getWeightedRandomWorkingGroups(options.count);
if (error) {
element.innerHTML = `<p class="alert alert-danger">Error when attempting to load working groups.</p>`;
console.error(error);
return;
}
const data = {
isFetching: false,
items: workingGroups.map(wg => ({
title: wg.title,
logo: getWorkingGroupLogo(wg),
websiteUrl: wg.resources.website,
}))
};
element.innerHTML = template(data);
})();
{{#items}}
<div class="weighted-working-group-block-wrapper">
{{#isFetching}}
<div class="weighted-working-group-block weighted-working-group-loading" aria-label="Loading working group website links">
<i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i>
<span class="sr-only">Loading...</span>
</div>
{{/isFetching}}
{{^isFetching}}
<a class="weighted-working-group-block" href="{{websiteUrl}}" aria-label="{{ title }}'s official website">
<div class="weighted-working-group-logo-wrapper">
<img class="weighted-working-group-logo" src="{{logo}}" alt="" />
</div>
</a>
{{/isFetching}}
</div>
{{/items}}
<section id="join-collaboration" class="container join-collaboration margin-bottom-60">
<h2>Join a Collaboration</h2>
<div class="row">
<div class="eclipsefdn-weighted-working-groups" data-count="3"></div>
<div class="eclipsefdn-weighted-collaborations" data-count="3"></div>
<div class="col-xs-24 join-collaboration-button-col">
<a class="btn btn-primary join-collaboration-button" href="/org/workinggroups/explore.php" tabindex="0">View All</a>
</div>
......
......@@ -209,38 +209,6 @@
}
// Join collaboration section
.eclipsefdn-weighted-working-groups {
display: flex;
gap: 3rem;
}
.weighted-working-group-block-wrapper {
width: 100%;
padding-bottom: 2em;
}
.weighted-working-group-block {
display: flex;
justify-content: center;
border-radius: 0.8em;
background-color: @collaboration-gray;
padding: 3.5rem;
height: 100%;
}
.weighted-working-group-logo-wrapper {
display: flex;
justify-content: center;
margin: auto;
max-height: 16rem;
img {
object-fit: contain;
width: 100%;
min-height: 8.5rem; // fixes svg height issues on firefox
}
}
.join-collaboration-button-col {
padding: 1em;
text-align: center;
......
......@@ -23,7 +23,7 @@
},
"dependencies": {
"datatables.net-dt": "^1.13.1",
"eclipsefdn-hugo-solstice-theme": "0.0.211",
"eclipsefdn-hugo-solstice-theme": "0.0.213",
"js-yaml": "^3.13.1",
"json-minify": "^1.0.0"
},
......
......@@ -471,6 +471,8 @@
version "7.24.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz#1c94799e20fcd5c4d4589523bbc57b7692979380"
integrity sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==
dependencies:
"@babel/helper-plugin-utils" "^7.24.0"
"@babel/plugin-transform-block-scoping@^7.24.5":
version "7.24.5"
......@@ -2672,20 +2674,20 @@ dotignore@~0.1.2:
dependencies:
minimatch "^3.0.4"
eclipsefdn-hugo-solstice-theme@0.0.211:
version "0.0.211"
resolved "https://registry.yarnpkg.com/eclipsefdn-hugo-solstice-theme/-/eclipsefdn-hugo-solstice-theme-0.0.211.tgz#af2d79a5b8e4edda9e56be56d242e403e5354816"
integrity sha512-B2boEm/LY0nj4xVlDtjOp3Q31UrS5TBwmfbdNwpcNyVg3/ozB3loFStCohop8hjTD9Kf1YT8oO+NdVtkudK5ZQ==
eclipsefdn-hugo-solstice-theme@0.0.213:
version "0.0.213"
resolved "https://registry.yarnpkg.com/eclipsefdn-hugo-solstice-theme/-/eclipsefdn-hugo-solstice-theme-0.0.213.tgz#ac98098b4666c13935f362fa869f6f88536daabf"
integrity sha512-Mz7kw2DrqPyp2EYruTQE+uO3/7KLiubwnG5DLfLDylCb/4ePPLmHym5qkL1n5t1X2fzrCAij0G0qoLpkiV7IaA==
dependencies:
axios "^1.6.0"
eclipsefdn-solstice-assets "0.0.261"
eclipsefdn-solstice-assets "0.0.264"
json2yaml "^1.1.0"
toml "^3.0.0"
eclipsefdn-solstice-assets@0.0.261:
version "0.0.261"
resolved "https://registry.yarnpkg.com/eclipsefdn-solstice-assets/-/eclipsefdn-solstice-assets-0.0.261.tgz#fcd496132bc11f086eed3856119fdb50f647d45c"
integrity sha512-exIFKTqSA6zzvVSB5VhIV9coigUhdzooypti+0qCF/9+KVCpCEUTFD08vJDEJ4l+ZHLKlXqdyMuxFS33wKU1wQ==
eclipsefdn-solstice-assets@0.0.264:
version "0.0.264"
resolved "https://registry.yarnpkg.com/eclipsefdn-solstice-assets/-/eclipsefdn-solstice-assets-0.0.264.tgz#1d37277e0427e4a44e3c6be11b381bc43dc8b290"
integrity sha512-/0smTI6b3/bzFpNzpqbbiN2fk6+bDnPKlfa/8wwxWw6dmTeI/eK68aGDRa19pnPR+P0VknycI/P4Z8k0sbXaUQ==
dependencies:
"@babel/core" "^7.23.2"
"@babel/plugin-proposal-class-properties" "^7.18.6"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment