CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vkontakte--vk-bridge

Bridge library for VK Mini Apps to communicate with VK clients across iOS, Android, and Web platforms

Pending
Overview
Eval results
Files

geolocation.mddocs/

Geolocation Services

Access device location data and geographic information with privacy controls and accuracy options for location-based features.

Capabilities

Location Data Access

Get current device location with coordinates and accuracy information.

/**
 * Get device geolocation data
 * @returns Current location coordinates and accuracy
 */
function send(method: 'VKWebAppGetGeodata'): Promise<{
  lat: number;
  long: number;
  accuracy?: number;
}>;

/**
 * Set location for the application context
 * @param props.location - Location string or coordinates
 */
function send(method: 'VKWebAppSetLocation', props: {
  location: string;
}): Promise<{ result: true }>;

Usage Examples:

// Get current location
try {
  const location = await bridge.send('VKWebAppGetGeodata');
  console.log('Latitude:', location.lat);
  console.log('Longitude:', location.long);
  console.log('Accuracy:', location.accuracy, 'meters');
  
  // Use location data
  displayUserLocation(location.lat, location.long);
  findNearbyPlaces(location.lat, location.long);
} catch (error) {
  console.error('Location access failed:', error);
  handleLocationError(error);
}

// Set application location context
try {
  await bridge.send('VKWebAppSetLocation', {
    location: 'Moscow, Russia'
  });
  console.log('App location context set');
} catch (error) {
  console.error('Failed to set location:', error);
}

// Check location availability before use
const canGetLocation = await bridge.supportsAsync('VKWebAppGetGeodata');
if (canGetLocation) {
  const geoData = await bridge.send('VKWebAppGetGeodata');
  processLocationData(geoData);
} else {
  // Fallback: IP-based location or manual input
  useAlternativeLocationMethod();
}

Location-Based Features

Implement common location-based functionality patterns.

