Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
04ba008
feat(pwa): add offline support and installable PWA with Serwist
abimbolaalabi Jul 21, 2026
c45fccd
fix: add --legacy-peer-deps to CI and sync lockfile
abimbolaalabi Jul 21, 2026
eb410d2
Merge pull request #111 from abimbolaalabi/feat/enhancement-progressive
zeemscript Jul 21, 2026
14ccd29
docs(readme): add live application screenshots
abimbolaalabi Jul 21, 2026
37c25df
Merge pull request #112 from abimbolaalabi/replace-placeholder
zeemscript Jul 21, 2026
918862d
feat(dashboard): add educator earnings analytics dashboard
IamOluwatoyin Jul 23, 2026
7db4b0d
Merge pull request #136 from IamOluwatoyin/features/educator-dashboar…
zeemscript Jul 23, 2026
2bf7cc3
unified saved hub and bookmark refactor (Closes #132)
Ahbiz Jul 23, 2026
036a014
added prayer times and Hijri date widget to dashboard (Closes #133)
Ahbiz Jul 23, 2026
5d9601e
fix: restore optimistically removed items on rollback and guard stale…
Ahbiz Jul 23, 2026
c49b1f5
fix: address CodeRabbit review feedback for prayer times widget
Ahbiz Jul 23, 2026
1be9b57
feat(purchases): add My Purchases library with Stellar payment receipts
IamOluwatoyin Jul 23, 2026
8443492
Merge branch 'dev' into features/my-purchases-library
IamOluwatoyin Jul 23, 2026
92ef962
fix: add trailing comma to ShoppingBag import in nav-routers
IamOluwatoyin Jul 23, 2026
c35d30a
Merge pull request #140 from IamOluwatoyin/features/my-purchases-library
zeemscript Jul 23, 2026
8a24dde
Merge pull request #139 from Ahbiz/feature/issue-133
zeemscript Jul 23, 2026
48767c6
chore: resolve merge conflict with upstream/dev in nav-routers.jsx an…
Ahbiz Jul 23, 2026
f8edf20
Merge pull request #138 from Ahbiz/feature/issue-132
zeemscript Jul 24, 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
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
legacy-peer-deps=true
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@

---

## 📸 Screenshots

| Landing Page | Login / Sign Up |
|:---:|:---:|
| ![Landing Page](docs/screenshots/landing.png) | ![Login](docs/screenshots/login.png) |

| Dashboard | Courses |
|:---:|:---:|
| ![Dashboard](docs/screenshots/dashboard.png) | ![Courses](docs/screenshots/courses.png) |

| Library | Wallet & Payments |
|:---:|:---:|
| ![Library](docs/screenshots/library.png) | ![Wallet](docs/screenshots/wallet.png) |

---

## About

Deen Bridge is a modern learning platform that connects Muslims worldwide with authentic Islamic knowledge. Learners enroll in interactive courses, read from a digital library, join live community spaces, message mentors directly, and get instant answers from an Islamic-knowledge AI assistant. Courses and books are purchased with **USDC on the Stellar network** — non-custodial, with creators paid directly to their own wallets.
Expand Down
48 changes: 47 additions & 1 deletion app/account/settings/page.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { useAuth } from "@/hooks/useAuth";
import {
Card,
Expand Down Expand Up @@ -46,6 +46,29 @@ const SettingsPage = () => {
const [showNewPassword, setShowNewPassword] = useState(false);
const [message, setMessage] = useState({ type: "", text: "" });

const [deferredPrompt, setDeferredPrompt] = useState(null);
const [installable, setInstallable] = useState(false);

useEffect(() => {
const handler = (e) => {
e.preventDefault();
setDeferredPrompt(e);
setInstallable(true);
};
window.addEventListener("beforeinstallprompt", handler);
return () => window.removeEventListener("beforeinstallprompt", handler);
}, []);

const handleInstall = async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const result = await deferredPrompt.userChoice;
if (result.outcome === "accepted") {
setInstallable(false);
}
setDeferredPrompt(null);
};
Comment on lines +62 to +70

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Install banner becomes a dead end after the user dismisses the native prompt.

installable only resets to false on "accepted". On dismiss, deferredPrompt is cleared but the banner (which gates on installable) stays visible — clicking "Install" again then silently no-ops since handleInstall early-returns when deferredPrompt is null.

🐛 Proposed fix
   const handleInstall = async () => {
     if (!deferredPrompt) return;
     deferredPrompt.prompt();
     const result = await deferredPrompt.userChoice;
-    if (result.outcome === "accepted") {
-      setInstallable(false);
-    }
+    setInstallable(false);
     setDeferredPrompt(null);
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleInstall = async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const result = await deferredPrompt.userChoice;
if (result.outcome === "accepted") {
setInstallable(false);
}
setDeferredPrompt(null);
};
const handleInstall = async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const result = await deferredPrompt.userChoice;
setInstallable(false);
setDeferredPrompt(null);
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/account/settings/page.jsx` around lines 62 - 70, Update handleInstall so
installable is set to false after any userChoice outcome, not only when the
outcome is "accepted"; continue clearing deferredPrompt afterward so dismissing
the native prompt cannot leave the banner visible with no deferred prompt.


// Profile state
const [profile, setProfile] = useState({
avatar: user?.avatar || "",
Expand Down Expand Up @@ -213,6 +236,29 @@ const SettingsPage = () => {
</AlertDescription>
</Alert>
)}

{installable && (
<div className="mb-4 flex items-center justify-between rounded-xl border border-accent/20 bg-accent/5 p-4">
<div className="flex items-center gap-3">
<Smartphone className="h-5 w-5 text-accent" />
<p className="text-sm font-medium text-foreground">
Install DeenBridge for quick access
</p>
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
onClick={() => setInstallable(false)}
>
Dismiss
</Button>
<Button size="sm" onClick={handleInstall}>
Install
</Button>
</div>
</div>
)}
</div>
<Tabs
value={activeTab}
Expand Down
Loading
Loading