feat: implement app content readiness handling in page and components

- Added `appContentReady` state to manage visibility of app content once loading is complete.
- Updated `useEffect` hooks in `CurrentDutyView` and `CalendarPage` to signal when content is ready, enhancing user experience by hiding native loading indicators.
- Refactored `Home` component to conditionally render content based on `appContentReady`, ensuring a smoother transition for users.
- Enhanced app store to include `setAppContentReady` method for state management.
This commit is contained in:
2026-03-03 18:11:02 +03:00
parent cac06f22fa
commit 68a153e4a7
4 changed files with 48 additions and 22 deletions

View File

@@ -16,7 +16,6 @@ import { CalendarHeader } from "@/components/calendar/CalendarHeader";
import { CalendarGrid } from "@/components/calendar/CalendarGrid";
import { DutyList } from "@/components/duty/DutyList";
import { DayDetail, type DayDetailHandle } from "@/components/day-detail";
import { callMiniAppReadyOnce } from "@/lib/telegram-ready";
import { ErrorState } from "@/components/states/ErrorState";
import { AccessDenied } from "@/components/states/AccessDenied";
@@ -60,6 +59,7 @@ export function CalendarPage({ isAllowed, initDataRaw }: CalendarPageProps) {
prevMonth,
setCurrentMonth,
setSelectedDay,
setAppContentReady,
} = useAppStore(
useShallow((s) => ({
currentMonth: s.currentMonth,
@@ -75,6 +75,7 @@ export function CalendarPage({ isAllowed, initDataRaw }: CalendarPageProps) {
prevMonth: s.prevMonth,
setCurrentMonth: s.setCurrentMonth,
setSelectedDay: s.setSelectedDay,
setAppContentReady: s.setAppContentReady,
}))
);
@@ -126,13 +127,13 @@ export function CalendarPage({ isAllowed, initDataRaw }: CalendarPageProps) {
}, [setSelectedDay]);
const readyCalledRef = useRef(false);
// Signal Telegram to hide loading when the first load finishes (loading goes true -> false).
// Mark content ready when first load finishes or access denied, so page can call ready() and show content.
useEffect(() => {
if (!loading && !readyCalledRef.current) {
if ((!loading || accessDenied) && !readyCalledRef.current) {
readyCalledRef.current = true;
callMiniAppReadyOnce();
setAppContentReady(true);
}
}, [loading]);
}, [loading, accessDenied, setAppContentReady]);
return (
<div className="mx-auto flex min-h-screen w-full max-w-[var(--max-width-app)] flex-col bg-background px-3 pb-6 pt-safe">

View File

@@ -20,7 +20,6 @@ import {
formatHHMM,
} from "@/lib/date-utils";
import { getRemainingTime, findCurrentDuty } from "@/lib/current-duty";
import { callMiniAppReadyOnce } from "@/lib/telegram-ready";
import { ContactLinks } from "@/components/contact/ContactLinks";
import { Button } from "@/components/ui/button";
import {
@@ -47,6 +46,7 @@ type ViewState = "loading" | "error" | "ready";
export function CurrentDutyView({ onBack, openedFromPin = false }: CurrentDutyViewProps) {
const { t } = useTranslation();
const lang = useAppStore((s) => s.lang);
const setAppContentReady = useAppStore((s) => s.setAppContentReady);
const { initDataRaw } = useTelegramAuth();
const [state, setState] = useState<ViewState>("loading");
@@ -93,12 +93,12 @@ export function CurrentDutyView({ onBack, openedFromPin = false }: CurrentDutyVi
return () => controller.abort();
}, [loadTodayDuties]);
// Signal Telegram to hide loading when this view is ready (or error).
// Mark content ready when data is loaded or error, so page can call ready() and show content.
useEffect(() => {
if (state !== "loading") {
callMiniAppReadyOnce();
setAppContentReady(true);
}
}, [state]);
}, [state, setAppContentReady]);
// Auto-update remaining time every second when there is an active duty.
useEffect(() => {