Mini Apps: Support reload events (#3953)

This commit is contained in:
Alexander Zinchuk 2023-12-04 14:38:49 +01:00
parent 65b7fd3e38
commit 5dc23f05fe
2 changed files with 39 additions and 9 deletions

View File

@ -5,6 +5,7 @@ import type { WebAppInboundEvent, WebAppOutboundEvent } from '../../../../types/
import { extractCurrentThemeParams } from '../../../../util/themeStyle';
import useLastCallback from '../../../../hooks/useLastCallback';
import useWindowSize from '../../../../hooks/useWindowSize';
const SCROLLBAR_STYLE = `* {
@ -27,6 +28,8 @@ const SCROLLBAR_STYLE = `* {
background-color: transparent;
}`;
const RELOAD_TIMEOUT = 500;
const useWebAppFrame = (
ref: React.RefObject<HTMLIFrameElement>,
isOpen: boolean,
@ -41,6 +44,8 @@ const useWebAppFrame = (
closeWebApp,
} = getActions();
const isReloadSupported = useRef<boolean>(false);
const reloadTimeout = useRef<ReturnType<typeof setTimeout>>();
const ignoreEventsRef = useRef<boolean>(false);
const lastFrameSizeRef = useRef<{ width: number; height: number; isResizing?: boolean }>();
const windowSize = useWindowSize();
@ -59,19 +64,33 @@ const useWebAppFrame = (
};
}, [onLoad, ref, isOpen]);
const reloadFrame = useCallback((url: string) => {
const sendEvent = useCallback((event: WebAppOutboundEvent) => {
if (!ref.current?.contentWindow) return;
ref.current.contentWindow.postMessage(JSON.stringify(event), '*');
}, [ref]);
const forceReloadFrame = useLastCallback((url: string) => {
if (!ref.current) return;
const frame = ref.current;
frame.src = 'about:blank';
frame.addEventListener('load', () => {
frame.src = url;
}, { once: true });
}, [ref]);
});
const sendEvent = useCallback((event: WebAppOutboundEvent) => {
if (!ref.current?.contentWindow) return;
ref.current.contentWindow.postMessage(JSON.stringify(event), '*');
}, [ref]);
const reloadFrame = useCallback((url: string) => {
if (isReloadSupported.current) {
sendEvent({
eventType: 'reload_iframe',
});
reloadTimeout.current = setTimeout(() => {
forceReloadFrame(url);
}, RELOAD_TIMEOUT);
return;
}
forceReloadFrame(url);
}, [sendEvent]);
const sendViewport = useCallback((isNonStable?: boolean) => {
if (!ref.current) {
@ -133,6 +152,11 @@ const useWebAppFrame = (
if (eventType === 'iframe_ready') {
const scrollbarColor = getComputedStyle(document.body).getPropertyValue('--color-scrollbar');
sendCustomStyle(SCROLLBAR_STYLE.replace(/%SCROLLBAR_COLOR%/g, scrollbarColor));
isReloadSupported.current = Boolean(eventData.reload_supported);
}
if (eventType === 'iframe_will_reload') {
clearTimeout(reloadTimeout.current);
}
if (eventType === 'web_app_data_send') {

View File

@ -9,6 +9,11 @@ export type PopupOptions = {
};
export type WebAppInboundEvent = {
eventType: 'iframe_ready';
eventData: {
reload_supported?: boolean;
};
} | {
eventType: 'web_app_data_send';
eventData: {
data: string;
@ -100,8 +105,8 @@ export type WebAppInboundEvent = {
};
} | {
eventType: 'web_app_request_viewport' | 'web_app_request_theme' | 'web_app_ready' | 'web_app_expand'
| 'web_app_request_phone' | 'web_app_close' | 'iframe_ready' | 'web_app_close_scan_qr_popup'
| 'web_app_request_write_access' | 'web_app_request_phone';
| 'web_app_request_phone' | 'web_app_close' | 'web_app_close_scan_qr_popup'
| 'web_app_request_write_access' | 'web_app_request_phone' | 'iframe_will_reload';
eventData: null;
};
@ -176,5 +181,6 @@ export type WebAppOutboundEvent = {
error: string;
});
} | {
eventType: 'main_button_pressed' | 'back_button_pressed' | 'settings_button_pressed' | 'scan_qr_popup_closed';
eventType: 'main_button_pressed' | 'back_button_pressed' | 'settings_button_pressed' | 'scan_qr_popup_closed'
| 'reload_iframe';
};