// Distance calculation
function calculateDistance(
  lat1: number, lon1: number,
  lat2: number, lon2: number
): number {
  const R = 6371; // Radius of Earth in kilometers
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLon = (lon2 - lon1) * Math.PI / 180;
  const a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * 
    Math.sin(dLon/2) * Math.sin(dLon/2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  return R * c; // Distance in kilometers
}

// Nearby places finder
async function findNearbyPlaces(userLat: number, userLon: number) {
  const places = [
    { name: 'Coffee Shop', lat: 55.7558, lon: 37.6176 },
    { name: 'Restaurant', lat: 55.7539, lon: 37.6208 },
    { name: 'Park', lat: 55.7527, lon: 37.6156 }
  ];
  
  const nearbyPlaces = places
    .map(place => ({
      ...place,
      distance: calculateDistance(userLat, userLon, place.lat, place.lon)
    }))
    .filter(place => place.distance <= 5) // Within 5km
    .sort((a, b) => a.distance - b.distance);
  
  console.log('Nearby places:', nearbyPlaces);
  return nearbyPlaces;
}

// Location-based content filtering
async function getLocationBasedContent() {
  try {
    const location = await bridge.send('VKWebAppGetGeodata');
    
    // Determine region/country from coordinates
    const region = await getRegionFromCoordinates(location.lat, location.long);
    
    // Filter content based on location
    const localizedContent = await fetchContentForRegion(region);
    
    return {
      location,
      region,
      content: localizedContent
    };
  } catch (error) {
    // Fallback to default content
    return {
      location: null,
      region: 'default',
      content: await fetchDefaultContent()
    };
  }
}

// Reverse geocoding helper
async function getRegionFromCoordinates(lat: number, lon: number): Promise<string> {
  // Simple region detection based on coordinates
  if (lat >= 55 && lat <= 56 && lon >= 37 && lon <= 38) {
    return 'moscow';
  } else if (lat >= 59 && lat <= 60 && lon >= 30 && lon <= 31) {
    return 'saint-petersburg';
  } else if (lat >= 40 && lat <= 85 && lon >= 19 && lon <= 170) {
    return 'russia';
  } else {
    return 'international';
  }
}

Location Permission Management

Handle location permissions and user privacy controls.

// Location permission checker
class LocationManager {
  private hasLocationPermission = false;
  
  async checkLocationPermission(): Promise<boolean> {
    try {
      // Check if geolocation is supported
      const isSupported = await bridge.supportsAsync('VKWebAppGetGeodata');
      if (!isSupported) {
        return false;
      }
      
      // Check granted permissions
      const permissions = await bridge.send('VKWebAppGetGrantedPermissions');
      this.hasLocationPermission = permissions.permissions.includes('location');
      
      return this.hasLocationPermission;
    } catch (error) {
      console.error('Permission check failed:', error);
      return false;
    }
  }
  
  async requestLocation(): Promise<{ lat: number; long: number } | null> {
    try {
      // Check permission first
      if (!await this.checkLocationPermission()) {
        console.log('Location permission not granted');
        return null;
      }
      
      // Get location
      const location = await bridge.send('VKWebAppGetGeodata');
      return location;
    } catch (error) {
      this.handleLocationError(error);
      return null;
    }
  }
  
  private handleLocationError(error: any) {
    switch (error.error_data?.error_code) {
      case 6: // Permission denied
        console.error('Location permission denied by user');
        this.showPermissionDialog();
        break;
      case 10: // Location not available
        console.error('Location services not available');
        this.showLocationUnavailableMessage();
        break;
      case 7: // Timeout
        console.error('Location request timed out');
        this.showTimeoutMessage();
        break;
      default:
        console.error('Location error:', error);
        this.showGenericErrorMessage();
    }
  }
  
  private showPermissionDialog() {
    // Show UI explaining why location is needed
    console.log('Please enable location permissions in VK app settings');
  }
  
  private showLocationUnavailableMessage() {
    // Show message about location services
    console.log('Location services are not available on this device');
  }
  
  private showTimeoutMessage() {
    // Show timeout message with retry option
    console.log('Location request timed out. Please try again.');
  }
  
  private showGenericErrorMessage() {
    // Show generic error message
    console.log('Unable to get location. Please try again later.');
  }
}

Location Caching and Optimization

Optimize location requests with caching and accuracy controls.

// Location cache manager
class LocationCache {
  private cache: {
    location: { lat: number; long: number; accuracy?: number };
    timestamp: number;
    maxAge: number;
  } | null = null;
  
  async getLocation(maxAge: number = 300000): Promise<{ lat: number; long: number } | null> {
    // Check cache first
    if (this.cache && 
        Date.now() - this.cache.timestamp < maxAge &&
        (this.cache.location.accuracy || 100) <= 100) {
      console.log('Using cached location');
      return this.cache.location;
    }
    
    try {
      // Get fresh location
      const location = await bridge.send('VKWebAppGetGeodata');
      
      // Update cache
      this.cache = {
        location,
        timestamp: Date.now(),
        maxAge
      };
      
      console.log('Got fresh location');
      return location;
    } catch (error) {
      // Return cached location if fresh request fails
      if (this.cache) {
        console.log('Using stale cached location due to error');
        return this.cache.location;
      }
      throw error;
    }
  }
  
  clearCache() {
    this.cache = null;
  }
  
  getCacheAge(): number {
    return this.cache ? Date.now() - this.cache.timestamp : -1;
  }
}

// Usage example
const locationCache = new LocationCache();

async function getCurrentLocation() {
  try {
    // Get location with 5-minute cache
    const location = await locationCache.getLocation(300000);
    return location;
  } catch (error) {
    console.error('Failed to get location:', error);
    return null;
  }
}

Platform Availability

Geolocation services are available on the following platforms:

  • iOS WebView: Full GPS access with VKWebAppGetGeodata
  • Android WebView: Full GPS access with VKWebAppGetGeodata
  • Web/Desktop: Limited availability (may not work in all contexts)

Always check method support and handle permissions appropriately:

async function initializeLocation() {
  const canGetLocation = await bridge.supportsAsync('VKWebAppGetGeodata');
  
  if (canGetLocation) {
    const locationManager = new LocationManager();
    const hasPermission = await locationManager.checkLocationPermission();
    
    if (hasPermission) {
      const location = await locationManager.requestLocation();
      if (location) {
        console.log('Location initialized:', location);
        return location;
      }
    } else {
      console.log('Location permission required');
    }
  } else {
    console.log('Geolocation not supported on this platform');
  }
  
  return null;
}

Privacy and Best Practices

  1. Request location only when needed - Don't request location access immediately on app start
  2. Explain why location is needed - Show clear UI explaining the benefit to users
  3. Handle permission denial gracefully - Provide alternative functionality when location is denied
  4. Cache location data - Avoid frequent location requests to preserve battery
  5. Respect accuracy - Use appropriate accuracy thresholds for your use case
  6. Provide manual alternatives - Allow users to manually enter location when automatic detection fails
// Privacy-conscious location request
async function requestLocationWithExplanation(reason: string) {
  // Show explanation first
  const userConsent = confirm(`This app needs your location to ${reason}. Allow location access?`);
  
  if (!userConsent) {
    console.log('User declined location access');
    return null;
  }
  
  try {
    const location = await bridge.send('VKWebAppGetGeodata');
    console.log('Location granted:', location);
    return location;
  } catch (error) {
    console.error('Location request failed:', error);
    return null;
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-vkontakte--vk-bridge

docs

advertising-monetization.md

application-lifecycle.md

authentication.md

core-bridge.md

device-features.md

geolocation.md

index.md

launch-parameters.md

middleware.md

payments-commerce.md

qr-barcode-scanning.md

social-features.md

storage-data.md

ui-display.md

user-data.md

tile.json