Added retry support to the HtmlIsland
This commit is contained in:
parent
0e20accee6
commit
7d0a7514ad
@ -2,6 +2,7 @@ import React, { useEffect, useState, useRef, useCallback } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Namespaces } from "../../i18n/i18n";
|
||||
import ErrorBlock from "./ErrorBlock";
|
||||
|
||||
export interface HtmlIslandProps {
|
||||
url: string;
|
||||
@ -20,29 +21,62 @@ const HtmlIsland: React.FC<HtmlIslandProps> = ({
|
||||
const { t: tIsland } = useTranslation(Namespaces.HtmlIsland);
|
||||
|
||||
//
|
||||
// Load the island HTML
|
||||
// Retry-capable fetch helper
|
||||
//
|
||||
const loadIsland = useCallback(() => {
|
||||
setError(null);
|
||||
|
||||
fetch(url, { credentials: "include" })
|
||||
.then((r) => {
|
||||
const fetchWithRetry = useCallback(
|
||||
async (
|
||||
attempt: number,
|
||||
maxAttempts: number,
|
||||
delayMs: number,
|
||||
): Promise<string> => {
|
||||
try {
|
||||
const r = await fetch(url, { credentials: "include" });
|
||||
if (!r.ok) throw new Error(`Failed to load island: ${r.status}`);
|
||||
return r.text();
|
||||
})
|
||||
.then((text) => setHtml(text))
|
||||
.catch(() => {
|
||||
setError(tIsland("island.loadError"));
|
||||
toast.error(tIsland("island.loadError"));
|
||||
});
|
||||
}, [url, tIsland]);
|
||||
return await r.text();
|
||||
} catch (err) {
|
||||
if (attempt >= maxAttempts) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const jitter = Math.random() * 150;
|
||||
const nextDelay = delayMs * Math.pow(2, attempt) + jitter;
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, nextDelay));
|
||||
|
||||
return fetchWithRetry(attempt + 1, maxAttempts, delayMs);
|
||||
}
|
||||
},
|
||||
[url],
|
||||
);
|
||||
|
||||
//
|
||||
// Initial load + reload when URL changes
|
||||
// Initial load + reload when URL changes (with retry + cancellation)
|
||||
//
|
||||
useEffect(() => {
|
||||
loadIsland();
|
||||
}, [loadIsland]);
|
||||
let cancelled = false;
|
||||
|
||||
const load = async () => {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const text = await fetchWithRetry(0, 4, 300);
|
||||
if (!cancelled) {
|
||||
setHtml(text);
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) {
|
||||
setError(tIsland("island.loadError"));
|
||||
toast.error(tIsland("island.loadError"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
load();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [url, tIsland, fetchWithRetry]);
|
||||
|
||||
//
|
||||
// After HTML is injected, run scripts + bind form handling
|
||||
@ -98,8 +132,7 @@ const HtmlIsland: React.FC<HtmlIslandProps> = ({
|
||||
form.getAttribute("data-full-page-redirect") === "true";
|
||||
|
||||
if (shouldFullPageRedirect) {
|
||||
// Do NOT intercept this form – let the browser handle POST + redirect
|
||||
return;
|
||||
return; // allow normal POST + redirect
|
||||
}
|
||||
|
||||
const submitHandler = async (e: Event) => {
|
||||
@ -131,7 +164,14 @@ const HtmlIsland: React.FC<HtmlIslandProps> = ({
|
||||
toast.success(
|
||||
successMessage ? successMessage : tIsland("island.saveSuccess"),
|
||||
);
|
||||
loadIsland();
|
||||
// reload island
|
||||
try {
|
||||
const text = await fetchWithRetry(0, 4, 300);
|
||||
setHtml(text);
|
||||
} catch {
|
||||
setError(tIsland("island.loadError"));
|
||||
toast.error(tIsland("island.loadError"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -150,28 +190,14 @@ const HtmlIsland: React.FC<HtmlIslandProps> = ({
|
||||
|
||||
form.addEventListener("submit", submitHandler);
|
||||
|
||||
// Cleanup on re-render
|
||||
return () => {
|
||||
form.removeEventListener("submit", submitHandler);
|
||||
};
|
||||
}, [html, islandId, successMessage, tIsland, loadIsland]);
|
||||
}, [html, islandId, successMessage, tIsland, fetchWithRetry]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
background: "#fee",
|
||||
border: "1px solid #c00",
|
||||
padding: "8px",
|
||||
marginBottom: "10px",
|
||||
borderRadius: "4px",
|
||||
color: "#900",
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<ErrorBlock error={error ?? undefined} />
|
||||
|
||||
<div ref={containerRef} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user