Mini App: Fix geolocation check (#6898)

This commit is contained in:
zubiden 2026-04-27 14:29:21 +02:00 committed by Alexander Zinchuk
parent fa6224abb7
commit e30555d3b3
5 changed files with 123 additions and 45 deletions

View File

@ -36,7 +36,7 @@ const LocationAccessModal: FC<OwnProps & StateProps> = ({
currentUser,
}) => {
const {
closeLocationAccessModal, toggleUserLocationPermission, sendWebAppEvent,
closeLocationAccessModal, toggleUserLocationPermission, sendWebAppEvent, showNotification,
} = getActions();
const isOpen = Boolean(modal);
@ -47,28 +47,48 @@ const LocationAccessModal: FC<OwnProps & StateProps> = ({
const containerRef = useRef<HTMLDivElement>();
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,
},
},
});

View File

@ -321,6 +321,35 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
});
});
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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();

View File

@ -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';

View File

@ -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<T extends GlobalState>(
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;
}

View File

@ -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;