fix: upgrade ALTCHA widget to v2.3.0 and rewrite component
- Upgrade from v0.5.0 to v2.3.0 (latest) - Use dangerouslySetInnerHTML for proper web component rendering - Add MutationObserver for reliable event binding - Enable debug mode on widget to diagnose issues - Simplify component API with onVerified/onError callbacks
This commit is contained in:
parent
96c0348d3a
commit
9f1dd857c4
|
|
@ -17,14 +17,14 @@ export default function LoginPage() {
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const handleAltchaStateChange = useCallback((state: any) => {
|
const handleAltchaVerified = useCallback((payload: string) => {
|
||||||
if (state.state === 'verified' && state.payload) {
|
setAltchaToken(payload);
|
||||||
setAltchaToken(state.payload);
|
|
||||||
setAltchaVerified(true);
|
setAltchaVerified(true);
|
||||||
} else {
|
}, []);
|
||||||
|
|
||||||
|
const handleAltchaError = useCallback(() => {
|
||||||
setAltchaToken('');
|
setAltchaToken('');
|
||||||
setAltchaVerified(false);
|
setAltchaVerified(false);
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const performLogin = useCallback(async () => {
|
const performLogin = useCallback(async () => {
|
||||||
|
|
@ -107,7 +107,8 @@ export default function LoginPage() {
|
||||||
<div>
|
<div>
|
||||||
<Altcha
|
<Altcha
|
||||||
challengeurl="https://api.sojorn.net/api/v1/admin/altcha-challenge"
|
challengeurl="https://api.sojorn.net/api/v1/admin/altcha-challenge"
|
||||||
onStateChange={handleAltchaStateChange}
|
onVerified={handleAltchaVerified}
|
||||||
|
onError={handleAltchaError}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,71 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useCallback } from 'react';
|
||||||
|
|
||||||
interface AltchaProps {
|
interface AltchaProps {
|
||||||
challengeurl: string;
|
challengeurl: string;
|
||||||
onStateChange?: (state: any) => void;
|
onVerified?: (payload: string) => void;
|
||||||
|
onError?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Altcha({ challengeurl, onStateChange }: AltchaProps) {
|
export default function Altcha({ challengeurl, onVerified, onError }: AltchaProps) {
|
||||||
const widgetRef = useRef<HTMLDivElement>(null);
|
const widgetRef = useRef<HTMLDivElement>(null);
|
||||||
const [loaded, setLoaded] = useState(false);
|
const scriptLoaded = useRef(false);
|
||||||
|
|
||||||
|
const handleStateChange = useCallback((e: Event) => {
|
||||||
|
const detail = (e as CustomEvent).detail;
|
||||||
|
if (detail?.state === 'verified' && detail?.payload) {
|
||||||
|
onVerified?.(detail.payload);
|
||||||
|
} else if (detail?.state === 'error') {
|
||||||
|
onError?.();
|
||||||
|
}
|
||||||
|
}, [onVerified, onError]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Load ALTCHA widget script
|
if (scriptLoaded.current) return;
|
||||||
|
scriptLoaded.current = true;
|
||||||
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://cdn.jsdelivr.net/npm/altcha@0.5.0/dist/altcha.min.js';
|
script.src = 'https://cdn.jsdelivr.net/npm/altcha@2.3.0/dist/altcha.min.js';
|
||||||
script.type = 'module';
|
script.type = 'module';
|
||||||
script.async = true;
|
script.async = true;
|
||||||
script.onload = () => setLoaded(true);
|
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (script.parentNode) {
|
|
||||||
script.parentNode.removeChild(script);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loaded || !widgetRef.current) return;
|
const container = widgetRef.current;
|
||||||
|
if (!container) return;
|
||||||
const widget = widgetRef.current.querySelector('altcha-widget');
|
|
||||||
if (!widget) return;
|
|
||||||
|
|
||||||
const handleStateChange = (event: any) => {
|
|
||||||
if (onStateChange) {
|
|
||||||
onStateChange(event.detail);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
const widget = container.querySelector('altcha-widget');
|
||||||
|
if (widget) {
|
||||||
widget.addEventListener('statechange', handleStateChange);
|
widget.addEventListener('statechange', handleStateChange);
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(container, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// Also try immediately in case widget already exists
|
||||||
|
const widget = container.querySelector('altcha-widget');
|
||||||
|
if (widget) {
|
||||||
|
widget.addEventListener('statechange', handleStateChange);
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
widget.removeEventListener('statechange', handleStateChange);
|
observer.disconnect();
|
||||||
|
const w = container.querySelector('altcha-widget');
|
||||||
|
if (w) {
|
||||||
|
w.removeEventListener('statechange', handleStateChange);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [loaded, onStateChange]);
|
}, [handleStateChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={widgetRef}>
|
<div ref={widgetRef} dangerouslySetInnerHTML={{
|
||||||
<altcha-widget
|
__html: `<altcha-widget challengeurl="${challengeurl}" debug></altcha-widget>`
|
||||||
challengeurl={challengeurl}
|
}} />
|
||||||
hidefooter="true"
|
|
||||||
hidelogo="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue