diff --git a/Dockerfile b/Dockerfile
index 64d8a57..8b17faa 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,7 +11,9 @@ WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_API_BASE_URL
+ARG NEXT_PUBLIC_SECURITY_KEY
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
+ENV NEXT_PUBLIC_SECURITY_KEY=$NEXT_PUBLIC_SECURITY_KEY
RUN npm run build
FROM base AS runner
diff --git a/next.config.ts b/next.config.ts
index 95db85b..1c1ba19 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -58,12 +58,16 @@ const nextConfig: NextConfig = {
],
},
{
- // Static pages – moderate cache
- source: "/:path((?!api/).*)",
+ // Application HTML pages – no-store to prevent HTML/shell caching in WebView
+ source: "/:path((?!api/|_next/|fonts/|assets/).*)",
headers: [
{
key: "Cache-Control",
- value: "public, max-age=3600, stale-while-revalidate=86400",
+ value: "no-store, no-cache, must-revalidate, max-age=0",
+ },
+ {
+ key: "Pragma",
+ value: "no-cache",
},
],
},
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index b176178..b0fe670 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -24,6 +24,14 @@ const amiri = Amiri({
fallback: ["Arial", "sans-serif"],
});
+const isDevelopment = process.env.NODE_ENV !== "production";
+
+// Never ship a fallback token to production: without it a real user whose
+// Flutter token has not been injected yet would silently browse a test account.
+const developmentFallbackToken = isDevelopment
+ ? (process.env.NEXT_PUBLIC_DEFAULT_TOKEN ?? "")
+ : "";
+
export const metadata: Metadata = {
title: "Habib Marriage",
description: "Islamic Marriage Platform",
@@ -117,7 +125,7 @@ export default function RootLayout({
}
},
get: function() {
- return this._habib_token || readCookie(HABIB_TOKEN_COOKIE) || sessionStorage.getItem(HABIB_TOKEN_COOKIE) || '${process.env.NEXT_PUBLIC_DEFAULT_TOKEN || "f3a7543b44ef0a713d1ee0d4f7866b3825cf1308"}';
+ return this._habib_token || readCookie(HABIB_TOKEN_COOKIE) || sessionStorage.getItem(HABIB_TOKEN_COOKIE) || undefined;
}
});
}
diff --git a/src/app/questions-list/page.tsx b/src/app/questions-list/page.tsx
index 80c3a1b..4727aef 100644
--- a/src/app/questions-list/page.tsx
+++ b/src/app/questions-list/page.tsx
@@ -18,6 +18,10 @@ import {
type QuestionListItem,
} from "@/data/question-data";
import { hasQuestionAnswerValue } from "@/components/questions/question-answer-storage";
+import {
+ clearMatchStartGrace,
+ markMatchStarted,
+} from "@/lib/match-start-grace";
import { useStartMarriageMatchMutation } from "@/hooks/marriage/use-match-start";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import { useMarriageSectionsQuery } from "@/hooks/marriage/use-sections";
@@ -36,16 +40,13 @@ export default function QuestionsListPage() {
});
const startMatchMutation = useStartMarriageMatchMutation({
onSuccess: () => {
- if (typeof window !== "undefined") {
- localStorage.setItem("match_submitted", "true");
- }
+ markMatchStarted();
router.push(localizePath("/finding-match", locale));
},
onError: () => {
- if (typeof window !== "undefined") {
- localStorage.setItem("match_submitted", "true");
- }
- router.push(localizePath("/finding-match", locale));
+ // Never pretend the request went through – the user stays here and can
+ // retry instead of being parked on the waiting screen forever.
+ clearMatchStartGrace();
},
});
const [isOptionalInfoSheetOpen, setIsOptionalInfoSheetOpen] = useState(false);
@@ -114,20 +115,11 @@ export default function QuestionsListPage() {
}, [questionListItems, sectionProgressBySlug]);
const handleStartMatch = () => {
- if (typeof window !== "undefined") {
- localStorage.setItem("match_submitted", "true");
- }
-
- if (!canStartMatch) {
- router.push(localizePath("/finding-match", locale));
+ if (isStartMatchDisabled) {
return;
}
- startMatchMutation.mutate(undefined, {
- onSettled: () => {
- router.push(localizePath("/finding-match", locale));
- },
- });
+ startMatchMutation.mutate();
};
return (
@@ -244,6 +236,14 @@ export default function QuestionsListPage() {
style={{ paddingBottom: "calc(16px + var(--safe-bottom))" }}
className="fixed inset-x-0 bottom-0 z-20 mx-auto w-full max-w-[375px] bg-[#F5F5F5]/95 px-[17px] pt-3 backdrop-blur-md"
>
+ {startMatchMutation.isError ? (
+
+ {t.questions.startMatchFailed}
+
+ ) : null}