diff --git a/src/components/modals/locationAccess/LocationAccessModal.tsx b/src/components/modals/locationAccess/LocationAccessModal.tsx index 50a56b947..5ded4166f 100644 --- a/src/components/modals/locationAccess/LocationAccessModal.tsx +++ b/src/components/modals/locationAccess/LocationAccessModal.tsx @@ -36,7 +36,7 @@ const LocationAccessModal: FC = ({ currentUser, }) => { const { - closeLocationAccessModal, toggleUserLocationPermission, sendWebAppEvent, + closeLocationAccessModal, toggleUserLocationPermission, sendWebAppEvent, showNotification, } = getActions(); const isOpen = Boolean(modal); @@ -47,28 +47,48 @@ const LocationAccessModal: FC = ({ const containerRef = useRef(); const confirmHandler = useLastCallback(async () => { - const geolocationData = await getGeolocationStatus(); - const { geolocation } = geolocationData; if (!modal?.bot?.id) return; + + const geolocationData = await getGeolocationStatus(); + const { accessRequested, accessGranted, geolocation } = geolocationData; + const isAccessGranted = accessRequested && accessGranted && Boolean(geolocation); closeLocationAccessModal(); if (modal?.webAppKey) { toggleUserLocationPermission({ botId: modal.bot.id, - isAccessGranted: true, + isAccessGranted, }); + if (!isAccessGranted || !geolocation) { + sendWebAppEvent({ + webAppKey: modal.webAppKey, + event: { + eventType: 'location_requested', + eventData: { + available: false, + }, + }, + }); + showNotification({ message: oldLang('PermissionNoLocationPosition') }); + return; + } + sendWebAppEvent({ webAppKey: modal.webAppKey, event: { eventType: 'location_requested', eventData: { available: true, - latitude: geolocation?.latitude, - longitude: geolocation?.longitude, - altitude: geolocation?.altitude, - course: geolocation?.heading, - speed: geolocation?.speed, - horizontal_accuracy: geolocation?.accuracy, - vertical_accuracy: geolocation?.accuracy, + latitude: geolocation.latitude, + longitude: geolocation.longitude, + altitude: geolocation.altitude, + course: geolocation.heading, + speed: geolocation.speed, + horizontal_accuracy: geolocation.accuracy, + vertical_accuracy: geolocation.altitudeAccuracy, + // eslint-disable-next-line no-null/no-null + course_accuracy: null, + // eslint-disable-next-line no-null/no-null + speed_accuracy: null, }, }, }); diff --git a/src/components/modals/webApp/WebAppModalTabContent.tsx b/src/components/modals/webApp/WebAppModalTabContent.tsx index 62c03770b..86814aa8b 100644 --- a/src/components/modals/webApp/WebAppModalTabContent.tsx +++ b/src/components/modals/webApp/WebAppModalTabContent.tsx @@ -321,6 +321,35 @@ const WebAppModalTabContent: FC = ({ }); }); + const sendLocationUnavailable = useLastCallback(() => { + sendEvent({ + eventType: 'location_requested', + eventData: { + available: false, + }, + }); + }); + + const sendLocation = useLastCallback((geolocation: GeolocationCoordinates) => { + sendEvent({ + eventType: 'location_requested', + eventData: { + available: true, + latitude: geolocation.latitude, + longitude: geolocation.longitude, + altitude: geolocation.altitude, + course: geolocation.heading, + speed: geolocation.speed, + horizontal_accuracy: geolocation.accuracy, + vertical_accuracy: geolocation.altitudeAccuracy, + // eslint-disable-next-line no-null/no-null + course_accuracy: null, + // eslint-disable-next-line no-null/no-null + speed_accuracy: null, + }, + }); + }); + const handleAppPopupModalClose = useLastCallback(() => { handleAppPopupClose(); }); @@ -773,10 +802,20 @@ const WebAppModalTabContent: FC = ({ if (eventType === 'web_app_check_location') { const handleGeolocationCheck = () => { + if (!isAvailable) { + sendEvent({ + eventType: 'location_checked', + eventData: { + available: false, + }, + }); + return; + } + sendEvent({ eventType: 'location_checked', eventData: { - available: isAvailable, + available: true, access_requested: isAccessRequested, access_granted: isAccessGranted, }, @@ -788,43 +827,38 @@ const WebAppModalTabContent: FC = ({ if (eventType === 'web_app_request_location') { const handleRequestLocation = async () => { - const geolocationData = await getGeolocationStatus(); - const { accessRequested, accessGranted, geolocation } = geolocationData; - - if (!accessGranted || !accessRequested) { - sendEvent({ - eventType: 'location_requested', - eventData: { - available: false, - }, - }); + if (!isAvailable) { + sendLocationUnavailable(); showNotification({ message: oldLang('PermissionNoLocationPosition') }); handleAppPopupClose(undefined); return; } - if (isAvailable) { - if (isAccessRequested) { - sendEvent({ - eventType: 'location_requested', - eventData: { - available: Boolean(botAppPermissions?.geolocation), - latitude: geolocation?.latitude, - longitude: geolocation?.longitude, - altitude: geolocation?.altitude, - course: geolocation?.heading, - speed: geolocation?.speed, - horizontal_accuracy: geolocation?.accuracy, - vertical_accuracy: geolocation?.altitudeAccuracy, - }, - }); - } else { + if (!isAccessRequested) { + if (bot && webAppKey) { openLocationAccessModal({ bot, webAppKey }); + } else { + sendLocationUnavailable(); } - } else { + return; + } + + if (!isAccessGranted) { + sendLocationUnavailable(); + return; + } + + const geolocationData = await getGeolocationStatus(); + const { accessRequested, accessGranted, geolocation } = geolocationData; + + if (!accessGranted || !accessRequested || !geolocation) { + sendLocationUnavailable(); showNotification({ message: oldLang('PermissionNoLocationPosition') }); handleAppPopupClose(undefined); + return; } + + sendLocation(geolocation); }; handleRequestLocation(); diff --git a/src/config.ts b/src/config.ts index 102725476..d19b69f66 100644 --- a/src/config.ts +++ b/src/config.ts @@ -61,7 +61,7 @@ export const MEDIA_PROGRESSIVE_CACHE_DISABLED = false; export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive'; export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg'; -export const LANG_CACHE_NAME = 'tt-lang-packs-v51'; +export const LANG_CACHE_NAME = 'tt-lang-packs-v52'; export const ASSET_CACHE_NAME = 'tt-assets'; export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500]; export const DATA_BROADCAST_CHANNEL_PREFIX = 'tt-global'; diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index e4901decd..341e172bd 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -55,6 +55,31 @@ import { } from '../../selectors'; const LOCAL_BOOST_COOLDOWN = 86400; // 24 hours +const SMART_GLOCAL_DOMAIN = 'smart-glocal.com'; +const SMART_GLOCAL_TOKENIZE_PATH = '/cds/v1/tokenize/card'; + +function isValidSmartGlocalTokenizeUrl(tokenizeUrl: string) { + if (tokenizeUrl !== tokenizeUrl.trim()) { + return false; + } + + let parsedUrl: URL; + try { + parsedUrl = new URL(tokenizeUrl); + } catch { + return false; + } + + const { protocol, hostname, pathname, port, username, password } = parsedUrl; + const isSmartGlocalHost = hostname === SMART_GLOCAL_DOMAIN || hostname.endsWith(`.${SMART_GLOCAL_DOMAIN}`); + + return protocol === 'https:' + && isSmartGlocalHost + && pathname === SMART_GLOCAL_TOKENIZE_PATH + && !port + && !username + && !password; +} addActionHandler('validateRequestedInfo', (global, actions, payload): ActionReturnType => { const { requestInfo, saveInfo, tabId = getCurrentTabId() } = payload; @@ -429,8 +454,7 @@ async function sendSmartGlocalCredentials( url = 'https://tgb.smart-glocal.com/cds/v1/tokenize/card'; } - if (tokenizeUrl?.startsWith('https://') - && tokenizeUrl.endsWith('.smart-glocal.com/cds/v1/tokenize/card')) { + if (tokenizeUrl && isValidSmartGlocalTokenizeUrl(tokenizeUrl)) { url = tokenizeUrl; } diff --git a/src/types/webapp.ts b/src/types/webapp.ts index f904c88b1..b84068a07 100644 --- a/src/types/webapp.ts +++ b/src/types/webapp.ts @@ -239,14 +239,14 @@ interface WebAppOutboundEventMap { location_checked: | { available: false } | { - available: boolean; + available: true; access_requested: boolean; access_granted?: boolean; }; location_requested: - | { available: boolean } + | { available: false } | { - available: boolean; + available: true; latitude: number; longitude: number; altitude: number | null;