Mini Apps: Support loading placeholder (#5285)
This commit is contained in:
parent
9290ea1d1c
commit
c0b89697cd
@ -5,6 +5,7 @@ import type {
|
|||||||
ApiAttachBotIcon,
|
ApiAttachBotIcon,
|
||||||
ApiAttachMenuPeerType,
|
ApiAttachMenuPeerType,
|
||||||
ApiBotApp,
|
ApiBotApp,
|
||||||
|
ApiBotAppSettings,
|
||||||
ApiBotCommand,
|
ApiBotCommand,
|
||||||
ApiBotInfo,
|
ApiBotInfo,
|
||||||
ApiBotInlineMediaResult,
|
ApiBotInlineMediaResult,
|
||||||
@ -16,11 +17,13 @@ import type {
|
|||||||
ApiMessagesBotApp,
|
ApiMessagesBotApp,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
import { numberToHexColor } from '../../../util/colors';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { addDocumentToLocalDb } from '../helpers';
|
import { addDocumentToLocalDb } from '../helpers';
|
||||||
import { buildApiPhoto, buildApiThumbnailFromStripped } from './common';
|
import { buildApiPhoto, buildApiThumbnailFromStripped } from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiDocument, buildApiWebDocument, buildVideoFromDocument } from './messageContent';
|
import { buildApiDocument, buildApiWebDocument, buildVideoFromDocument } from './messageContent';
|
||||||
|
import { buildSvgPath } from './pathBytesToSvg';
|
||||||
import { buildApiPeerId } from './peers';
|
import { buildApiPeerId } from './peers';
|
||||||
import { buildStickerFromDocument } from './symbols';
|
import { buildStickerFromDocument } from './symbols';
|
||||||
|
|
||||||
@ -111,7 +114,7 @@ function buildApiAttachMenuIcon(icon: GramJs.AttachMenuBotIcon): ApiAttachBotIco
|
|||||||
export function buildApiBotInfo(botInfo: GramJs.BotInfo, chatId: string): ApiBotInfo {
|
export function buildApiBotInfo(botInfo: GramJs.BotInfo, chatId: string): ApiBotInfo {
|
||||||
const {
|
const {
|
||||||
description, descriptionPhoto, descriptionDocument, userId, commands, menuButton, privacyPolicyUrl,
|
description, descriptionPhoto, descriptionDocument, userId, commands, menuButton, privacyPolicyUrl,
|
||||||
hasPreviewMedias,
|
hasPreviewMedias, appSettings,
|
||||||
} = botInfo;
|
} = botInfo;
|
||||||
|
|
||||||
const botId = userId && buildApiPeerId(userId, 'user');
|
const botId = userId && buildApiPeerId(userId, 'user');
|
||||||
@ -129,6 +132,18 @@ export function buildApiBotInfo(botInfo: GramJs.BotInfo, chatId: string): ApiBot
|
|||||||
privacyPolicyUrl,
|
privacyPolicyUrl,
|
||||||
commands: commandsArray?.length ? commandsArray : undefined,
|
commands: commandsArray?.length ? commandsArray : undefined,
|
||||||
hasPreviewMedia: hasPreviewMedias,
|
hasPreviewMedia: hasPreviewMedias,
|
||||||
|
appSettings: appSettings && buildBotAppSettings(appSettings),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildBotAppSettings(settings: GramJs.BotAppSettings): ApiBotAppSettings {
|
||||||
|
const placeholderPath = settings.placeholderPath && buildSvgPath(settings.placeholderPath);
|
||||||
|
return {
|
||||||
|
backgroundColor: settings.backgroundColor ? numberToHexColor(settings.backgroundColor) : undefined,
|
||||||
|
backgroundDarkColor: settings.backgroundDarkColor ? numberToHexColor(settings.backgroundDarkColor) : undefined,
|
||||||
|
headerColor: settings.headerColor ? numberToHexColor(settings.headerColor) : undefined,
|
||||||
|
headerDarkColor: settings.headerDarkColor ? numberToHexColor(settings.headerDarkColor) : undefined,
|
||||||
|
placeholderPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type {
|
|||||||
LangPackStringValue,
|
LangPackStringValue,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
import { numberToHexColor } from '../../../util/colors';
|
||||||
import {
|
import {
|
||||||
buildCollectionByCallback, omit, omitUndefined, pick,
|
buildCollectionByCallback, omit, omitUndefined, pick,
|
||||||
} from '../../../util/iteratees';
|
} from '../../../util/iteratees';
|
||||||
@ -297,7 +298,7 @@ export function buildApiLanguage(lang: GramJs.TypeLangPackLanguage): ApiLanguage
|
|||||||
|
|
||||||
function buildApiPeerColorSet(colorSet: GramJs.help.TypePeerColorSet) {
|
function buildApiPeerColorSet(colorSet: GramJs.help.TypePeerColorSet) {
|
||||||
if (colorSet instanceof GramJs.help.PeerColorSet) {
|
if (colorSet instanceof GramJs.help.PeerColorSet) {
|
||||||
return colorSet.colors.map((color) => `#${color.toString(16).padStart(6, '0')}`);
|
return colorSet.colors.map((color) => numberToHexColor(color));
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,12 @@ const LOOKUP = 'AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,
|
|||||||
|
|
||||||
export function pathBytesToSvg(bytes: Buffer, width: number, height: number) {
|
export function pathBytesToSvg(bytes: Buffer, width: number, height: number) {
|
||||||
return TEMPLATE
|
return TEMPLATE
|
||||||
.replace('{{path}}', buildPath(bytes))
|
.replace('{{path}}', buildSvgPath(bytes))
|
||||||
.replace('{{width}}', String(width))
|
.replace('{{width}}', String(width))
|
||||||
.replace('{{height}}', String(height));
|
.replace('{{height}}', String(height));
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPath(bytes: Buffer) {
|
export function buildSvgPath(bytes: Buffer) {
|
||||||
let path = 'M';
|
let path = 'M';
|
||||||
|
|
||||||
const len = bytes.length;
|
const len = bytes.length;
|
||||||
|
|||||||
@ -67,6 +67,14 @@ type ApiBotMenuButtonWebApp = {
|
|||||||
|
|
||||||
export type ApiBotMenuButton = ApiBotMenuButtonWebApp | ApiBotMenuButtonCommands;
|
export type ApiBotMenuButton = ApiBotMenuButtonWebApp | ApiBotMenuButtonCommands;
|
||||||
|
|
||||||
|
export interface ApiBotAppSettings {
|
||||||
|
placeholderPath?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
backgroundDarkColor?: string;
|
||||||
|
headerColor?: string;
|
||||||
|
headerDarkColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiBotInfo {
|
export interface ApiBotInfo {
|
||||||
botId: string;
|
botId: string;
|
||||||
commands?: ApiBotCommand[];
|
commands?: ApiBotCommand[];
|
||||||
@ -76,6 +84,7 @@ export interface ApiBotInfo {
|
|||||||
menuButton: ApiBotMenuButton;
|
menuButton: ApiBotMenuButton;
|
||||||
privacyPolicyUrl?: string;
|
privacyPolicyUrl?: string;
|
||||||
hasPreviewMedia?: true;
|
hasPreviewMedia?: true;
|
||||||
|
appSettings?: ApiBotAppSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiBotPreviewMedia extends MediaContainer {
|
export interface ApiBotPreviewMedia extends MediaContainer {
|
||||||
|
|||||||
@ -13,7 +13,8 @@
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-spinner {
|
.loadingPlaceholder {
|
||||||
|
width: 4rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@ -22,6 +23,40 @@
|
|||||||
transition: opacity 0.25s ease-in-out;
|
transition: opacity 0.25s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fadeOpacityBreath {
|
||||||
|
0% {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholderPath {
|
||||||
|
animation: 2s linear -0.8s infinite fadeOpacityBreath;
|
||||||
|
}
|
||||||
|
|
||||||
|
.defaultPlaceholderGrid {
|
||||||
|
width: 4.5rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
animation: 2s linear -0.8s infinite fadeOpacityBreath;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholderSquare {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background-color: black;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
.hide {
|
.hide {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
import React, {
|
import React, {
|
||||||
memo, useEffect,
|
memo, useEffect,
|
||||||
|
useMemo,
|
||||||
useRef, useState,
|
useRef, useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiAttachBot, ApiChat, ApiUser } from '../../../api/types';
|
import type {
|
||||||
|
ApiAttachBot, ApiBotAppSettings, ApiChat, ApiUser,
|
||||||
|
} from '../../../api/types';
|
||||||
import type { TabState, WebApp, WebAppModalStateType } from '../../../global/types';
|
import type { TabState, WebApp, WebAppModalStateType } from '../../../global/types';
|
||||||
import type { ThemeKey } from '../../../types';
|
import type { ThemeKey } from '../../../types';
|
||||||
import type { PopupOptions, WebAppInboundEvent, WebAppOutboundEvent } from '../../../types/webapp';
|
import type { PopupOptions, WebAppInboundEvent, WebAppOutboundEvent } from '../../../types/webapp';
|
||||||
@ -14,7 +17,7 @@ import { TME_LINK_PREFIX } from '../../../config';
|
|||||||
import { convertToApiChatType } from '../../../global/helpers';
|
import { convertToApiChatType } from '../../../global/helpers';
|
||||||
import { getWebAppKey } from '../../../global/helpers/bots';
|
import { getWebAppKey } from '../../../global/helpers/bots';
|
||||||
import {
|
import {
|
||||||
selectCurrentChat, selectTabState, selectTheme, selectUser,
|
selectCurrentChat, selectTabState, selectTheme, selectUser, selectUserFullInfo,
|
||||||
selectWebApp,
|
selectWebApp,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
@ -69,6 +72,7 @@ export type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
chat?: ApiChat;
|
chat?: ApiChat;
|
||||||
bot?: ApiUser;
|
bot?: ApiUser;
|
||||||
|
botAppSettings?: ApiBotAppSettings;
|
||||||
attachBot?: ApiAttachBot;
|
attachBot?: ApiAttachBot;
|
||||||
theme?: ThemeKey;
|
theme?: ThemeKey;
|
||||||
isPaymentModalOpen?: boolean;
|
isPaymentModalOpen?: boolean;
|
||||||
@ -113,6 +117,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
frameSize,
|
frameSize,
|
||||||
isMultiTabSupported,
|
isMultiTabSupported,
|
||||||
onContextMenuButtonClick,
|
onContextMenuButtonClick,
|
||||||
|
botAppSettings,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
closeActiveWebApp,
|
closeActiveWebApp,
|
||||||
@ -169,8 +174,13 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
const activeWebApp = modal?.activeWebAppKey ? modal.openedWebApps[modal.activeWebAppKey] : undefined;
|
const activeWebApp = modal?.activeWebAppKey ? modal.openedWebApps[modal.activeWebAppKey] : undefined;
|
||||||
const activeWebAppName = activeWebApp?.appName;
|
const activeWebAppName = activeWebApp?.appName;
|
||||||
const {
|
const {
|
||||||
url, buttonText, headerColor, serverHeaderColorKey, serverHeaderColor, isBackButtonVisible,
|
url, buttonText, isBackButtonVisible,
|
||||||
} = webApp || {};
|
} = webApp || {};
|
||||||
|
|
||||||
|
const {
|
||||||
|
placeholderPath,
|
||||||
|
} = botAppSettings || {};
|
||||||
|
|
||||||
const isCloseModalOpen = Boolean(webApp?.isCloseModalOpen);
|
const isCloseModalOpen = Boolean(webApp?.isCloseModalOpen);
|
||||||
const isRemoveModalOpen = Boolean(webApp?.isRemoveModalOpen);
|
const isRemoveModalOpen = Boolean(webApp?.isRemoveModalOpen);
|
||||||
|
|
||||||
@ -184,11 +194,36 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
updateWebApp({ key: webAppKey, update: updatedPartialWebApp });
|
updateWebApp({ key: webAppKey, update: updatedPartialWebApp });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const themeParams = useMemo(() => {
|
||||||
|
return extractCurrentThemeParams();
|
||||||
|
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const themeParams = extractCurrentThemeParams();
|
|
||||||
setBottomBarColor(themeParams.secondary_bg_color);
|
setBottomBarColor(themeParams.secondary_bg_color);
|
||||||
updateCurrentWebApp({ headerColor: themeParams.bg_color, backgroundColor: themeParams.bg_color });
|
}, [themeParams]);
|
||||||
}, []);
|
|
||||||
|
const themeBackgroundColor = themeParams.bg_color;
|
||||||
|
const [backgroundColorFromEvent, setBackgroundColorFromEvent] = useState<string | undefined>();
|
||||||
|
const backgroundColorFromSettings = theme === 'light' ? botAppSettings?.backgroundColor
|
||||||
|
: botAppSettings?.backgroundDarkColor;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const color = backgroundColorFromEvent || backgroundColorFromSettings || themeBackgroundColor;
|
||||||
|
|
||||||
|
updateCurrentWebApp({ backgroundColor: color });
|
||||||
|
}, [themeBackgroundColor, backgroundColorFromEvent, backgroundColorFromSettings]);
|
||||||
|
|
||||||
|
const themeHeaderColor = themeParams.bg_color;
|
||||||
|
const [headerColorFromEvent, setHeaderColorFromEvent] = useState<string | undefined>();
|
||||||
|
const headerColorFromSettings = theme === 'light' ? botAppSettings?.headerColor
|
||||||
|
: botAppSettings?.headerDarkColor;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const color = headerColorFromEvent || headerColorFromSettings || themeHeaderColor;
|
||||||
|
|
||||||
|
updateCurrentWebApp({ headerColor: color });
|
||||||
|
}, [themeHeaderColor, headerColorFromEvent, headerColorFromSettings]);
|
||||||
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const frameRef = useRef<HTMLIFrameElement>(null);
|
const frameRef = useRef<HTMLIFrameElement>(null);
|
||||||
@ -210,8 +245,8 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
if (isActive) registerReloadFrameCallback(reloadFrame);
|
if (isActive) registerReloadFrameCallback(reloadFrame);
|
||||||
}, [reloadFrame, registerReloadFrameCallback, isActive]);
|
}, [reloadFrame, registerReloadFrameCallback, isActive]);
|
||||||
|
|
||||||
const isMainButtonVisible = mainButton?.isVisible && mainButton.text.trim().length > 0;
|
const isMainButtonVisible = isLoaded && mainButton?.isVisible && mainButton.text.trim().length > 0;
|
||||||
const isSecondaryButtonVisible = secondaryButton?.isVisible && secondaryButton.text.trim().length > 0;
|
const isSecondaryButtonVisible = isLoaded && secondaryButton?.isVisible && secondaryButton.text.trim().length > 0;
|
||||||
|
|
||||||
const handleHideCloseModal = useLastCallback(() => {
|
const handleHideCloseModal = useLastCallback(() => {
|
||||||
updateCurrentWebApp({ isCloseModalOpen: false });
|
updateCurrentWebApp({ isCloseModalOpen: false });
|
||||||
@ -254,32 +289,8 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
handleAppPopupClose();
|
handleAppPopupClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
const calculateHeaderColor = useLastCallback(
|
|
||||||
(serverColorKey? : 'bg_color' | 'secondary_bg_color', serverColor? : string) => {
|
|
||||||
if (serverColorKey) {
|
|
||||||
const themeParams = extractCurrentThemeParams();
|
|
||||||
const key = serverColorKey;
|
|
||||||
const newColor = themeParams[key];
|
|
||||||
const color = validateHexColor(newColor) ? newColor : headerColor;
|
|
||||||
updateCurrentWebApp({ headerColor: color, serverHeaderColorKey: key });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverColor) {
|
|
||||||
const color = validateHexColor(serverColor) ? serverColor : headerColor;
|
|
||||||
updateCurrentWebApp({ headerColor: color, serverHeaderColor: serverColor });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateHeaderColor = useLastCallback(
|
|
||||||
() => {
|
|
||||||
calculateHeaderColor(serverHeaderColorKey, serverHeaderColor);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const sendThemeCallback = useLastCallback(() => {
|
const sendThemeCallback = useLastCallback(() => {
|
||||||
sendTheme();
|
sendTheme();
|
||||||
updateHeaderColor();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notify view that theme changed
|
// Notify view that theme changed
|
||||||
@ -526,13 +537,12 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_set_background_color') {
|
if (eventType === 'web_app_set_background_color') {
|
||||||
const themeParams = extractCurrentThemeParams();
|
setBackgroundColorFromEvent(validateHexColor(eventData.color) ? eventData.color : undefined);
|
||||||
const color = validateHexColor(eventData.color) ? eventData.color : themeParams.bg_color;
|
|
||||||
updateCurrentWebApp({ backgroundColor: color });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_set_header_color') {
|
if (eventType === 'web_app_set_header_color') {
|
||||||
calculateHeaderColor(eventData.color_key, eventData.color);
|
const key = eventData.color_key;
|
||||||
|
setHeaderColorFromEvent(eventData.color || (key ? themeParams[key] : undefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_set_bottom_bar_color') {
|
if (eventType === 'web_app_set_bottom_bar_color') {
|
||||||
@ -882,6 +892,32 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderDefaultPlaceholder() {
|
||||||
|
const className = buildClassName(styles.loadingPlaceholder, styles.defaultPlaceholderGrid, isLoaded && styles.hide);
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<div className={styles.placeholderSquare} />
|
||||||
|
<div className={styles.placeholderSquare} />
|
||||||
|
<div className={styles.placeholderSquare} />
|
||||||
|
<div className={styles.placeholderSquare} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPlaceholder() {
|
||||||
|
if (!placeholderPath) {
|
||||||
|
return renderDefaultPlaceholder();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={buildClassName(styles.loadingPlaceholder, isLoaded && styles.hide)}
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
>
|
||||||
|
<path className={styles.placeholderPath} d={placeholderPath} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
@ -892,7 +928,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isFullscreen && getIsWebAppsFullscreenSupported() && renderFullscreenHeaderPanel()}
|
{isFullscreen && getIsWebAppsFullscreenSupported() && renderFullscreenHeaderPanel()}
|
||||||
{!isMinimizedState && <Spinner className={buildClassName(styles.loadingSpinner, isLoaded && styles.hide)} />}
|
{!isMinimizedState && renderPlaceholder()}
|
||||||
<iframe
|
<iframe
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.frame,
|
styles.frame,
|
||||||
@ -1045,6 +1081,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
const attachBot = activeBotId ? global.attachMenu.bots[activeBotId] : undefined;
|
const attachBot = activeBotId ? global.attachMenu.bots[activeBotId] : undefined;
|
||||||
const bot = activeBotId ? selectUser(global, activeBotId) : undefined;
|
const bot = activeBotId ? selectUser(global, activeBotId) : undefined;
|
||||||
|
const userFullInfo = activeBotId ? selectUserFullInfo(global, activeBotId) : undefined;
|
||||||
|
const botAppSettings = userFullInfo?.botInfo?.appSettings;
|
||||||
const chat = selectCurrentChat(global);
|
const chat = selectCurrentChat(global);
|
||||||
const theme = selectTheme(global);
|
const theme = selectTheme(global);
|
||||||
const { isPaymentModalOpen, status: regularPaymentStatus } = selectTabState(global).payment;
|
const { isPaymentModalOpen, status: regularPaymentStatus } = selectTabState(global).payment;
|
||||||
@ -1060,6 +1098,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isPaymentModalOpen: isPaymentModalOpen || Boolean(starsInputInvoice),
|
isPaymentModalOpen: isPaymentModalOpen || Boolean(starsInputInvoice),
|
||||||
paymentStatus,
|
paymentStatus,
|
||||||
modalState,
|
modalState,
|
||||||
|
botAppSettings,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(WebAppModalTabContent));
|
)(WebAppModalTabContent));
|
||||||
|
|||||||
@ -1337,8 +1337,6 @@ export type WebApp = {
|
|||||||
isCloseModalOpen?: boolean;
|
isCloseModalOpen?: boolean;
|
||||||
shouldConfirmClosing?: boolean;
|
shouldConfirmClosing?: boolean;
|
||||||
headerColor?: string;
|
headerColor?: string;
|
||||||
serverHeaderColor?: string;
|
|
||||||
serverHeaderColorKey?: 'bg_color' | 'secondary_bg_color';
|
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
isBackButtonVisible?: boolean;
|
isBackButtonVisible?: boolean;
|
||||||
isSettingsButtonVisible?: boolean;
|
isSettingsButtonVisible?: boolean;
|
||||||
|
|||||||
@ -216,6 +216,10 @@ export const convertToRGBA = (color: number): string => {
|
|||||||
return `rgba(${red}, ${green}, ${blue}, ${alphaFloat})`;
|
return `rgba(${red}, ${green}, ${blue}, ${alphaFloat})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const numberToHexColor = (color: number): string => {
|
||||||
|
return `#${color.toString(16).padStart(6, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
export const getTextColor = (color: number): string => {
|
export const getTextColor = (color: number): string => {
|
||||||
const r = (color >> 16) & 0xff;
|
const r = (color >> 16) & 0xff;
|
||||||
const g = (color >> 8) & 0xff;
|
const g = (color >> 8) & 0xff;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user