Skip to content

Commit 83d86c7

Browse files
committed
Enhance stats display with dynamic linking and cache management
- Added dynamic linking to PR and issue counts in hover panel - Introduced CONFIG constants for cache expiration, hover delay, and stat padding - Created modular createStatRow function for generating stat rows - Implemented isCacheExpired function to manage data freshness - Improved stat number styling with hover effects and consistent padding
1 parent e79fef2 commit 83d86c7

1 file changed

Lines changed: 50 additions & 49 deletions

File tree

src/content.js

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ const ELEMENT_IDS = {
2424
UPDATE_TIME: "gce-update-time",
2525
};
2626

27+
// Configuration constants
28+
const CONFIG = {
29+
CACHE_EXPIRATION: 7 * 24 * 60 * 60 * 1000, // 7 days in milliseconds (was 24 hours)
30+
HOVER_DELAY: 100, // milliseconds to wait before hiding panel
31+
STAT_PADDING: 3, // number of digits to pad stats to
32+
};
33+
2734
// Path and user detection helpers
2835
const isPR = (path) => /^\/[^/]+\/[^/]+\/pull\/\d+/.test(path);
2936
const isIssue = (path) => /^\/[^/]+\/[^/]+\/issues\/\d+/.test(path);
@@ -170,53 +177,31 @@ function issueOrPrLink(type, repoPath, contributor) {
170177
return `https://github.com/${end}+user:${repoPath}`;
171178
}
172179

