golang.org/x/build@v0.0.0-20240506185731-218518f32b70/devapp/static/gophercon/index.html (about) 1 <!DOCTYPE html> 2 <!-- 3 Copyright 2022 The Go Authors. All rights reserved. 4 Use of this source code is governed by a BSD-style 5 license that can be found in the LICENSE file. 6 --> 7 8 <meta charset="utf-8"> 9 <title>GopherCon 2017 Contribution Dashboard</title> 10 <meta name=viewport content="width=device-width,minimum-scale=1,maximum-scale=1"> 11 <style> 12 * { 13 box-sizing: border-box; 14 margin: 0; 15 padding: 0; 16 } 17 a:link, 18 a:visited { 19 color: #fff; 20 } 21 .Site { 22 background-color: #2a2a2a; 23 color: #fff; 24 display: flex; 25 flex-direction: column; 26 font: 14px 'Helvetica-Neue', Helvetica, sans-serif; 27 height: 100vh; 28 text-rendering: optimizeLegibility; 29 } 30 .Site-body { 31 display: flex; 32 flex: 1; 33 } 34 .Site-content { 35 border-right: 1px solid #3a3939; 36 overflow: hidden; 37 padding: 1.5em; 38 width: 60%; 39 } 40 .Site-activity { 41 background-color: #323232; 42 flex: 1; 43 overflow: scroll; 44 } 45 .Site-logo { 46 height: 60px; 47 margin-right: 2em; 48 } 49 .Site-pointsTitle, 50 .Site-points { 51 text-align: center; 52 } 53 .Site-pointsTitle { 54 font-size: 4vh; 55 letter-spacing: 1px; 56 text-transform: uppercase; 57 } 58 .Site-points { 59 font-size: 30vh; 60 } 61 .u-avatar { 62 border-radius: 50%; 63 } 64 .AvatarGroup { 65 display: flex; 66 flex-wrap: wrap; 67 justify-content: center; 68 } 69 .AvatarGroup-avatar { 70 height: 6vh; 71 margin: 0 .5em .5em 0; 72 width: 6vh; 73 } 74 .Activity { 75 align-items: flex-start; 76 background-color: #323232; 77 border-bottom: 1px solid #2d2d2d; 78 border-top: 1px solid #404040; 79 display: flex; 80 font-size: 2vw; 81 padding: 1em; 82 } 83 .Activity-avatar { 84 height: 2.5em; 85 margin-right: .75em; 86 width: 2.5em; 87 } 88 .Activity-main { 89 flex: 1; 90 margin-right: .15em; 91 } 92 .Activity-points { 93 color: #85ebf9; 94 font-size: .85em; 95 margin-top: .25em; 96 } 97 .Activity-icon { 98 font-size: 2em; 99 } 100 .Site-footer { 101 align-items: center; 102 background: #3a3737; 103 display: flex; 104 justify-content: space-between; 105 padding: 1em 1.5em; 106 } 107 </style> 108 <body class="Site"> 109 <div class="Site-body"> 110 <main class="Site-content"> 111 <div class="Site-pointsTitle">Total points</div> 112 <div class="Site-points js-totalPoints">0</div> 113 <div class="AvatarGroup js-avatarGroup"></div> 114 </main> 115 <aside class="Site-activity js-activityList"></aside> 116 </div> 117 <footer class="Site-footer"> 118 <img class="Site-logo" src="https://github.com/ashleymcnamara/gophers/raw/master/GoCommunity.png" alt="Go Community"> 119 <div> 120 Gopher artwork by <a href="https://github.com/ashleymcnamara/gophers" target="_blank">Ashley McNamara</a> 121 based on the original artwork by the amazing 122 <a href="https://reneefrench.blogspot.com/" target="_blank">Renee French</a>. 123 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> 124 </div> 125 </footer> 126 </body> 127 128 <template class="activityTemplate"> 129 <div class="Activity"> 130 <img class="u-avatar Activity-avatar js-avatar"> 131 <div class="Activity-main"> 132 <div class="Activity-body js-body"></div> 133 <div class="Activity-points js-points"></div> 134 </div> 135 <div class="Activity-icon js-icon"></div> 136 </div> 137 </template> 138 139 <script> 140 (function() { 141 'use strict'; 142 143 const ActivityType = { 144 AMEND_CHANGE: 'AMEND_CHANGE', 145 CREATE_CHANGE: 'CREATE_CHANGE', 146 MERGE_CHANGE: 'MERGE_CHANGE', 147 REGISTER: 'REGISTER', 148 }; 149 150 const iconMap = {}; 151 iconMap.AMEND_CHANGE = '👍'; 152 iconMap.CREATE_CHANGE = '👏'; 153 iconMap.MERGE_CHANGE = '🤘'; 154 iconMap.REGISTER = '🎉'; 155 156 let lastUpdateMs = 0; 157 getActivities(lastUpdateMs); 158 startUpdateLoop(); 159 160 function getActivities(since) { 161 const url = `/_/activities?since=${since}`; 162 fetch(url).then(resp => { 163 resp.json().then(obj => { 164 processAll(obj.activities); 165 updatePoints(obj.totalPoints); 166 }); 167 }).catch(err => { 168 console.error(err); 169 }); 170 } 171 172 function startUpdateLoop() { 173 window.setInterval(() => { 174 getActivities(lastUpdateMs); 175 }, 5000); 176 } 177 178 // Username => img element. 179 let avatarMap = {}; 180 181 function processAll(activities) { 182 const activityListEl = document.querySelector('.js-activityList'); 183 const avatarGroupEl = document.querySelector('.js-avatarGroup'); 184 activities.forEach(activity => { 185 if (!avatarMap[activity.gitHubUser]) { 186 const el = createAvatarEl(activity.gitHubUser); 187 el.classList.add('AvatarGroup-avatar'); 188 avatarMap[activity.gitHubUser] = el; 189 } 190 avatarGroupEl.insertBefore(avatarMap[activity.gitHubUser], 191 avatarGroupEl.firstChild); 192 const activityEl = createActivityEl(activity); 193 activityListEl.insertBefore(activityEl, activityListEl.firstChild); 194 }); 195 if (activities.length > 0) { 196 lastUpdateMs = new Date(activities[activities.length - 1].created).getTime(); 197 } 198 } 199 200 function updatePoints(points) { 201 document.querySelector('.js-totalPoints').textContent = points; 202 } 203 204 function createAvatarEl(username) { 205 let img = document.createElement('IMG'); 206 img.classList.add('u-avatar'); 207 img.src = avatarSrc(username); 208 img.alt = avatarAlt(username); 209 return img; 210 } 211 212 function avatarSrc(username) { 213 return `https://github.com/${encodeURIComponent(username)}.png?size=100`; 214 } 215 216 function avatarAlt(username) { 217 return `Avatar for ${username}`; 218 } 219 220 function activityBody(type, username) { 221 switch (type) { 222 case ActivityType.AMEND_CHANGE: 223 return `${username} just amended a change!`; 224 225 case ActivityType.CREATE_CHANGE: 226 return `${username} just created a change!`; 227 228 case ActivityType.MERGE_CHANGE: 229 return `${username} just merged a change!`; 230 231 case ActivityType.REGISTER: 232 return `${username} just registered!`; 233 } 234 } 235 236 function pointsStr(points) { 237 return `+${points} Point` + (points > 1 ? 's' : ''); 238 } 239 240 function createActivityEl(activity) { 241 const tmpl = document.querySelector('.activityTemplate'); 242 const imgEl = tmpl.content.querySelector('.js-avatar'); 243 imgEl.src = avatarSrc(activity.gitHubUser); 244 imgEl.alt = avatarAlt(activity.gitHubUser); 245 tmpl.content.querySelector('.js-icon').textContent = iconMap[activity.type]; 246 tmpl.content.querySelector('.js-body').textContent = 247 activityBody(activity.type, activity.gitHubUser); 248 tmpl.content.querySelector('.js-points').textContent = 249 pointsStr(activity.points); 250 return document.importNode(tmpl.content, true); 251 } 252 })(); 253 </script>