feat(ui): show repo count in contribution graph summaries
Add "in N repositories" to both the year and all-time graph summary lines. Year graph counts repos with overlapping activity; all-time graph uses total project count. OG image includes repo count too. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -180,7 +180,10 @@ async fn og_contributions(
|
|||||||
.await
|
.await
|
||||||
.map_err(internal)?;
|
.map_err(internal)?;
|
||||||
|
|
||||||
let png = render_contributions_png(&counts, earliest, today).map_err(|e| ApiError {
|
let projects = state.store.list_projects().await.map_err(internal)?;
|
||||||
|
let repo_count = projects.len();
|
||||||
|
|
||||||
|
let png = render_contributions_png(&counts, earliest, today, repo_count).map_err(|e| ApiError {
|
||||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
message: e,
|
message: e,
|
||||||
})?;
|
})?;
|
||||||
@@ -199,6 +202,7 @@ fn render_contributions_png(
|
|||||||
counts: &[DailyCount],
|
counts: &[DailyCount],
|
||||||
from: NaiveDate,
|
from: NaiveDate,
|
||||||
to: NaiveDate,
|
to: NaiveDate,
|
||||||
|
repo_count: usize,
|
||||||
) -> Result<Vec<u8>, String> {
|
) -> Result<Vec<u8>, String> {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -280,8 +284,13 @@ fn render_contributions_png(
|
|||||||
let mut svg = format!(
|
let mut svg = format!(
|
||||||
r#"<svg xmlns="http://www.w3.org/2000/svg" width="{svg_w}" height="{svg_h}" viewBox="0 0 {svg_w} {svg_h}"><rect width="100%" height="100%" fill="{bg}"/>"#,
|
r#"<svg xmlns="http://www.w3.org/2000/svg" width="{svg_w}" height="{svg_h}" viewBox="0 0 {svg_w} {svg_h}"><rect width="100%" height="100%" fill="{bg}"/>"#,
|
||||||
);
|
);
|
||||||
|
let repo_text = if repo_count > 0 {
|
||||||
|
format!(" in {repo_count} repositories")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
svg.push_str(&format!(
|
svg.push_str(&format!(
|
||||||
r##"<text x="{x}" y="18" fill="#ecf0f1" font-size="12" opacity="0.6">{total} contributions since {from}</text>"##,
|
r##"<text x="{x}" y="18" fill="#ecf0f1" font-size="12" opacity="0.6">{total} contributions since {from}{repo_text}</text>"##,
|
||||||
x = year_label_w,
|
x = year_label_w,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { fetchDailyCounts, fetchSources } from '../api/client';
|
import { fetchDailyCounts, fetchProjects, fetchSources } from '../api/client';
|
||||||
|
|
||||||
const CELL_SIZE = 12;
|
const CELL_SIZE = 12;
|
||||||
const GAP = 3;
|
const GAP = 3;
|
||||||
@@ -37,6 +37,23 @@ export function ContributionGraph() {
|
|||||||
staleTime: 5 * 60_000,
|
staleTime: 5 * 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const projectsQ = useQuery({
|
||||||
|
queryKey: ['projects'],
|
||||||
|
queryFn: fetchProjects,
|
||||||
|
staleTime: 60_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const repoCount = useMemo(() => {
|
||||||
|
if (!projectsQ.data) return 0;
|
||||||
|
const fromMs = from.getTime();
|
||||||
|
const toMs = to.getTime();
|
||||||
|
return projectsQ.data.filter((p) => {
|
||||||
|
const first = p.first_activity ? new Date(p.first_activity).getTime() : Infinity;
|
||||||
|
const last = p.last_activity ? new Date(p.last_activity).getTime() : 0;
|
||||||
|
return last >= fromMs && first <= toMs;
|
||||||
|
}).length;
|
||||||
|
}, [projectsQ.data]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { weeks, monthMarkers, thresholds, totalCount } = useMemo(() => {
|
const { weeks, monthMarkers, thresholds, totalCount } = useMemo(() => {
|
||||||
@@ -89,6 +106,7 @@ export function ContributionGraph() {
|
|||||||
<div className="contribution-graph mb-3">
|
<div className="contribution-graph mb-3">
|
||||||
<p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
|
<p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
|
||||||
{totalCount} contributions in the last year
|
{totalCount} contributions in the last year
|
||||||
|
{repoCount > 0 && ` in ${repoCount} repositories`}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`} width="100%" className="d-block">
|
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`} width="100%" className="d-block">
|
||||||
@@ -155,6 +173,13 @@ export function AllTimeGraph() {
|
|||||||
return dates.length > 0 ? new Date(Math.min(...dates.map((d) => d.getTime()))) : null;
|
return dates.length > 0 ? new Date(Math.min(...dates.map((d) => d.getTime()))) : null;
|
||||||
}, [sourcesQ.data]);
|
}, [sourcesQ.data]);
|
||||||
|
|
||||||
|
const projectsQ = useQuery({
|
||||||
|
queryKey: ['projects'],
|
||||||
|
queryFn: fetchProjects,
|
||||||
|
staleTime: 60_000,
|
||||||
|
});
|
||||||
|
const repoCount = projectsQ.data?.length ?? 0;
|
||||||
|
|
||||||
const to = new Date();
|
const to = new Date();
|
||||||
const from = earliest ?? new Date(to.getFullYear() - 5, 0, 1);
|
const from = earliest ?? new Date(to.getFullYear() - 5, 0, 1);
|
||||||
const fromStr = fmt(from);
|
const fromStr = fmt(from);
|
||||||
@@ -227,6 +252,7 @@ export function AllTimeGraph() {
|
|||||||
<div className="contribution-graph mb-4">
|
<div className="contribution-graph mb-4">
|
||||||
<p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
|
<p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
|
||||||
{totalCount} contributions since {fmt(from)}
|
{totalCount} contributions since {fmt(from)}
|
||||||
|
{repoCount > 0 && ` in ${repoCount} repositories`}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`} width="100%" className="d-block">
|
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`} width="100%" className="d-block">
|
||||||
|
|||||||
Reference in New Issue
Block a user