blob: 9749aedafea78791e877ee6b6297f089d4943e19 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2022 Go Authors All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<meta charset="utf-8">
<title>GopherCon 2017 Contribution Dashboard</title>
<meta name=viewport content="width=device-width,minimum-scale=1,maximum-scale=1">
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
a:link,
a:visited {
color: #fff;
}
.Site {
background-color: #2a2a2a;
color: #fff;
display: flex;
flex-direction: column;
font: 14px 'Helvetica-Neue', Helvetica, sans-serif;
height: 100vh;
text-rendering: optimizeLegibility;
}
.Site-body {
display: flex;
flex: 1;
}
.Site-content {
border-right: 1px solid #3a3939;
overflow: hidden;
padding: 1.5em;
width: 60%;
}
.Site-activity {
background-color: #323232;
flex: 1;
overflow: scroll;
}
.Site-logo {
height: 60px;
margin-right: 2em;
}
.Site-pointsTitle,
.Site-points {
text-align: center;
}
.Site-pointsTitle {
font-size: 4vh;
letter-spacing: 1px;
text-transform: uppercase;
}
.Site-points {
font-size: 30vh;
}
.u-avatar {
border-radius: 50%;
}
.AvatarGroup {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.AvatarGroup-avatar {
height: 6vh;
margin: 0 .5em .5em 0;
width: 6vh;
}
.Activity {
align-items: flex-start;
background-color: #323232;
border-bottom: 1px solid #2d2d2d;
border-top: 1px solid #404040;
display: flex;
font-size: 2vw;
padding: 1em;
}
.Activity-avatar {
height: 2.5em;
margin-right: .75em;
width: 2.5em;
}
.Activity-main {
flex: 1;
margin-right: .15em;
}
.Activity-points {
color: #85ebf9;
font-size: .85em;
margin-top: .25em;
}
.Activity-icon {
font-size: 2em;
}
.Site-footer {
align-items: center;
background: #3a3737;
display: flex;
justify-content: space-between;
padding: 1em 1.5em;
}
</style>
<body class="Site">
<div class="Site-body">
<main class="Site-content">
<div class="Site-pointsTitle">Total points</div>
<div class="Site-points js-totalPoints">0</div>
<div class="AvatarGroup js-avatarGroup"></div>
</main>
<aside class="Site-activity js-activityList"></aside>
</div>
<footer class="Site-footer">
<img class="Site-logo" src="https://github.com/ashleymcnamara/gophers/raw/master/GoCommunity.png" alt="Go Community">
<div>
Gopher artwork by <a href="https://github.com/ashleymcnamara/gophers" target="_blank">Ashley McNamara</a>
based on the original artwork by the amazing
<a href="https://reneefrench.blogspot.com/" target="_blank">Renee French</a>.
Licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.</a>
</div>
</footer>
</body>
<template class="activityTemplate">
<div class="Activity">
<img class="u-avatar Activity-avatar js-avatar">
<div class="Activity-main">
<div class="Activity-body js-body"></div>
<div class="Activity-points js-points"></div>
</div>
<div class="Activity-icon js-icon"></div>
</div>
</template>
<script>
(function() {
'use strict';
const ActivityType = {
AMEND_CHANGE: 'AMEND_CHANGE',
CREATE_CHANGE: 'CREATE_CHANGE',
MERGE_CHANGE: 'MERGE_CHANGE',
REGISTER: 'REGISTER',
};
const iconMap = {};
iconMap.AMEND_CHANGE = '👍';
iconMap.CREATE_CHANGE = '👏';
iconMap.MERGE_CHANGE = '🤘';
iconMap.REGISTER = '🎉';
let lastUpdateMs = 0;
getActivities(lastUpdateMs);
startUpdateLoop();
function getActivities(since) {
const url = `/_/activities?since=${since}`;
fetch(url).then(resp => {
resp.json().then(obj => {
processAll(obj.activities);
updatePoints(obj.totalPoints);
});
}).catch(err => {
console.error(err);
});
}
function startUpdateLoop() {
window.setInterval(() => {
getActivities(lastUpdateMs);
}, 5000);
}
// Username => img element.
let avatarMap = {};
function processAll(activities) {
const activityListEl = document.querySelector('.js-activityList');
const avatarGroupEl = document.querySelector('.js-avatarGroup');
activities.forEach(activity => {
if (!avatarMap[activity.gitHubUser]) {
const el = createAvatarEl(activity.gitHubUser);
el.classList.add('AvatarGroup-avatar');
avatarMap[activity.gitHubUser] = el;
}
avatarGroupEl.insertBefore(avatarMap[activity.gitHubUser],
avatarGroupEl.firstChild);
const activityEl = createActivityEl(activity);
activityListEl.insertBefore(activityEl, activityListEl.firstChild);
});
if (activities.length > 0) {
lastUpdateMs = new Date(activities[activities.length - 1].created).getTime();
}
}
function updatePoints(points) {
document.querySelector('.js-totalPoints').textContent = points;
}
function createAvatarEl(username) {
let img = document.createElement('IMG');
img.classList.add('u-avatar');
img.src = avatarSrc(username);
img.alt = avatarAlt(username);
return img;
}
function avatarSrc(username) {
return `https://github.com/${encodeURIComponent(username)}.png?size=100`;
}
function avatarAlt(username) {
return `Avatar for ${username}`;
}
function activityBody(type, username) {
switch (type) {
case ActivityType.AMEND_CHANGE:
return `${username} just amended a change!`;
case ActivityType.CREATE_CHANGE:
return `${username} just created a change!`;
case ActivityType.MERGE_CHANGE:
return `${username} just merged a change!`;
case ActivityType.REGISTER:
return `${username} just registered!`;
}
}
function pointsStr(points) {
return `+${points} Point` + (points > 1 ? 's' : '');
}
function createActivityEl(activity) {
const tmpl = document.querySelector('.activityTemplate');
const imgEl = tmpl.content.querySelector('.js-avatar');
imgEl.src = avatarSrc(activity.gitHubUser);
imgEl.alt = avatarAlt(activity.gitHubUser);
tmpl.content.querySelector('.js-icon').textContent = iconMap[activity.type];
tmpl.content.querySelector('.js-body').textContent =
activityBody(activity.type, activity.gitHubUser);
tmpl.content.querySelector('.js-points').textContent =
pointsStr(activity.points);
return document.importNode(tmpl.content, true);
}
})();
</script>