From 0bf3331b6476db099c8e32771bbd2928b7b8b665 Mon Sep 17 00:00:00 2001 From: Viren070 Date: Sat, 26 Jul 2025 18:59:30 +0100 Subject: [PATCH] feat(frontend): improve changelog card --- .../frontend/src/components/menu/about.tsx | 329 ++++++++++-------- 1 file changed, 187 insertions(+), 142 deletions(-) diff --git a/packages/frontend/src/components/menu/about.tsx b/packages/frontend/src/components/menu/about.tsx index 600a0cae..1f67e619 100644 --- a/packages/frontend/src/components/menu/about.tsx +++ b/packages/frontend/src/components/menu/about.tsx @@ -40,6 +40,7 @@ import { import { Select } from '@/components/ui/select'; import { cn } from '@/components/ui/core/styling'; import { Textarea } from '../ui/textarea'; +import { GlowingEffect } from '../shared/glowing-effect'; export function AboutMenu() { return ( @@ -338,52 +339,52 @@ function ChangelogCard({ setPosition({ x, y }); }; - const stretchContent = - className?.includes('h-full') || className?.includes('flex'); - - const channelOptions = [ - { value: 'stable', label: 'Stable' }, - { value: 'nightly', label: 'Nightly' }, - ]; - return ( + {title && ( - -
- + +
+ {title} - {description && {description}} -
-
- + onChannelChange(value as 'stable' | 'nightly') + } + placeholder="Select channel" + className="w-32" + /> +
+ {description && ( + {description} + )}
)} - + {children}
@@ -395,7 +396,6 @@ function ChangelogBox({ version }: { version: string }) { const [error, setError] = React.useState(null); const [releases, setReleases] = React.useState([]); const [visibleCount, setVisibleCount] = React.useState(0); - const [autoLoading, setAutoLoading] = React.useState(false); const [selectedChannel, setSelectedChannel] = React.useState< 'stable' | 'nightly' >(() => { @@ -407,7 +407,8 @@ function ChangelogBox({ version }: { version: string }) { const [hasMorePages, setHasMorePages] = React.useState(true); const [currentPage, setCurrentPage] = React.useState(1); const [fetchingMore, setFetchingMore] = React.useState(false); - const loadMoreRef = React.useRef(null); + const [showLoadMoreOverlay, setShowLoadMoreOverlay] = React.useState(false); + const containerRef = React.useRef(null); // Fetch releases with pagination const fetchReleases = React.useCallback( @@ -527,43 +528,28 @@ function ChangelogBox({ version }: { version: string }) { filterReleasesByChannel, ]); - // Intersection Observer for auto-loading + // Show/hide load more overlay based on scroll position React.useEffect(() => { - if (!loadMoreRef.current || releases.length <= visibleCount) return; + const handleScroll = () => { + const container = containerRef.current; + if (!container) return; - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if ( - entry.isIntersecting && - !autoLoading && - releases.length > visibleCount - ) { - setAutoLoading(true); - // Small delay to show loading state - setTimeout(() => { - setVisibleCount((prev) => Math.min(prev + 5, releases.length)); - setAutoLoading(false); - }, 300); - } - }); - }, - { threshold: 0.1 } - ); + const { scrollTop, scrollHeight, clientHeight } = container; + const isNearBottom = scrollHeight - scrollTop - clientHeight < 50; - observer.observe(loadMoreRef.current); - return () => observer.disconnect(); - }, [visibleCount, releases.length, autoLoading]); + const hasMoreContent = + releases.length > visibleCount || // More releases in memory + (hasMorePages && !fetchingMore); // More pages to fetch - // Check if we need to fetch more releases when reaching the end - React.useEffect(() => { - if ( - visibleCount >= releases.length && - hasMorePages && - !fetchingMore && - !loading - ) { - fetchMoreReleases(); + setShowLoadMoreOverlay(isNearBottom && hasMoreContent && !loading); + }; + + const container = containerRef.current; + if (container) { + container.addEventListener('scroll', handleScroll); + // Check on mount and when dependencies change + handleScroll(); + return () => container.removeEventListener('scroll', handleScroll); } }, [ visibleCount, @@ -571,17 +557,30 @@ function ChangelogBox({ version }: { version: string }) { hasMorePages, fetchingMore, loading, - fetchMoreReleases, + containerRef, ]); const handleLoadMore = () => { - setVisibleCount((prev) => Math.min(prev + 5, releases.length)); + if (releases.length > visibleCount) { + // Load more from current releases + setVisibleCount((prev) => Math.min(prev + 5, releases.length)); + // Check if we need to fetch more after increasing visible count + if (releases.length <= visibleCount + 5 && hasMorePages) { + fetchMoreReleases(); + } + } else if (hasMorePages && !fetchingMore) { + // Fetch more releases from API + fetchMoreReleases(); + } }; const handleChannelChange = (channel: 'stable' | 'nightly') => { setSelectedChannel(channel); }; + const hasMoreContent = + releases.length > visibleCount || (hasMorePages && !fetchingMore); + return ( -
- {loading ? ( - <> - {[...Array(2)].map((_, i) => ( - - ))} - - ) : error ? ( - - ) : releases.length === 0 ? ( - - ) : ( - releases.slice(0, visibleCount).map((release, idx) => ( - - -
- - {release.name || release.tag_name} - - - {new Date(release.published_at).toLocaleDateString()} - -
- - {release.tag_name} - -
- - - {release.body || 'No changelog provided.'} - - - - +
+ {loading ? ( +
+ {[...Array(2)].map((_, i) => ( + + ))} +
+ ) : error ? ( +
+ +
+ ) : releases.length === 0 ? ( +
+ +
+ ) : ( +
+ {releases.slice(0, visibleCount).map((release, idx) => ( + - - View on GitHub - - - - )) - )} - - {/* Auto-load trigger element */} - {(releases.length > visibleCount || (hasMorePages && !fetchingMore)) && - !loading && ( -
- {autoLoading || fetchingMore ? ( -
-
- {fetchingMore - ? 'Loading more releases...' - : 'Loading more...'} -
- ) : ( - - )} + +
+
+ + {release.tag_name} + +
+
+ + {new Date(release.published_at).toLocaleDateString()} + +
+
+
+ + + {release.body || 'No changelog provided.'} + + + + + + View on GitHub + + + + ))}
)} +
+ + {/* Bottom Load More Overlay */} + {showLoadMoreOverlay && hasMoreContent && ( +
+
+
+ + {fetchingMore + ? 'Loading...' + : releases.length > visibleCount + ? `Load ${Math.min(5, releases.length - visibleCount)} more` + : 'Load more releases'} + + +
+
+
+ )}
);