Various fixes for Web App bots (#2102)
This commit is contained in:
parent
d7ebc691e2
commit
923c946167
@ -179,7 +179,7 @@ export async function requestWebView({
|
|||||||
startParam,
|
startParam,
|
||||||
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
||||||
fromBotMenu: isFromBotMenu || undefined,
|
fromBotMenu: isFromBotMenu || undefined,
|
||||||
platform: 'web',
|
platform: 'webz',
|
||||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ export async function requestSimpleWebView({
|
|||||||
url,
|
url,
|
||||||
bot: buildInputPeer(bot.id, bot.accessHash),
|
bot: buildInputPeer(bot.id, bot.accessHash),
|
||||||
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
||||||
platform: 'web',
|
platform: 'webz',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return result?.url;
|
return result?.url;
|
||||||
|
|||||||
@ -103,6 +103,9 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
const prevPopupParams = usePrevious(popupParams);
|
const prevPopupParams = usePrevious(popupParams);
|
||||||
const renderingPopupParams = popupParams || prevPopupParams;
|
const renderingPopupParams = popupParams || prevPopupParams;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const frameRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const {
|
const {
|
||||||
url, buttonText, queryId,
|
url, buttonText, queryId,
|
||||||
@ -191,8 +194,8 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ref, reloadFrame, sendEvent, sendViewport, sendTheme,
|
reloadFrame, sendEvent, sendViewport, sendTheme,
|
||||||
} = useWebAppFrame(isOpen, isSimple, handleEvent);
|
} = useWebAppFrame(frameRef, isOpen, isSimple, handleEvent);
|
||||||
|
|
||||||
const shouldShowMainButton = mainButton?.isVisible && mainButton.text.trim().length > 0;
|
const shouldShowMainButton = mainButton?.isVisible && mainButton.text.trim().length > 0;
|
||||||
|
|
||||||
@ -238,6 +241,10 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
}, [sendEvent]);
|
}, [sendEvent]);
|
||||||
|
|
||||||
|
const handlePopupModalClose = useCallback(() => {
|
||||||
|
handlePopupClose();
|
||||||
|
}, [handlePopupClose]);
|
||||||
|
|
||||||
// Notify view that height changed
|
// Notify view that height changed
|
||||||
useOnChange(() => {
|
useOnChange(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -427,13 +434,13 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
{isOpen && (
|
{isOpen && (
|
||||||
<>
|
<>
|
||||||
<iframe
|
<iframe
|
||||||
ref={ref}
|
|
||||||
className={buildClassName('web-app-frame', shouldDecreaseWebFrameSize && 'with-button')}
|
className={buildClassName('web-app-frame', shouldDecreaseWebFrameSize && 'with-button')}
|
||||||
src={url}
|
src={url}
|
||||||
title={`${bot?.firstName} Web App`}
|
title={`${bot?.firstName} Web App`}
|
||||||
sandbox={SANDBOX_ATTRIBUTES}
|
sandbox={SANDBOX_ATTRIBUTES}
|
||||||
allow="camera; microphone; geolocation;"
|
allow="camera; microphone; geolocation;"
|
||||||
allowFullScreen
|
allowFullScreen
|
||||||
|
ref={frameRef}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
@ -465,7 +472,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={Boolean(popupParams)}
|
isOpen={Boolean(popupParams)}
|
||||||
title={renderingPopupParams.title || NBSP}
|
title={renderingPopupParams.title || NBSP}
|
||||||
onClose={handlePopupClose}
|
onClose={handlePopupModalClose}
|
||||||
hasCloseButton
|
hasCloseButton
|
||||||
className={buildClassName('web-app-popup', !renderingPopupParams.title?.trim().length && 'without-title')}
|
className={buildClassName('web-app-popup', !renderingPopupParams.title?.trim().length && 'without-title')}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import useWindowSize from '../../../hooks/useWindowSize';
|
|
||||||
import { useCallback, useEffect, useRef } from '../../../lib/teact/teact';
|
import { useCallback, useEffect, useRef } from '../../../lib/teact/teact';
|
||||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||||
|
import useWindowSize from '../../../hooks/useWindowSize';
|
||||||
|
|
||||||
export type PopupOptions = {
|
export type PopupOptions = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -84,6 +84,7 @@ type WebAppOutboundEvent = {
|
|||||||
height: number;
|
height: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
is_expanded?: boolean;
|
is_expanded?: boolean;
|
||||||
|
is_state_stable?: boolean;
|
||||||
};
|
};
|
||||||
} | {
|
} | {
|
||||||
eventType: 'theme_changed';
|
eventType: 'theme_changed';
|
||||||
@ -141,10 +142,14 @@ const SCROLLBAR_STYLE = `* {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event: WebAppInboundEvent) => void) => {
|
const useWebAppFrame = (
|
||||||
// eslint-disable-next-line no-null/no-null
|
ref: React.RefObject<HTMLIFrameElement>,
|
||||||
const ref = useRef<HTMLIFrameElement>(null);
|
isOpen: boolean,
|
||||||
|
isSimpleView: boolean,
|
||||||
|
onEvent: (event: WebAppInboundEvent) => void,
|
||||||
|
) => {
|
||||||
const ignoreEventsRef = useRef<boolean>(false);
|
const ignoreEventsRef = useRef<boolean>(false);
|
||||||
|
const lastFrameSizeRef = useRef<{ width: number; height: number; isResizing?: boolean }>();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
const reloadFrame = useCallback((url: string) => {
|
const reloadFrame = useCallback((url: string) => {
|
||||||
@ -154,14 +159,14 @@ const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event:
|
|||||||
frame.addEventListener('load', () => {
|
frame.addEventListener('load', () => {
|
||||||
frame.src = url;
|
frame.src = url;
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
}, []);
|
}, [ref]);
|
||||||
|
|
||||||
const sendEvent = useCallback((event: WebAppOutboundEvent) => {
|
const sendEvent = useCallback((event: WebAppOutboundEvent) => {
|
||||||
if (!ref.current?.contentWindow) return;
|
if (!ref.current?.contentWindow) return;
|
||||||
ref.current.contentWindow.postMessage(JSON.stringify(event), '*');
|
ref.current.contentWindow.postMessage(JSON.stringify(event), '*');
|
||||||
}, []);
|
}, [ref]);
|
||||||
|
|
||||||
const sendViewport = useCallback(() => {
|
const sendViewport = useCallback((isNonStable?: boolean) => {
|
||||||
if (!ref.current) {
|
if (!ref.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -172,9 +177,10 @@ const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event:
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
is_expanded: true,
|
is_expanded: true,
|
||||||
|
is_state_stable: !isNonStable,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [sendEvent]);
|
}, [sendEvent, ref]);
|
||||||
|
|
||||||
const sendTheme = useCallback(() => {
|
const sendTheme = useCallback(() => {
|
||||||
sendEvent({
|
sendEvent({
|
||||||
@ -201,7 +207,7 @@ const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event:
|
|||||||
const data = JSON.parse(event.data) as WebAppInboundEvent;
|
const data = JSON.parse(event.data) as WebAppInboundEvent;
|
||||||
// Handle some app requests here to simplify hook usage
|
// Handle some app requests here to simplify hook usage
|
||||||
if (data.eventType === 'web_app_request_viewport') {
|
if (data.eventType === 'web_app_request_viewport') {
|
||||||
sendViewport();
|
sendViewport(windowSize.isResizing);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.eventType === 'web_app_request_theme') {
|
if (data.eventType === 'web_app_request_theme') {
|
||||||
@ -221,12 +227,14 @@ const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event:
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore other messages
|
// Ignore other messages
|
||||||
}
|
}
|
||||||
}, [isSimpleView, onEvent, sendCustomStyle, sendTheme, sendViewport]);
|
}, [isSimpleView, onEvent, sendCustomStyle, sendTheme, sendViewport, windowSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (windowSize) {
|
const { width, height, isResizing } = windowSize;
|
||||||
sendViewport();
|
if (lastFrameSizeRef.current && lastFrameSizeRef.current.width === width
|
||||||
}
|
&& lastFrameSizeRef.current.height === height && !lastFrameSizeRef.current.isResizing) return;
|
||||||
|
lastFrameSizeRef.current = { width, height, isResizing };
|
||||||
|
sendViewport(isResizing);
|
||||||
}, [sendViewport, windowSize]);
|
}, [sendViewport, windowSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -238,11 +246,13 @@ const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event:
|
|||||||
if (isOpen && ref.current?.contentWindow) {
|
if (isOpen && ref.current?.contentWindow) {
|
||||||
sendViewport();
|
sendViewport();
|
||||||
ignoreEventsRef.current = false;
|
ignoreEventsRef.current = false;
|
||||||
|
} else {
|
||||||
|
lastFrameSizeRef.current = undefined;
|
||||||
}
|
}
|
||||||
}, [isOpen, sendViewport]);
|
}, [isOpen, sendViewport, ref]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref, sendEvent, reloadFrame, sendViewport, sendTheme,
|
sendEvent, reloadFrame, sendViewport, sendTheme,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from '../lib/teact/teact';
|
import { useEffect, useMemo, useState } from '../lib/teact/teact';
|
||||||
import type { ApiDimensions } from '../api/types';
|
import type { ApiDimensions } from '../api/types';
|
||||||
|
|
||||||
import { throttle } from '../util/schedulers';
|
import { throttle } from '../util/schedulers';
|
||||||
@ -12,6 +12,8 @@ const useWindowSize = () => {
|
|||||||
const [isResizing, setIsResizing] = useState(false);
|
const [isResizing, setIsResizing] = useState(false);
|
||||||
const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [], THROTTLE, true);
|
const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [], THROTTLE, true);
|
||||||
|
|
||||||
|
const result = useMemo(() => ({ ...size, isResizing }), [isResizing, size]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const throttledSetIsResizing = throttle(() => {
|
const throttledSetIsResizing = throttle(() => {
|
||||||
setIsResizing(true);
|
setIsResizing(true);
|
||||||
@ -34,7 +36,7 @@ const useWindowSize = () => {
|
|||||||
};
|
};
|
||||||
}, [setIsResizingDebounced]);
|
}, [setIsResizingDebounced]);
|
||||||
|
|
||||||
return { ...size, isResizing };
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useWindowSize;
|
export default useWindowSize;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user