Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
44a3baf
feat: add toast feedback for copy actions
shreyamittal0622 May 27, 2026
36217fe
feat: add toast feedback for copy actions
shreyamittal0622 May 27, 2026
68a21a2
fix: regenerate lockfile and resolve dependency sync
shreyamittal0622 May 27, 2026
7c1d5a9
feat: add toast feedback for copy actions
shreyamittal0622 May 27, 2026
60ff1bd
feat: add toast feedback for copy actions
shreyamittal0622 May 27, 2026
1a5ff06
test(api): verify no-cache header for ?theme=random (#489)
sandesh-861 May 27, 2026
6df6b32
Refactor/add portuguese locale (#570)
mahek888 May 27, 2026
4c4e68c
refactor(github): extract language color map
mahek888 May 27, 2026
e8780b0
test(dashboard): add LanguageChart gradient tests
sanzzzz-g May 27, 2026
1fa7f91
style(test): format sanitizeFont tests
sanzzzz-g May 27, 2026
6ee8249
test(api): add integration tests for og route (#607)
sanzzzz-g May 27, 2026
1080d16
docs(time): add JSDoc for UTC and timezone midnight functions
anishakujur367-alt May 27, 2026
3431942
test(svg): add year parameter route test
shambavi2007 May 27, 2026
eaedab7
docs(cache): add JSDoc comments for TTLCache
May 27, 2026
844c15a
refactor(svg): reuse renderHeader in auto theme renderer
VirenSumbly May 27, 2026
a9669d2
improved footer ui consistency
abhi-nav-25 May 27, 2026
0137c8c
refactor(dashboard): extract heatmap intensity utility
deepsikha-dash May 27, 2026
eb20c51
test(github): add fetchWithRetry unit tests for 429 and 5xx retry logic
shushant24 May 27, 2026
b786840
test: cover auto theme in streak route
mittalsonal May 27, 2026
1dc0d7f
refactor(types): add a branded hexcolor type to replace plain strings
MaitrayeeK May 27, 2026
033555b
refactor(ci): enforce no-console rule in production code
Nightkilller May 27, 2026
c931835
test(streak): Adds text colour parameter route tests (#600)
Ishanvee May 27, 2026
e9e2ec8
fix(github): cap max pages in fetchUserRepos to prevent DoS (#591)
riddhima25bet10005-a11y May 27, 2026
04a2ff6
fix(api): add IP rate limiting to track-user endpoint to prevent DoS …
riddhima25bet10005-a11y May 27, 2026
201aa07
refactor(github): add retry count to fetchWithRetry error message
May 27, 2026
c21ab50
test(github): add cache hit tests for profile and repos
shambavi2007 May 27, 2026
a68ab55
docs(github): document fetch retry backoff constants
May 27, 2026
ce9cb16
return to top
kanishka-2007-tech May 27, 2026
6049b9b
docs(api): add JSDoc to GET handler in app/api/streak/route.ts #359
Ishrath0 May 26, 2026
cccf23a
test(svg): add tests for auto-theme SVG dark palette
Ixotic27 May 27, 2026
198a804
refactor(dashboard): extract share action handlers into useShareActio…
shushant24 May 27, 2026
b761187
fix(useShareActions): revert setTimeout revoke to fix test assertions
shushant24 May 27, 2026
f0f810b
fix(useShareActions): add use client, fix native fallback error state…
shushant24 May 27, 2026
6613829
fix: update StatsCardSkeleton background color and fix missing class …
JhaSourav07 May 27, 2026
007c1fd
test(svg): add ghost-city mode integration test in route
Ixotic27 May 27, 2026
aa5a2d8
test(api): add test for ?lang= parameter in streak route
Ixotic27 May 27, 2026
1e7875d
test(api): add radius parameter route tests
shambavi2007 May 27, 2026
7a8e01e
test(dashboard): add tests for Achievements unlock logic
Ixotic27 May 27, 2026
b07e764
test(svg): add unit tests for computeTowers edge cases in layout.ts
Ixotic27 May 27, 2026
5d40028
docs(theme): improve dracula theme description (#479)
tanmaykapadnis May 27, 2026
3a2c2f5
fix(api): return 400 for invalid GitHub usernames in /api/streak (#516)
harendra-godara May 27, 2026
d02f0d9
refactor(svg): add hide_background=true tests in streak route
RavindiFernando May 27, 2026
4e8183e
feat(customize): expose font parameter as dropdown in Customization S…
RavindiFernando May 27, 2026
dc57d47
fix(cache): prevent unbounded TTLCache memory growth
KRUSHAL2956 May 27, 2026
f041ea2
docs: Add complete THEMES.md gallery with all 13 themes
May 27, 2026
a069667
docs: run prettier format before PR
May 27, 2026
093a90a
docs: reformat THEMES.md to pass Prettier CI check
May 27, 2026
5d29d24
test(utils): add tests for stripHash and isValidHex (#568)
abhilashgedela28-lang May 27, 2026
d062585
refactor(svg): document auto-theme css variables
May 27, 2026
1428aec
feat: add persistence utilities for recent searches using localStorage
JhaSourav07 May 27, 2026
81caf78
refactor(github): add type annotation to fetchWithRetry return type
May 27, 2026
9e68bcd
fix(TTLCache): reset insertion order on key update in set() [ GSSOC '…
rudra3007-pro May 27, 2026
3331509
refactor(i18n): add Korean locale to badge labels (#565)
mahek888 May 27, 2026
0325c6f
refactor: rename auto theme aliases
Harshitbhardwaj468 May 27, 2026
ec6354f
Docs/create architecture md (#638)
Divyanshi-kumari May 27, 2026
81e4f91
feat(calculate): add configurable grace period for streaks
smrithipiedy May 27, 2026
da3dda1
test(navbar): add mobile menu toggle unit tests
deepsikha-dash May 27, 2026
5c52366
test: add cacheKey unit tests (#566)
deepsikha-dash May 27, 2026
2fc3259
fix: simplify recent searches state initialization
shreyamittal0622 May 27, 2026
d7acce4
fix: remove unused storage helper functions
shreyamittal0622 May 28, 2026
104353d
fix: resolve footer JSX structure issue
shreyamittal0622 May 28, 2026
00d6093
resolve merge conflicts with upstream
shreyamittal0622 May 28, 2026
39b56bd
fix: restore removeSearch in recent searches hook
shreyamittal0622 May 28, 2026
bc99ed0
fix: update package lock dependencies
shreyamittal0622 May 28, 2026
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
9 changes: 5 additions & 4 deletions app/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Link from 'next/link';

export function Footer() {
return (
<footer className="mt-14 border-t border-black/15 dark:border-white/10 bg-transparent pt-10 pb-24 md:pb-6 text-sm transition-colors">
<div className="mx-auto flex max-w-7xl flex-col items-center text-center justify-between gap-8 px-6 md:flex-row md:items-start md:text-left">
<footer className="mt-14 border-t border-black/15 bg-transparent pt-10 pb-4 text-sm transition-colors dark:border-white/10">
<div className="mx-auto flex max-w-7xl flex-col items-start justify-between gap-8 px-6 md:flex-row">
Comment on lines +5 to +6
{/* LEFT */}
<div className="text-center md:text-left">
<h2 className="text-lg font-semibold text-black dark:text-white">CommitPulse</h2>
Expand All @@ -14,14 +14,14 @@ export function Footer() {
</div>

{/* RIGHT */}
<div className="flex flex-wrap justify-center items-center gap-6 font-medium text-zinc-600 dark:text-zinc-400">
{' '}
<div className="flex items-start gap-6 text-sm font-medium text-zinc-600 dark:text-zinc-400">
<Link
href="/contributors"
className="transition-colors duration-200 hover:text-black dark:hover:text-white"
>
Contributors
</Link>

<a
href="https://github.com/JhaSourav07/commitpulse/blob/main/README.md"
target="_blank"
Expand All @@ -30,6 +30,7 @@ export function Footer() {
>
Documentation
</a>

<a
href="https://github.com/jhasourav07"
target="_blank"
Expand Down
130 changes: 61 additions & 69 deletions hooks/useRecentSearches.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,92 @@
'use client';

import { useState, useEffect } from 'react';
import { useState } from 'react';

export const STORAGE_KEY = 'recentSearches';
export const MAX_SEARCHES = 5;

type State = { searches: string[]; mounted: boolean };

function loadFromStorage(): string[] {
let saved: string[] = [];
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) saved = JSON.parse(stored) as string[];
} catch {
// ignore malformed storage
}
return saved;
}
type State = {
searches: string[];
mounted: boolean;
};

function writeStorage(searches: string[] | null): void {
try {
if (searches === null) {
localStorage.removeItem(STORAGE_KEY);
return;
export function useRecentSearches() {
const [state, setState] = useState<State>(() => {
if (typeof window === 'undefined') {
return {
searches: [],
mounted: false,
};
}

localStorage.setItem(STORAGE_KEY, JSON.stringify(searches));
} catch {
// ignore storage write failures
}
}
try {
const stored = localStorage.getItem(STORAGE_KEY);

return {
searches: stored ? (JSON.parse(stored) as string[]) : [],
mounted: true,
};
Comment on lines +14 to +28
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have actually done the same only

} catch {
return {
searches: [],
mounted: true,
};
}
});

/**
* A hook to manage and persist a list of recent searches.
*
* It uses localStorage for persistence and ensures SSR compatibility by starting
* with an empty state on the first render and updating upon hydration.
*
* @returns An object containing the recent searches, a function to add a search, and a function to clear all searches.
*/
export function useRecentSearches() {
// Always start with [] and mounted:false on both server and client so the
// initial render matches (SSR-safe). A single setState in the mount effect
// reads from localStorage and flips mounted:true in one batch β€” this satisfies
// the react-hooks/set-state-in-effect rule which flags multiple synchronous
// setState calls inside an effect body.
const [state, setState] = useState<State>({ searches: [], mounted: false });

useEffect(() => {
// Single setState call β€” reads external system (localStorage) and syncs
// React state in one update, which is exactly what effects are for.
// eslint-disable-next-line react-hooks/set-state-in-effect
setState({ searches: loadFromStorage(), mounted: true });
}, []);

/**
* Adds a new search query to the recent searches list.
* If the query already exists, it is moved to the top.
* The list is truncated to the maximum number of searches allowed.
*
* @param query - The search query to add.
*/
const addSearch = (query: string) => {
if (!query.trim()) return;

setState((prev) => {
const deduped = [query, ...prev.searches.filter((s) => s !== query)].slice(0, MAX_SEARCHES);
writeStorage(deduped);
return { ...prev, searches: deduped };

try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(deduped));
} catch {
// ignore storage write errors
}

return {
...prev,
searches: deduped,
};
});
};

/**
* Clears all recent searches from state and localStorage.
*/
const clearSearches = () => {
writeStorage(null);
const removeSearch = (query: string) => {
setState((prev) => {
const updated = prev.searches.filter((s) => s !== query);

try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
} catch {
// ignore storage write errors
}

return {
...prev,
searches: updated,
};
});
};

const clearSearches = () => {
setState((prev) => ({
...prev,
searches: [],
}));
};

const removeSearch = (query: string): void => {
setState((prev) => {
const filtered = prev.searches.filter((s) => s !== query);
writeStorage(filtered);
return { ...prev, searches: filtered };
});
try {
localStorage.removeItem(STORAGE_KEY);
} catch {
// ignore storage clear errors
}
};

// Return empty searches until after hydration to prevent SSR/client mismatch.
return {
searches: state.mounted ? state.searches : [],
addSearch,
clearSearches,
removeSearch,
clearSearches,
};
}
Loading
Loading