Mini Apps: Support secondary button (#5074)
This commit is contained in:
parent
e2a717dc29
commit
0eb0784165
@ -44,19 +44,19 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.secondary-button,
|
||||||
.main-button {
|
.main-button {
|
||||||
position: absolute;
|
flex-grow: 1;
|
||||||
bottom: 0;
|
margin: 0.5rem;
|
||||||
border-radius: 0;
|
|
||||||
|
|
||||||
z-index: 1;
|
|
||||||
transform: translateY(100%);
|
transform: translateY(100%);
|
||||||
transition-property: background-color, color, transform;
|
opacity: 0;
|
||||||
|
transition-property: background-color, color, transform, margin-inline, flex-grow, opacity;
|
||||||
transition-duration: 0.25s;
|
transition-duration: 0.25s;
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
|
|
||||||
&.visible {
|
&.visible {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
@ -64,9 +64,84 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttons-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||||
|
transition-property: height;
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
|
||||||
|
height: 0rem;
|
||||||
|
|
||||||
|
&.one-row {
|
||||||
|
align-items: center;
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.two-rows {
|
||||||
|
height: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left-to-right {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right-to-left {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top-to-bottom,
|
||||||
|
&.bottom-to-top {
|
||||||
|
.secondary-button,
|
||||||
|
.main-button {
|
||||||
|
position: absolute;
|
||||||
|
left: 0rem;
|
||||||
|
right: 0rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top-to-bottom {
|
||||||
|
.secondary-button {
|
||||||
|
top: 0rem;
|
||||||
|
}
|
||||||
|
.main-button {
|
||||||
|
bottom: 0rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.bottom-to-top {
|
||||||
|
.main-button {
|
||||||
|
top: 0rem;
|
||||||
|
}
|
||||||
|
.secondary-button {
|
||||||
|
bottom: 0rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-horizontal {
|
||||||
|
.secondary-button,
|
||||||
|
.main-button {
|
||||||
|
transform: translateY(0);
|
||||||
|
flex-grow: 0;
|
||||||
|
margin-inline: 0;
|
||||||
|
padding-inline: 0;
|
||||||
|
width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
margin-inline: 0.5rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-button-spinner,
|
||||||
.main-button-spinner {
|
.main-button-spinner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.web-app-popup {
|
.web-app-popup {
|
||||||
|
|||||||
@ -23,10 +23,10 @@ import { callApi } from '../../../api/gramjs';
|
|||||||
import { REM } from '../../common/helpers/mediaDimensions';
|
import { REM } from '../../common/helpers/mediaDimensions';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
|
||||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
import usePopupLimit from './hooks/usePopupLimit';
|
import usePopupLimit from './hooks/usePopupLimit';
|
||||||
import useWebAppFrame from './hooks/useWebAppFrame';
|
import useWebAppFrame from './hooks/useWebAppFrame';
|
||||||
@ -45,6 +45,7 @@ type WebAppButton = {
|
|||||||
color: string;
|
color: string;
|
||||||
textColor: string;
|
textColor: string;
|
||||||
isProgressVisible: boolean;
|
isProgressVisible: boolean;
|
||||||
|
position?: 'left' | 'right' | 'top' | 'bottom';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
@ -113,12 +114,14 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
updateWebApp,
|
updateWebApp,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const [mainButton, setMainButton] = useState<WebAppButton | undefined>();
|
const [mainButton, setMainButton] = useState<WebAppButton | undefined>();
|
||||||
|
const [secondaryButton, setSecondaryButton] = useState<WebAppButton | undefined>();
|
||||||
|
|
||||||
const [isLoaded, markLoaded, markUnloaded] = useFlag(false);
|
const [isLoaded, markLoaded, markUnloaded] = useFlag(false);
|
||||||
|
|
||||||
const [popupParameters, setPopupParameters] = useState<PopupOptions | undefined>();
|
const [popupParameters, setPopupParameters] = useState<PopupOptions | undefined>();
|
||||||
const [isRequestingPhone, setIsRequestingPhone] = useState(false);
|
const [isRequestingPhone, setIsRequestingPhone] = useState(false);
|
||||||
const [isRequestingWriteAccess, setIsRequestingWriteAccess] = useState(false);
|
const [isRequestingWriteAccess, setIsRequestingWriteAccess] = useState(false);
|
||||||
|
const [bottomBarColor, setBottomBarColor] = useState<string | undefined>();
|
||||||
const {
|
const {
|
||||||
unlockPopupsAt, handlePopupOpened, handlePopupClosed,
|
unlockPopupsAt, handlePopupOpened, handlePopupClosed,
|
||||||
} = usePopupLimit(POPUP_SEQUENTIAL_LIMIT, POPUP_RESET_DELAY);
|
} = usePopupLimit(POPUP_SEQUENTIAL_LIMIT, POPUP_RESET_DELAY);
|
||||||
@ -147,6 +150,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const themeParams = extractCurrentThemeParams();
|
const themeParams = extractCurrentThemeParams();
|
||||||
|
setBottomBarColor(themeParams.secondary_bg_color);
|
||||||
updateCurrentWebApp({ headerColor: themeParams.bg_color, backgroundColor: themeParams.bg_color });
|
updateCurrentWebApp({ headerColor: themeParams.bg_color, backgroundColor: themeParams.bg_color });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -169,7 +173,8 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
if (isActive) registerReloadFrameCallback(reloadFrame);
|
if (isActive) registerReloadFrameCallback(reloadFrame);
|
||||||
}, [reloadFrame, registerReloadFrameCallback, isActive]);
|
}, [reloadFrame, registerReloadFrameCallback, isActive]);
|
||||||
|
|
||||||
const shouldShowMainButton = mainButton?.isVisible && mainButton.text.trim().length > 0;
|
const isMainButtonVisible = mainButton?.isVisible && mainButton.text.trim().length > 0;
|
||||||
|
const isSecondaryButtonVisible = secondaryButton?.isVisible && secondaryButton.text.trim().length > 0;
|
||||||
|
|
||||||
const handleHideCloseModal = useLastCallback(() => {
|
const handleHideCloseModal = useLastCallback(() => {
|
||||||
updateCurrentWebApp({ isCloseModalOpen: false });
|
updateCurrentWebApp({ isCloseModalOpen: false });
|
||||||
@ -191,6 +196,12 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleSecondaryButtonClick = useLastCallback(() => {
|
||||||
|
sendEvent({
|
||||||
|
eventType: 'secondary_button_pressed',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const handleAppPopupClose = useLastCallback((buttonId?: string) => {
|
const handleAppPopupClose = useLastCallback((buttonId?: string) => {
|
||||||
setPopupParameters(undefined);
|
setPopupParameters(undefined);
|
||||||
handlePopupClosed();
|
handlePopupClosed();
|
||||||
@ -241,13 +252,6 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
}, ANIMATION_WAIT);
|
}, ANIMATION_WAIT);
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
// Notify view that height changed
|
|
||||||
useSyncEffect(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
sendViewport();
|
|
||||||
}, ANIMATION_WAIT);
|
|
||||||
}, [mainButton?.isVisible, sendViewport]);
|
|
||||||
|
|
||||||
useSyncEffect(([prevIsPaymentModalOpen]) => {
|
useSyncEffect(([prevIsPaymentModalOpen]) => {
|
||||||
if (isPaymentModalOpen === prevIsPaymentModalOpen) return;
|
if (isPaymentModalOpen === prevIsPaymentModalOpen) return;
|
||||||
if (webApp?.slug && !isPaymentModalOpen && paymentStatus) {
|
if (webApp?.slug && !isPaymentModalOpen && paymentStatus) {
|
||||||
@ -362,6 +366,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
setIsRequestingPhone(false);
|
setIsRequestingPhone(false);
|
||||||
setIsRequestingWriteAccess(false);
|
setIsRequestingWriteAccess(false);
|
||||||
setMainButton(undefined);
|
setMainButton(undefined);
|
||||||
|
setSecondaryButton(undefined);
|
||||||
updateCurrentWebApp({
|
updateCurrentWebApp({
|
||||||
isSettingsButtonVisible: false,
|
isSettingsButtonVisible: false,
|
||||||
shouldConfirmClosing: false,
|
shouldConfirmClosing: false,
|
||||||
@ -399,6 +404,10 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
calculateHeaderColor(eventData.color_key, eventData.color);
|
calculateHeaderColor(eventData.color_key, eventData.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventType === 'web_app_set_bottom_bar_color') {
|
||||||
|
setBottomBarColor(eventData.color);
|
||||||
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_data_send') {
|
if (eventType === 'web_app_data_send') {
|
||||||
closeActiveWebApp();
|
closeActiveWebApp();
|
||||||
sendWebViewData({
|
sendWebViewData({
|
||||||
@ -409,19 +418,32 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_setup_main_button') {
|
if (eventType === 'web_app_setup_main_button') {
|
||||||
const themeParams = extractCurrentThemeParams();
|
const color = eventData.color;
|
||||||
const color = validateHexColor(eventData.color) ? eventData.color : themeParams.button_color;
|
const textColor = eventData.text_color;
|
||||||
const textColor = validateHexColor(eventData.text_color) ? eventData.text_color : themeParams.text_color;
|
|
||||||
setMainButton({
|
setMainButton({
|
||||||
isVisible: eventData.is_visible && Boolean(eventData.text?.trim().length),
|
isVisible: eventData.is_visible && Boolean(eventData.text?.trim().length),
|
||||||
isActive: eventData.is_active,
|
isActive: eventData.is_active,
|
||||||
text: eventData.text || '',
|
text: eventData.text,
|
||||||
color,
|
color,
|
||||||
textColor,
|
textColor,
|
||||||
isProgressVisible: eventData.is_progress_visible,
|
isProgressVisible: eventData.is_progress_visible,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventType === 'web_app_setup_secondary_button') {
|
||||||
|
const color = eventData.color;
|
||||||
|
const textColor = eventData.text_color;
|
||||||
|
setSecondaryButton({
|
||||||
|
isVisible: eventData.is_visible && Boolean(eventData.text?.trim().length),
|
||||||
|
isActive: eventData.is_active,
|
||||||
|
text: eventData.text,
|
||||||
|
color,
|
||||||
|
textColor,
|
||||||
|
isProgressVisible: eventData.is_progress_visible,
|
||||||
|
position: eventData.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_setup_closing_behavior') {
|
if (eventType === 'web_app_setup_closing_behavior') {
|
||||||
updateCurrentWebApp({ shouldConfirmClosing: true });
|
updateCurrentWebApp({ shouldConfirmClosing: true });
|
||||||
}
|
}
|
||||||
@ -478,39 +500,99 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevMainButtonColor = usePreviousDeprecated(mainButton?.color, true);
|
const mainButtonCurrentColor = useCurrentOrPrev(mainButton?.color, true);
|
||||||
const prevMainButtonTextColor = usePreviousDeprecated(mainButton?.textColor, true);
|
const mainButtonCurrentTextColor = useCurrentOrPrev(mainButton?.textColor, true);
|
||||||
const prevMainButtonIsActive = usePreviousDeprecated(mainButton && Boolean(mainButton.isActive), true);
|
const mainButtonCurrentIsActive = useCurrentOrPrev(mainButton && Boolean(mainButton.isActive), true);
|
||||||
const prevMainButtonText = usePreviousDeprecated(mainButton?.text, true);
|
const mainButtonCurrentText = useCurrentOrPrev(mainButton?.text, true);
|
||||||
|
|
||||||
const mainButtonCurrentColor = mainButton?.color || prevMainButtonColor;
|
const secondaryButtonCurrentPosition = useCurrentOrPrev(secondaryButton?.position, true);
|
||||||
const mainButtonCurrentTextColor = mainButton?.textColor || prevMainButtonTextColor;
|
const secondaryButtonCurrentColor = useCurrentOrPrev(secondaryButton?.color, true);
|
||||||
const mainButtonCurrentIsActive = mainButton?.isActive !== undefined ? mainButton.isActive : prevMainButtonIsActive;
|
const secondaryButtonCurrentTextColor = useCurrentOrPrev(secondaryButton?.textColor, true);
|
||||||
const mainButtonCurrentText = mainButton?.text || prevMainButtonText;
|
const secondaryButtonCurrentIsActive = useCurrentOrPrev(secondaryButton && Boolean(secondaryButton.isActive), true);
|
||||||
|
const secondaryButtonCurrentText = useCurrentOrPrev(secondaryButton?.text, true);
|
||||||
|
|
||||||
const [shouldDecreaseWebFrameSize, setShouldDecreaseWebFrameSize] = useState(false);
|
const [shouldDecreaseWebFrameSize, setShouldDecreaseWebFrameSize] = useState(false);
|
||||||
const [shouldHideButton, setShouldHideButton] = useState(true);
|
const [shouldHideMainButton, setShouldHideMainButton] = useState(true);
|
||||||
|
const [shouldHideSecondaryButton, setShouldHideSecondaryButton] = useState(true);
|
||||||
|
const [shouldShowMainButton, setShouldShowMainButton] = useState(false);
|
||||||
|
const [shouldShowSecondaryButton, setShouldShowSecondaryButton] = useState(false);
|
||||||
|
|
||||||
const buttonChangeTimeout = useRef<ReturnType<typeof setTimeout>>();
|
// Notify view that height changed
|
||||||
|
useSyncEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
sendViewport();
|
||||||
|
}, ANIMATION_WAIT);
|
||||||
|
}, [shouldShowSecondaryButton, shouldHideSecondaryButton,
|
||||||
|
shouldShowMainButton, shouldShowMainButton,
|
||||||
|
secondaryButton?.position, sendViewport]);
|
||||||
|
|
||||||
|
const isVerticalLayout = secondaryButtonCurrentPosition === 'top' || secondaryButtonCurrentPosition === 'bottom';
|
||||||
|
const isHorizontalLayout = !isVerticalLayout;
|
||||||
|
|
||||||
|
const rowsCount = (isVerticalLayout && shouldShowMainButton && shouldShowSecondaryButton) ? 2
|
||||||
|
: shouldShowMainButton || shouldShowSecondaryButton ? 1 : 0;
|
||||||
|
|
||||||
|
const hideDirection = (isHorizontalLayout
|
||||||
|
&& (!shouldHideMainButton && !shouldHideSecondaryButton)) ? 'horizontal' : 'vertical';
|
||||||
|
|
||||||
|
const mainButtonChangeTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
const mainButtonFastTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
const secondaryButtonChangeTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
const secondaryButtonFastTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (buttonChangeTimeout.current) clearTimeout(buttonChangeTimeout.current);
|
if (mainButtonChangeTimeout.current) clearTimeout(mainButtonChangeTimeout.current);
|
||||||
if (!shouldShowMainButton) {
|
if (mainButtonFastTimeout.current) clearTimeout(mainButtonFastTimeout.current);
|
||||||
setShouldDecreaseWebFrameSize(false);
|
|
||||||
buttonChangeTimeout.current = setTimeout(() => {
|
if (isMainButtonVisible) {
|
||||||
setShouldHideButton(true);
|
mainButtonFastTimeout.current = setTimeout(() => {
|
||||||
}, MAIN_BUTTON_ANIMATION_TIME);
|
setShouldShowMainButton(true);
|
||||||
} else {
|
}, 35);
|
||||||
setShouldHideButton(false);
|
setShouldHideMainButton(false);
|
||||||
buttonChangeTimeout.current = setTimeout(() => {
|
mainButtonChangeTimeout.current = setTimeout(() => {
|
||||||
setShouldDecreaseWebFrameSize(true);
|
setShouldDecreaseWebFrameSize(true);
|
||||||
}, MAIN_BUTTON_ANIMATION_TIME);
|
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||||
}
|
}
|
||||||
}, [setShouldDecreaseWebFrameSize, shouldShowMainButton]);
|
|
||||||
|
if (!isMainButtonVisible) {
|
||||||
|
setShouldShowMainButton(false);
|
||||||
|
mainButtonChangeTimeout.current = setTimeout(() => {
|
||||||
|
setShouldHideMainButton(true);
|
||||||
|
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||||
|
}
|
||||||
|
}, [isMainButtonVisible]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (secondaryButtonChangeTimeout.current) clearTimeout(secondaryButtonChangeTimeout.current);
|
||||||
|
if (secondaryButtonFastTimeout.current) clearTimeout(secondaryButtonFastTimeout.current);
|
||||||
|
|
||||||
|
if (isSecondaryButtonVisible) {
|
||||||
|
secondaryButtonFastTimeout.current = setTimeout(() => {
|
||||||
|
setShouldShowSecondaryButton(true);
|
||||||
|
}, 35);
|
||||||
|
setShouldHideSecondaryButton(false);
|
||||||
|
secondaryButtonChangeTimeout.current = setTimeout(() => {
|
||||||
|
setShouldDecreaseWebFrameSize(true);
|
||||||
|
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSecondaryButtonVisible) {
|
||||||
|
setShouldShowSecondaryButton(false);
|
||||||
|
secondaryButtonChangeTimeout.current = setTimeout(() => {
|
||||||
|
setShouldHideSecondaryButton(true);
|
||||||
|
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||||
|
}
|
||||||
|
}, [isSecondaryButtonVisible]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!shouldShowSecondaryButton && !shouldShowMainButton) {
|
||||||
|
setShouldDecreaseWebFrameSize(false);
|
||||||
|
}
|
||||||
|
}, [setShouldDecreaseWebFrameSize, shouldShowSecondaryButton, shouldShowMainButton]);
|
||||||
|
|
||||||
const frameWidth = frameSize?.width || 0;
|
const frameWidth = frameSize?.width || 0;
|
||||||
let frameHeight = frameSize?.height || 0;
|
let frameHeight = frameSize?.height || 0;
|
||||||
if (shouldDecreaseWebFrameSize) { frameHeight -= 3.5 * REM; }
|
if (shouldDecreaseWebFrameSize) { frameHeight -= 4 * REM; }
|
||||||
const frameStyle = buildStyle(
|
const frameStyle = buildStyle(
|
||||||
`left: ${0}px;`,
|
`left: ${0}px;`,
|
||||||
`top: ${0}px;`,
|
`top: ${0}px;`,
|
||||||
@ -543,19 +625,53 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
ref={frameRef}
|
ref={frameRef}
|
||||||
/>
|
/>
|
||||||
{isMaximizedState && (
|
{isMaximizedState && (
|
||||||
<Button
|
<div
|
||||||
|
style={`background-color: ${bottomBarColor};`}
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.mainButton,
|
styles.buttonsContainer,
|
||||||
shouldShowMainButton && styles.visible,
|
secondaryButtonCurrentPosition === 'left' && styles.leftToRight,
|
||||||
shouldHideButton && styles.hidden,
|
secondaryButtonCurrentPosition === 'right' && styles.rightToLeft,
|
||||||
|
secondaryButtonCurrentPosition === 'top' && styles.topToBottom,
|
||||||
|
secondaryButtonCurrentPosition === 'bottom' && styles.bottomToTop,
|
||||||
|
hideDirection === 'horizontal' && styles.hideHorizontal,
|
||||||
|
rowsCount === 1 && styles.oneRow,
|
||||||
|
rowsCount === 2 && styles.twoRows,
|
||||||
)}
|
)}
|
||||||
style={`background-color: ${mainButtonCurrentColor}; color: ${mainButtonCurrentTextColor}`}
|
|
||||||
disabled={!mainButtonCurrentIsActive}
|
|
||||||
onClick={handleMainButtonClick}
|
|
||||||
>
|
>
|
||||||
{mainButtonCurrentText}
|
<Button
|
||||||
{mainButton?.isProgressVisible && <Spinner className={styles.mainButtonSpinner} color="white" />}
|
className={buildClassName(
|
||||||
</Button>
|
styles.secondaryButton,
|
||||||
|
shouldShowSecondaryButton && !shouldHideSecondaryButton && styles.visible,
|
||||||
|
shouldHideSecondaryButton && styles.hidden,
|
||||||
|
)}
|
||||||
|
fluid
|
||||||
|
style={`background-color: ${secondaryButtonCurrentColor}; color: ${secondaryButtonCurrentTextColor}`}
|
||||||
|
disabled={!secondaryButtonCurrentIsActive && !secondaryButton?.isProgressVisible}
|
||||||
|
nonInteractive={secondaryButton?.isProgressVisible}
|
||||||
|
onClick={handleSecondaryButtonClick}
|
||||||
|
size="smaller"
|
||||||
|
>
|
||||||
|
{!secondaryButton?.isProgressVisible && secondaryButtonCurrentText}
|
||||||
|
{secondaryButton?.isProgressVisible
|
||||||
|
&& <Spinner className={styles.mainButtonSpinner} color="blue" />}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={buildClassName(
|
||||||
|
styles.mainButton,
|
||||||
|
shouldShowMainButton && !shouldHideMainButton && styles.visible,
|
||||||
|
shouldHideMainButton && styles.hidden,
|
||||||
|
)}
|
||||||
|
fluid
|
||||||
|
style={`background-color: ${mainButtonCurrentColor}; color: ${mainButtonCurrentTextColor}`}
|
||||||
|
disabled={!mainButtonCurrentIsActive && !mainButton?.isProgressVisible}
|
||||||
|
nonInteractive={mainButton?.isProgressVisible}
|
||||||
|
onClick={handleMainButtonClick}
|
||||||
|
size="smaller"
|
||||||
|
>
|
||||||
|
{!mainButton?.isProgressVisible && mainButtonCurrentText}
|
||||||
|
{mainButton?.isProgressVisible && <Spinner className={styles.mainButtonSpinner} color="white" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isRequestingPhone}
|
isOpen={isRequestingPhone}
|
||||||
|
|||||||
@ -16,6 +16,16 @@ type WebAppEvent<T, D> = D extends null ? {
|
|||||||
eventData: D;
|
eventData: D;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WebAppButtonOptions = {
|
||||||
|
is_visible: boolean;
|
||||||
|
is_active: boolean;
|
||||||
|
text: string;
|
||||||
|
color: string;
|
||||||
|
text_color: string;
|
||||||
|
is_progress_visible: boolean;
|
||||||
|
position?: 'left' | 'right' | 'top' | 'bottom';
|
||||||
|
};
|
||||||
|
|
||||||
export type WebAppInboundEvent =
|
export type WebAppInboundEvent =
|
||||||
WebAppEvent<'iframe_ready', {
|
WebAppEvent<'iframe_ready', {
|
||||||
reload_supported?: boolean;
|
reload_supported?: boolean;
|
||||||
@ -23,14 +33,8 @@ export type WebAppInboundEvent =
|
|||||||
WebAppEvent<'web_app_data_send', {
|
WebAppEvent<'web_app_data_send', {
|
||||||
data: string;
|
data: string;
|
||||||
}> |
|
}> |
|
||||||
WebAppEvent<'web_app_setup_main_button', {
|
WebAppEvent<'web_app_setup_main_button', WebAppButtonOptions> |
|
||||||
is_visible: boolean;
|
WebAppEvent<'web_app_setup_secondary_button', WebAppButtonOptions> |
|
||||||
is_active: boolean;
|
|
||||||
text: string;
|
|
||||||
color: string;
|
|
||||||
text_color: string;
|
|
||||||
is_progress_visible: boolean;
|
|
||||||
}> |
|
|
||||||
WebAppEvent<'web_app_setup_back_button', {
|
WebAppEvent<'web_app_setup_back_button', {
|
||||||
is_visible: boolean;
|
is_visible: boolean;
|
||||||
}> |
|
}> |
|
||||||
@ -52,6 +56,9 @@ export type WebAppInboundEvent =
|
|||||||
impact_style?: 'light' | 'medium' | 'heavy';
|
impact_style?: 'light' | 'medium' | 'heavy';
|
||||||
notification_type?: 'error' | 'success' | 'warning';
|
notification_type?: 'error' | 'success' | 'warning';
|
||||||
}> |
|
}> |
|
||||||
|
WebAppEvent<'web_app_set_bottom_bar_color', {
|
||||||
|
color: string;
|
||||||
|
}> |
|
||||||
WebAppEvent<'web_app_set_background_color', {
|
WebAppEvent<'web_app_set_background_color', {
|
||||||
color: string;
|
color: string;
|
||||||
}> |
|
}> |
|
||||||
@ -160,5 +167,6 @@ export type WebAppOutboundEvent =
|
|||||||
WebAppEvent<'biometry_token_updated', {
|
WebAppEvent<'biometry_token_updated', {
|
||||||
status: 'updated' | 'removed' | 'failed';
|
status: 'updated' | 'removed' | 'failed';
|
||||||
}> |
|
}> |
|
||||||
WebAppEvent<'main_button_pressed' | 'back_button_pressed' | 'settings_button_pressed' | 'scan_qr_popup_closed'
|
WebAppEvent<'main_button_pressed' |
|
||||||
|
'secondary_button_pressed' | 'back_button_pressed' | 'settings_button_pressed' | 'scan_qr_popup_closed'
|
||||||
| 'reload_iframe', null>;
|
| 'reload_iframe', null>;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user