fix: harden dashboard notification focus rendering

This commit is contained in:
Daniel Volz
2026-05-10 15:03:23 +02:00
parent ba789f9794
commit 70f2392a71
2 changed files with 84 additions and 16 deletions
+37 -16
View File
@@ -46,6 +46,10 @@ function getMedicationIdFromNotificationDoseId(doseId: string | null): string |
}
function findFocusTargetElement(doseId: string | null, medId: string | null): HTMLElement | null {
if (typeof document === "undefined") {
return null;
}
if (doseId) {
const elements = Array.from(document.querySelectorAll<HTMLElement>("[data-dose-id]"));
const doseElement = elements.find((element) => element.dataset.doseId === doseId);
@@ -67,6 +71,8 @@ function getDosePeople(takenBy: unknown): Array<string | null> {
return takenByArray.length > 0 ? takenByArray : [null];
}
const EMPTY_DOSE_SET = new Set<string>();
export function DashboardPage() {
const { t, i18n } = useTranslation();
const { user } = useAuth();
@@ -115,6 +121,9 @@ export function DashboardPage() {
const [showObsoleteConfirm, setShowObsoleteConfirm] = useState(false);
const [obsoleteCandidate, setObsoleteCandidate] = useState<{ id: number; name: string } | null>(null);
const notificationFocusAppliedRef = useRef<string | null>(null);
const effectiveSkippedDoses =
skippedDoses instanceof Set ? skippedDoses : dismissedDoses instanceof Set ? dismissedDoses : EMPTY_DOSE_SET;
const canManageSkippedDoses = typeof markDoseSkipped === "function" && typeof undoDoseSkipped === "function";
const isDoseTakenForDisplay = useCallback((doseId: string) => takenDoses.has(doseId), [takenDoses]);
@@ -182,16 +191,20 @@ export function DashboardPage() {
return;
}
if (targetDayState.section === "past" && !showPastDays) {
setShowPastDays(true);
}
try {
if (targetDayState.section === "past" && !showPastDays) {
setShowPastDays(true);
}
if (targetDayState.section === "future" && !showFutureDays) {
setShowFutureDays(true);
}
if (targetDayState.section === "future" && !showFutureDays) {
setShowFutureDays(true);
}
if (targetDayState.isCollapsed) {
toggleDayCollapse(targetDayState.day.dateStr, targetDayState.isAutoCollapsed);
if (targetDayState.isCollapsed) {
toggleDayCollapse(targetDayState.day.dateStr, targetDayState.isAutoCollapsed);
}
} catch {
notificationFocusAppliedRef.current = null;
}
}, [
notificationTarget,
@@ -224,14 +237,18 @@ export function DashboardPage() {
let correctionTimerId: number | null = null;
const scrollTargetIntoView = () => {
const targetElement = findFocusTargetElement(notificationTarget.doseId, notificationTarget.medId);
try {
const targetElement = findFocusTargetElement(notificationTarget.doseId, notificationTarget.medId);
if (!targetElement) {
if (!targetElement || typeof targetElement.scrollIntoView !== "function") {
return false;
}
targetElement.scrollIntoView({ behavior: "smooth", block: "start" });
return true;
} catch {
return false;
}
targetElement.scrollIntoView({ behavior: "smooth", block: "start" });
return true;
};
const frameId = requestAnimationFrame(() => {
@@ -364,6 +381,10 @@ export function DashboardPage() {
</button>
);
if (!canManageSkippedDoses) {
return takeButton;
}
const skipButton = options.isSkipped ? (
<button className="dose-btn undo skip" onClick={() => undoDoseSkipped(options.doseId)} title={t("common.undo")}>
<span className="dose-btn-label">{t("common.undo")}</span>
@@ -1071,7 +1092,7 @@ export function DashboardPage() {
{people.map((person) => {
const doseId = getDoseId(dose.id, person);
const isTaken = isDoseTakenForDisplay(doseId);
const isSkipped = skippedDoses.has(doseId);
const isSkipped = effectiveSkippedDoses.has(doseId);
const isAutomaticallyTaken =
isTaken && isDoseTakenAutomatically(doseId) && dose.when <= Date.now();
const personClasses = ["dose-person"];
@@ -1395,7 +1416,7 @@ export function DashboardPage() {
{people.map((person) => {
const doseId = getDoseId(dose.id, person);
const isTaken = isDoseTakenForDisplay(doseId);
const isSkipped = skippedDoses.has(doseId);
const isSkipped = effectiveSkippedDoses.has(doseId);
const isAutomaticallyTaken =
isTaken && isDoseTakenAutomatically(doseId) && dose.when <= Date.now();
const personClasses = ["dose-person"];
@@ -1659,7 +1680,7 @@ export function DashboardPage() {
{people.map((person) => {
const doseId = getDoseId(dose.id, person);
const isTaken = isDoseTakenForDisplay(doseId);
const isSkipped = skippedDoses.has(doseId);
const isSkipped = effectiveSkippedDoses.has(doseId);
const isAutomaticallyTaken =
isTaken && isDoseTakenAutomatically(doseId) && dose.when <= Date.now();
const personClasses = ["dose-person"];