From cbbfb145dc3e5b0da55bd894afc738c317bcf373 Mon Sep 17 00:00:00 2001 From: mobiniqow Date: Sun, 20 Sep 2026 12:26:41 +0330 Subject: [PATCH] Add gamification, micro-animations, and a mini-game for empty states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies a few Hook Model (trigger/action/reward/investment) patterns on top of the existing swipe/match loop: - Daily streak: a flame badge in the home header, backed by the new checkin endpoint — an external trigger to return daily. - Profile completeness meter + badge collection on the profile page (client-computed from existing data) — the investment loop nudging users to fill out bio/photos/answers. - Bio editor on the profile page, shown on swipe cards and candidate detail so profiles read richer. - Catch the Hearts: a small tap mini-game (falling hearts, bonus items, 20s timer, persisted high score) shown instead of a dead end when the discover feed runs out, so there's still a reason to stay in the app. - Micro-animations: heart-burst on like, confetti on match, tap feedback on all buttons (active:scale-95). Fixed a React "purity" lint violation along the way (Math.random() can't be called inside useMemo under this project's rules — moved to useState lazy initializers, which run once on mount as intended). --- src/app/(main)/home/[id]/page.tsx | 6 + src/app/(main)/home/page.tsx | 11 +- src/app/(main)/profile/page.tsx | 193 +++++++++++++++++++- src/components/StreakBadge.tsx | 25 +++ src/components/discover/CatchHeartsGame.tsx | 172 +++++++++++++++++ src/components/discover/MatchModal.tsx | 40 ++++ src/components/discover/SwipeCard.tsx | 5 + src/components/discover/SwipeDeck.tsx | 53 ++++-- src/components/ui/Button.tsx | 4 +- src/lib/api.ts | 20 ++ src/lib/auth-context.tsx | 16 ++ 11 files changed, 524 insertions(+), 21 deletions(-) create mode 100644 src/components/StreakBadge.tsx create mode 100644 src/components/discover/CatchHeartsGame.tsx diff --git a/src/app/(main)/home/[id]/page.tsx b/src/app/(main)/home/[id]/page.tsx index bb5a542..7ac756c 100644 --- a/src/app/(main)/home/[id]/page.tsx +++ b/src/app/(main)/home/[id]/page.tsx @@ -180,6 +180,12 @@ export default function ProfileDetailPage() { )} + {profile.bio && ( +

+ {profile.bio} +

+ )} + {(profile.height_cm || profile.weight_kg) && ( } title="قد و وزن">
diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index 52dab2b..c1295a1 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -12,6 +12,7 @@ import { useAuth } from "@/lib/auth-context"; import { formatApiError } from "@/lib/hooks"; import { SwipeDeck } from "@/components/discover/SwipeDeck"; import { FiltersSheet, DiscoverFilters } from "@/components/discover/FiltersSheet"; +import { StreakBadge } from "@/components/StreakBadge"; import { DiscoverCandidate, getDiscoverFeed, updateLocation } from "@/lib/api"; const POLL_MS = 20_000; @@ -110,6 +111,7 @@ function Rejected({ reason }: { reason?: string }) { function DiscoverScreen() { const router = useRouter(); + const { streak } = useAuth(); const [filtersOpen, setFiltersOpen] = useState(false); const [filters, setFilters] = useState(DEFAULT_FILTERS); const [profiles, setProfiles] = useState(null); @@ -165,9 +167,12 @@ function DiscoverScreen() { return (
- - دلدل 💌 - +
+ + دلدل 💌 + + +
+ )} +
+ + {editing ? ( +
+