+
+ {isQuiz ? (
+
+ ) : null}
@@ -497,13 +585,15 @@ export function QuizPollPresentPage() {
totalResponses={stats?.totalResponses ?? 0}
revealCorrect={revealCorrect}
/>
- ) : (
+ ) : tab === "participants" ? (
+ ) : (
+
)}
diff --git a/src/pages/QuizPollTakePage.tsx b/src/pages/QuizPollTakePage.tsx
index a945a76..5158125 100644
--- a/src/pages/QuizPollTakePage.tsx
+++ b/src/pages/QuizPollTakePage.tsx
@@ -13,12 +13,11 @@ import {
import { isFirebaseClientConfigured } from "../lib/firebaseClient";
import type { RtdbQuizPollDetail, RtdbQuizPollQuestion } from "../lib/sessionQuizPollsRtdb";
import {
- firstQuizMasterOrManualVisibleId,
isQuizPollVisibleForAudience,
settingsDisplayMode,
subscribeSessionQuizPollDetail,
} from "../lib/sessionQuizPollsRtdb";
-import { useSessionQuizPollsRtdb } from "../hooks/useSessionQuizPollsRtdb";
+import { useAutoNavigateToLiveQuizPoll } from "../hooks/useAutoNavigateToLiveQuizPoll";
import { getAttendeeToken } from "../lib/attendeeAuth";
import { notifyRewardFromApiData } from "../lib/rewardCelebration";
import type { LoginPreviewResponse } from "../types/visitorContext";
@@ -186,10 +185,14 @@ export function QuizPollTakePage() {
const sessionId = searchParams.get("session_id")?.trim() || "";
const quizPollId = searchParams.get("quiz_poll_id")?.trim() || "";
- const { items: sessionLiveQuizPolls, loading: sessionQuizPollListLoading } =
- useSessionQuizPollsRtdb(sessionId);
-
const ctx = readStoredVisitorContext();
+
+ useAutoNavigateToLiveQuizPoll({
+ eventId,
+ sessionId,
+ currentQuizPollId: quizPollId || null,
+ enabled: Boolean(eventId && sessionId && quizPollId),
+ });
const [quiz, setQuiz] = useState
(null);
const [loading, setLoading] = useState(true);
const [err, setErr] = useState(null);
@@ -263,28 +266,6 @@ export function QuizPollTakePage() {
return () => unsub();
}, [eventId, sessionId, quizPollId]);
- useEffect(() => {
- if (!eventId || !sessionId || !quizPollId) return;
- if (sessionQuizPollListLoading) return;
- const target = firstQuizMasterOrManualVisibleId(sessionLiveQuizPolls);
- if (!target || target === quizPollId) return;
- navigate(
- `/quiz-poll?${new URLSearchParams({
- event_id: eventId,
- session_id: sessionId,
- quiz_poll_id: target,
- }).toString()}`,
- { replace: true },
- );
- }, [
- eventId,
- sessionId,
- quizPollId,
- sessionQuizPollListLoading,
- sessionLiveQuizPolls,
- navigate,
- ]);
-
const [answers, setAnswers] = useState>({});
const [submitted, setSubmitted] = useState(false);
const [submitting, setSubmitting] = useState(false);
diff --git a/src/pages/RatingsPage.tsx b/src/pages/RatingsPage.tsx
index e3f5f10..97a76b3 100644
--- a/src/pages/RatingsPage.tsx
+++ b/src/pages/RatingsPage.tsx
@@ -3,6 +3,7 @@ import { Link, useSearchParams } from "react-router-dom";
import { HeaderMobile } from "../components/HeaderMobile";
import { MobileShell } from "../components/MobileShell";
import { DynamicFeedbackForm } from "../components/DynamicFeedbackForm";
+import { useAutoNavigateToLiveQuizPoll } from "../hooks/useAutoNavigateToLiveQuizPoll";
import { useEventRegistrationSettings } from "../hooks/useEventRegistrationSettings";
import {
getVisitorFeedbackFormSchema,
@@ -21,6 +22,12 @@ export function RatingsPage() {
const eventId = urlEventId || ctx?.eventId || "";
const sessionId = urlSessionId || ctx?.sessionId || "";
+ useAutoNavigateToLiveQuizPoll({
+ eventId,
+ sessionId,
+ enabled: Boolean(eventId && sessionId),
+ });
+
const [eventSnapshot, setEventSnapshot] = useState(
ctx?.eventId === eventId && ctx.event ? ctx.event : null,
);
diff --git a/src/pages/SingleEventPage.tsx b/src/pages/SingleEventPage.tsx
index 1c96537..cde5835 100644
--- a/src/pages/SingleEventPage.tsx
+++ b/src/pages/SingleEventPage.tsx
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { Bookmark, Calendar, ChevronDown, ChevronRight, Clock, ExternalLink, MapPin, MessageCircle, Star } from "lucide-react";
-import { Link, useNavigate, useSearchParams } from "react-router-dom";
+import { Link, useSearchParams } from "react-router-dom";
import { HeaderMobile } from "../components/HeaderMobile";
import {
groupSessionsIntoAgendaDays,
@@ -28,8 +28,7 @@ import type { LoginPreviewResponse } from "../types/visitorContext";
import { readStoredVisitorContext } from "../types/visitorContext";
import { useSessionBookmarks } from "../hooks/useSessionBookmarks";
import { useEventRegistrationSettings } from "../hooks/useEventRegistrationSettings";
-import { useSessionQuizPollsRtdb } from "../hooks/useSessionQuizPollsRtdb";
-import { firstQuizMasterOrManualVisibleId } from "../lib/sessionQuizPollsRtdb";
+import { useAutoNavigateToLiveQuizPoll } from "../hooks/useAutoNavigateToLiveQuizPoll";
type SessionTabId = "key-persons" | "my-sessions" | "agenda";
@@ -230,7 +229,6 @@ function headerEventNameForEvent(
export function SingleEventPage() {
const [searchParams] = useSearchParams();
- const navigate = useNavigate();
const eventId = searchParams.get("event_id")?.trim() || "";
const sessionId = searchParams.get("session_id")?.trim() || "";
const ctx = readStoredVisitorContext();
@@ -250,28 +248,11 @@ export function SingleEventPage() {
const reg = useEventRegistrationSettings(eventInfo);
- const { items: sessionLiveQuizPolls, loading: rtdbQuizPollsLoading } =
- useSessionQuizPollsRtdb(sessionId);
-
- useEffect(() => {
- if (!eventId || !sessionId || loading || err || !session) return;
- if (rtdbQuizPollsLoading) return;
- const target = firstQuizMasterOrManualVisibleId(sessionLiveQuizPolls);
- if (!target) return;
- navigate(
- `/quiz-poll?${new URLSearchParams({ event_id: eventId, session_id: sessionId, quiz_poll_id: target }).toString()}`,
- { replace: true },
- );
- }, [
+ useAutoNavigateToLiveQuizPoll({
eventId,
sessionId,
- loading,
- err,
- session,
- rtdbQuizPollsLoading,
- sessionLiveQuizPolls,
- navigate,
- ]);
+ enabled: Boolean(eventId && sessionId && !loading && !err && session),
+ });
useEffect(() => {
if (!eventId || !sessionId) {
diff --git a/src/types/visitorContext.ts b/src/types/visitorContext.ts
index 46bc23f..1f9126b 100644
--- a/src/types/visitorContext.ts
+++ b/src/types/visitorContext.ts
@@ -94,10 +94,34 @@ export type VisitorJoinContext = {
trackId?: string;
};
+/** Optional return path after visitor auth (e.g. photo-wall upload). */
+export const EB_POST_AUTH_RETURN_KEY = "eb_post_auth_return";
+
+export function setPostAuthReturnPath(path: string) {
+ try {
+ if (path && path.startsWith("/")) sessionStorage.setItem(EB_POST_AUTH_RETURN_KEY, path);
+ } catch {
+ /* ignore */
+ }
+}
+
+export function consumePostAuthReturnPath(): string | null {
+ try {
+ const path = sessionStorage.getItem(EB_POST_AUTH_RETURN_KEY);
+ if (path) sessionStorage.removeItem(EB_POST_AUTH_RETURN_KEY);
+ return path && path.startsWith("/") ? path : null;
+ } catch {
+ return null;
+ }
+}
+
/**
* After login / profile completion: `/event|session|track?event_id=&session_id=&track_id=`
+ * Honors an optional return path set via `setPostAuthReturnPath`.
*/
export function buildPostAuthUrl(ctx: VisitorJoinContext | EbVisitorContext | null): string {
+ const ret = consumePostAuthReturnPath();
+ if (ret) return ret;
if (!ctx) return "/event";
const params = new URLSearchParams();
params.set("event_id", ctx.eventId);