diff --git a/CHANGELOG.md b/CHANGELOG.md index 827bdff3..78738d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1079,3 +1079,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - Prevented locale layout rendering from failing when blog category loading throws at runtime - Prevented duplicate slashes in homepage canonical URLs when `NEXT_PUBLIC_SITE_URL` ends with `/` + +## [1.0.14] - 2026-08-18 + +### Added + +- Last-used login method indicator: + - Remembers successful email, Google, or GitHub sign-in in a secure, HTTP-only cookie + - Highlights the matching option when the user returns to the login page + - Adds localized labels for English, Ukrainian, and Polish + - Includes unit coverage for badge rendering and accessibility wiring + +### Changed + +- Homepage delivery: + - Converted the Features section to an async Server Component with server-side translations + - Added a Suspense boundary so the Features section can stream independently + - Deferred mounting and loading the Footer client chunk until it is within 300px of the viewport + - Removed unused homepage state and React imports +- About page social metric: + - Updated the LinkedIn followers fallback to `2.4k` + - Refreshed the cached platform-statistics key so the new value is served immediately +- Test tooling security: + - Updated Vitest and its V8 coverage provider from vulnerable `4.0.x` versions to patched `4.1.10` + +### Fixed + +- Hardened post-login redirect validation against duplicate query values, external URLs, protocol-relative URLs, backslashes, and control-character normalization bypasses +- Added accessible descriptions linking the “Last used” badge to email and OAuth login buttons diff --git a/frontend/app/[locale]/login/page.tsx b/frontend/app/[locale]/login/page.tsx index f8cea2ed..f03dbc2f 100644 --- a/frontend/app/[locale]/login/page.tsx +++ b/frontend/app/[locale]/login/page.tsx @@ -1,16 +1,25 @@ -'use client'; - -import { useSearchParams } from 'next/navigation'; -import { useLocale } from 'next-intl'; - import { LoginForm } from '@/components/auth/LoginForm'; import { getSafeRedirect } from '@/lib/auth/safe-redirect'; +import { getLastLoginMethod } from '@/lib/auth-last-login'; -export default function LoginPage() { - const locale = useLocale(); - const searchParams = useSearchParams(); +export default async function LoginPage({ + params, + searchParams, +}: { + params: Promise<{ locale: string }>; + searchParams: Promise<{ returnTo?: string | string[] }>; +}) { + const { locale } = await params; + const { returnTo: returnToParam } = await searchParams; - const returnTo = getSafeRedirect(searchParams.get('returnTo')); + const returnTo = getSafeRedirect(returnToParam); + const lastLoginMethod = await getLastLoginMethod(); - return ; + return ( + + ); } diff --git a/frontend/app/[locale]/page.tsx b/frontend/app/[locale]/page.tsx index 99624b80..42813f8b 100644 --- a/frontend/app/[locale]/page.tsx +++ b/frontend/app/[locale]/page.tsx @@ -1,9 +1,10 @@ import { getTranslations } from 'next-intl/server'; +import { Suspense } from 'react'; import FeaturesHeroSection from '@/components/home/FeaturesHeroSection'; import HomePageScroll from '@/components/home/HomePageScroll'; import WelcomeHeroSection from '@/components/home/WelcomeHeroSection'; -import Footer from '@/components/shared/Footer'; +import LazyFooter from '@/components/shared/LazyFooter'; export async function generateMetadata({ params, @@ -75,9 +76,11 @@ export default function Home() { data-home-step className="min-h-[calc(100dvh-4rem)] shrink-0 snap-start [scroll-snap-stop:always]" > - + + + -