Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 19 additions & 29 deletions app/api/card/route.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ImageResponse } from '@vercel/og';
import { unstable_cache } from 'next/cache';
import { promises as fs } from 'fs';
import path from 'path';
import { classifyAgent, getPowerTier } from '../../../lib/agent-class.js';
Expand All @@ -10,16 +9,18 @@ import { getCosmosContainer } from '../../../lib/cosmos.js';

export const runtime = 'nodejs';

async function loadRankedDevelopers() {
async function getDeveloper(login) {
const cosmosContainer = getCosmosContainer();
if (cosmosContainer) {
try {
const { resources } = await cosmosContainer.items.query({
query: `SELECT c.id, c.login, c.name, c.avatarUrl, c.location, c.followers, c.totalStars, c.totalForks, c.totalWatchers, c.totalCommits, c.topLanguage, c.soReputation, c.soAnswers, c.soAcceptRate, c.soBadges, c.claimed
query: `SELECT TOP 1 c.id, c.login, c.name, c.avatarUrl, c.location, c.followers, c.totalStars, c.totalForks, c.totalWatchers, c.totalCommits, c.topLanguage, c.soReputation, c.soAnswers, c.soAcceptRate, c.soBadges, c.publicRepos, c.claimed, c.score, c.globalRank, c.globalTotal, c.country, c.countryRank, c.countryTotal, c.city, c.cityRank, c.cityTotal
FROM c
WHERE NOT IS_DEFINED(c.nomination) OR c.nomination.status = 'approved'`,
WHERE (c.login = @login OR c.id = @login)
AND (NOT IS_DEFINED(c.nomination) OR c.nomination.status = 'approved')`,
parameters: [{ name: '@login', value: login }],
}).fetchAll();
if (resources.length > 0) return addDeveloperRanks(scoreAll(resources));
if (resources[0]) return resources[0];
} catch (err) {
console.error('Card: Cosmos error', err.message);
}
Expand All @@ -28,17 +29,7 @@ async function loadRankedDevelopers() {
const filePath = path.join(process.cwd(), 'data', 'developers-sample.json');
const raw = await fs.readFile(filePath, 'utf-8');
const data = JSON.parse(raw);
return addDeveloperRanks(scoreAll(data));
}

const getRankedDevelopers = unstable_cache(
loadRankedDevelopers,
['card-ranked-developers-v1'],
{ revalidate: 3600, tags: ['developers'] }
);

async function getDeveloper(login) {
const developers = await getRankedDevelopers();
const developers = addDeveloperRanks(scoreAll(data));
return developers.find(d => d.login.toLowerCase() === login.toLowerCase()) || null;
}

Expand All @@ -52,7 +43,10 @@ async function loadAvatarDataUrl(avatarUrl) {
if (!avatarUrl) return null;

try {
const response = await fetch(avatarUrl, { signal: AbortSignal.timeout(3000) });
const response = await fetch(avatarUrl, {
signal: AbortSignal.timeout(3000),
next: { revalidate: 86400 },
});
if (!response.ok) return null;

const contentType = response.headers.get('content-type') || 'image/png';
Expand All @@ -63,12 +57,6 @@ async function loadAvatarDataUrl(avatarUrl) {
}
}

const getAvatarDataUrl = unstable_cache(
loadAvatarDataUrl,
['card-avatar-v1'],
{ revalidate: 86400 }
);

export async function GET(request) {
const { searchParams } = new URL(request.url);
const login = searchParams.get('login');
Expand All @@ -82,9 +70,10 @@ export async function GET(request) {
return new Response('Developer not found', { status: 404 });
}

const agent = classifyAgent(dev);
const power = getPowerTier(dev.score || 0);
const avatarDataUrl = await getAvatarDataUrl(dev.avatarUrl);
const score = Number.isFinite(dev.score) ? dev.score : 0;
const agent = classifyAgent({ ...dev, score });
const power = getPowerTier(score);
const avatarDataUrl = await loadAvatarDataUrl(dev.avatarUrl);
const avatarInitial = (dev.name || dev.login).trim().charAt(0).toUpperCase();
const agentMark = agent.name
.replace(/^The\s+/, '')
Expand All @@ -93,6 +82,7 @@ export async function GET(request) {
.join('')
.slice(0, 2)
.toUpperCase();
const hasGlobalRank = Boolean(dev.globalRank && dev.globalTotal);
const hasCityRank = Boolean(dev.cityRank && dev.city);
const rankCardWidth = hasCityRank ? '166' : dev.countryRank ? '255' : '522';

Expand Down Expand Up @@ -328,7 +318,7 @@ export async function GET(request) {
</div>

{/* Global and local rank */}
<div style={{ display: 'flex', gap: '12', marginBottom: '22' }}>
{hasGlobalRank && <div style={{ display: 'flex', gap: '12', marginBottom: '22' }}>
<div
style={{
display: 'flex',
Expand Down Expand Up @@ -390,7 +380,7 @@ export async function GET(request) {
<div style={{ display: 'flex', color: '#ddd6fe', fontSize: '11', fontWeight: '700', letterSpacing: '1.2' }}>IN {dev.city.toUpperCase()}</div>
</div>
)}
</div>
</div>}

{/* Score + Tier */}
<div
Expand All @@ -417,7 +407,7 @@ export async function GET(request) {
display: 'flex',
}}
>
{dev.score}
{score}
</div>
<div
style={{
Expand Down
4 changes: 2 additions & 2 deletions app/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ export default function Home() {
useEffect(() => {
async function loadData() {
try {
fetch('/api/developers/count')
fetch('/api/developers/count', { signal: AbortSignal.timeout(10000) })
.then(res => res.ok ? res.json() : null)
.then(data => {
if (Number.isInteger(data?.count)) setDatasetCount(data.count);
})
.catch(() => {});

const res = await fetch('/api/developers');
const res = await fetch('/api/developers', { signal: AbortSignal.timeout(30000) });
if (!res.ok) throw new Error(`Failed to load data: ${res.status}`);
const raw = await res.json();
const scored = addDeveloperRanks(scoreAll(raw));
Expand Down
23 changes: 11 additions & 12 deletions components/LoadingOverlay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@ function AnimatedCounter({ target, duration = 2000, suffix = '' }) {
const [count, setCount] = useState(0);

useEffect(() => {
let start = 0;
const step = target / (duration / 16);
const timer = setInterval(() => {
start += step;
if (start >= target) {
setCount(target);
clearInterval(timer);
} else {
setCount(Math.floor(start));
}
}, 16);
return () => clearInterval(timer);
let frame;
const startedAt = performance.now();

const update = (now) => {
const progress = Math.min((now - startedAt) / duration, 1);
setCount(Math.floor(target * progress));
if (progress < 1) frame = requestAnimationFrame(update);
};

frame = requestAnimationFrame(update);
return () => cancelAnimationFrame(frame);
}, [target, duration]);

return <>{count.toLocaleString()}{suffix}</>;
Expand Down
2 changes: 1 addition & 1 deletion lib/site.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const DEFAULT_SITE_URL = 'https://www.devglobe.dev';
export const SOCIAL_PREVIEW_VERSION = '5';
export const SOCIAL_PREVIEW_VERSION = '6';

export function getSiteUrl() {
const configuredUrl = process.env.NEXT_PUBLIC_SITE_URL?.trim();
Expand Down
Loading