173-
function createHoverPanelHTML({ contributor, repoPath }) {
180+
function createStatRow(scope, label, contributor, repoPath) {
174181
return `
175-
<div id="${ELEMENT_IDS.HOVER_PANEL}" class="position-absolute Box color-shadow-medium rounded-2 p-2" style="display: none; z-index: 100; top: 100%; left: -146px; min-width: 250px; margin-top: 4px;">
176-
<!-- Repo stats -->
177-
<div class="d-flex flex-items-center py-1">
178-
<div class="gce-scope-label">
179-
<span class="f6 color-fg-muted">In this repo:</span>
180-
</div>
181-
<div class="d-flex flex-items-center ml-auto">
182-
<div class="d-inline-flex flex-items-center mr-2">
183-
${ICONS.PR}<span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-repo-pr-count">...</span>
184-
</div>
185-
<div class="d-inline-flex flex-items-center">
186-
${ICONS.ISSUE}<span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-repo-issue-count">...</span>
187-
</div>
188-
</div>
182+
<div class="d-flex flex-items-center py-1">
183+
<div class="gce-scope-label">
184+
<span class="f6 color-fg-muted">${label}</span>
189185
</div>
190-
191-
<!-- Org stats -->
192-
<div class="d-flex flex-items-center py-1">
193-
<div class="gce-scope-label">
194-
<span class="f6 color-fg-muted">In this org:</span>
186+
<div class="d-flex flex-items-center ml-auto">
187+
<div class="d-inline-flex flex-items-center mr-2">
188+
${ICONS.PR}<a href="${issueOrPrLink("pr", scope === "repo" ? repoPath : scope === "org" ? repoPath : "__self", contributor)}" class="ml-1 Text-sc-17v1xeu-0 gce-stat-number Link--secondary" id="gce-${scope}-pr-count">...</a>
195189
</div>
196-
<div class="d-flex flex-items-center ml-auto">
197-
<div class="d-inline-flex flex-items-center mr-2">
198-
${ICONS.PR}<span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-org-pr-count">...</span>
199-
</div>
200-
<div class="d-inline-flex flex-items-center">
201-
${ICONS.ISSUE}<span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-org-issue-count">...</span>
202-
</div>
203-
</div>
204-
</div>
205-
206-
<!-- Account stats -->
207-
<div class="d-flex flex-items-center py-1">
208-
<div class="gce-scope-label">
209-
<span class="f6 color-fg-muted">In this account:</span>
210-
</div>
211-
<div class="d-flex flex-items-center ml-auto">
212-
<div class="d-inline-flex flex-items-center mr-2">
213-
${ICONS.PR}<span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-account-pr-count">...</span>
214-
</div>
215-
<div class="d-inline-flex flex-items-center">
216-
${ICONS.ISSUE}<span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-account-issue-count">...</span>
217-
</div>
190+
<div class="d-inline-flex flex-items-center">
191+
${ICONS.ISSUE}<a href="${issueOrPrLink("issue", scope === "repo" ? repoPath : scope === "org" ? repoPath : "__self", contributor)}" class="ml-1 Text-sc-17v1xeu-0 gce-stat-number Link--secondary" id="gce-${scope}-issue-count">...</a>
218192
</div>
219193
</div>
194+
</div>
195+
`;
196+
}
197+
198+
function createHoverPanelHTML({ contributor, repoPath, org }) {
199+
return `
200+
<div id="${ELEMENT_IDS.HOVER_PANEL}" class="position-absolute Box color-shadow-medium rounded-2 p-2" style="display: none; z-index: 100; top: 100%; left: -146px; min-width: 250px; margin-top: 4px;">
201+
<!-- Stats rows -->
202+
${createStatRow("repo", "In this repo:", contributor, repoPath)}
203+
${createStatRow("org", "In this org:", contributor, org)}
204+
${createStatRow("account", "In this account:", contributor, "__self")}
220205
221206
<div class="border-top mt-1 mb-1"></div>
222207
<div class="d-flex flex-items-center">
@@ -255,10 +240,15 @@ function injectStyles() {
255240
flex-shrink: 0;
256241
}
257242
.gce-stat-number {
258-
min-width: 24px;
243+
min-width: 30px;
259244
display: inline-block;
260245
text-align: right;
261246
font-variant-numeric: tabular-nums;
247+
text-decoration: none;
248+
}
249+
.gce-stat-number:hover {
250+
text-decoration: underline;
251+
color: var(--color-accent-fg);
262252
}
263253
#${ELEMENT_IDS.SYNC_BUTTON} {
264254
padding: 2px 0;
@@ -296,7 +286,7 @@ function injectInitialUI({ contributor, repoPath, currentNum, org }) {
296286
${ICONS.ISSUE}<span class="ml-1 Text-sc-17v1xeu-0" style="font-size: 12px; line-height: 1.5;">${"..."}</span>
297287
</a>
298288
299-
${createHoverPanelHTML({ contributor, repoPath })}
289+
${createHoverPanelHTML({ contributor, repoPath, org })}
300290
</div>
301291
</div>`,
302292
);
@@ -332,7 +322,7 @@ function setupHoverBehavior() {
332322

333323
$statsContainer.addEventListener("mouseenter", showPanel);
334324
$statsContainer.addEventListener("mouseleave", () => {
335-
setTimeout(hidePanel, 100);
325+
setTimeout(hidePanel, CONFIG.HOVER_DELAY);
336326
});
337327

338328
$hoverPanel.addEventListener("mouseenter", () => {
@@ -366,6 +356,14 @@ function setupSyncButton({ contributor, repoPath, currentNum, org }) {
366356
});
367357
}
368358

359+
// Check if cache is expired
360+
function isCacheExpired(lastUpdate) {
361+
if (!lastUpdate) return true;
362+
363+
const now = Date.now();
364+
return now - lastUpdate > CONFIG.CACHE_EXPIRATION;
365+
}
366+
369367
// Fetch stats for all scopes (repo, org, account)
370368
function fetchAllStats({ contributor, repoPath, currentNum, org }) {
371369
// Fetch repo stats
@@ -448,14 +446,17 @@ function fetchStats({ contributor, repoPath, currentNum, scope, user }) {
448446
getStorage(contributor, repoPath).then((storage) => {
449447
const storageKey = `${contributor}|${user || repoPath}`;
450448
const storageRes = storage[storageKey] || {};
451-
452-
if (storageRes.prs !== undefined || storageRes.issues !== undefined) {
449+
450+
// Check if we have valid data that's not expired
451+
if ((storageRes.prs !== undefined || storageRes.issues !== undefined) &&
452+
!isCacheExpired(storageRes.lastUpdate)) {
453453
// Format the text for display
454454
const prText = formatText(storageRes.prs, storageRes.firstPrNumber, currentNum, scope);
455455
const issueText = formatText(storageRes.issues, storageRes.firstIssueNumber, currentNum, scope);
456456

457457
updateStatsDisplay({ prText, issueText, scope, lastUpdate: storageRes.lastUpdate });
458458
} else {
459+
// If cache is expired or no data, fetch fresh data
459460
getSyncStorage({ access_token: null }).then((res) => {
460461
Promise.all([
461462
contributorCount({
@@ -512,8 +513,8 @@ function padNumber(text) {
512513
// Only pad if it's a number
513514
const num = Number(text);
514515
if (!Number.isNaN(num) && text !== "...") {
515-
// Right-align with space padding (only pad to 3 digits for compactness)
516-
return text.toString().padStart(3, ' ');
516+
// Right-align with space padding
517+
return text.toString().padStart(CONFIG.STAT_PADDING, ' ');
517518
}
518519
return text;
519520
}

0 commit comments

Comments
 (0)