rnmapbox / rnmapbox/maps

[Bug]: Black screen when device is offline

Open
#3,683 2 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug 🪲
Dominant language
Kotlin
Stars
2.9k
Forks
947
Avg merge
6d 37m
Merged PRs (30d)
1

Description

Mapbox Implementation

Mapbox

Mapbox Version

11.7.0

React Native Version

0.73.2

Platform

iOS, Android

@rnmapbox/maps version

10.1.33

Standalone component to reproduce
import React, { useState, useRef } from 'react';
import { View, TouchableOpacity, Platform, Dimensions, Alert } from 'react-native';
import Mapbox from '@rnmapbox/maps';
import geoViewport from '@mapbox/geo-viewport';

const MAPBOX_VECTOR_TILE_SIZE = 512;

const Home = () => {
  const mapRef = useRef(null);
  const cameraRef = useRef(null);
  const [followUserLocation, setFollowUserLocation] = useState(true);
  const [userLocation, setUserLocation] = useState([-80.1918, 25.7617]);
  const [showBasemapSelector, setShowBasemapSelector] = useState(false);
  const [rotation, setRotation] = useState(0);
  const [centerToDisplay, setCenterToDisplay] = useState([-80.1918, 25.7617]);

  const startDownload = async () => {
    const {width, height} = Dimensions.get('window');

    const region = {
      name: "Region",
      styleURL: "selectedStyle.url",
      bounds: geoViewport.bounds(
        centerToDisplay,
        12,
        [width, height],
        MAPBOX_VECTOR_TILE_SIZE,
      ),
      minZoom: 1,
      maxZoom: 16,
    };

    try {
      const pack = await Mapbox.offlineManager.createPack(
        {
          name: region.name,
          styleURL: region.styleURL,
          bounds: [
            [region.bounds[0], region.bounds[1]],
            [region.bounds[2], region.bounds[3]],
          ],
          minZoom: region.minZoom,
          maxZoom: region.maxZoom,
          metadata: {},
        },
      );
    } catch (error) {
      console.error('Error downloading region:', error);
      Alert.alert(
        'Download Failed',
        'Please try selecting a smaller area or check your internet connection.',
      );
    } finally {
    }
  };

  const handleCameraChanged = (event) => {
    setRotation(event.properties.heading);
    setCenterToDisplay(event.properties.center);
  };

  const resetNorth = () => {
    cameraRef.current?.setCamera({
      heading: 0,
      animationDuration: 500,
    });
  };

  const zoomIn = () => {
    cameraRef.current?.zoomTo(cameraRef.current?.getZoom() + 1, 500);
  };

  const zoomOut = () => {
    cameraRef.current?.zoomTo(cameraRef.current?.getZoom() - 1, 500);
  };

  return (
    <View style={styles.page}>
      <Mapbox.MapView
        onPress={() => setShowBasemapSelector(false)}
        attributionEnabled={false}
        ref={mapRef}
        style={styles.map}
        styleJSON={JSON.stringify({ /* style json object */})}
        logoEnabled={false}
        scaleBarEnabled={true}
        scaleBarPosition={{
          bottom: 45,
        }}
        onTouchMove={() => followUserLocation && setFollowUserLocation(false)}
        onCameraChanged={handleCameraChanged}>
        <Mapbox.UserLocation
          visible={true}
          animated={true}
          minDisplacement={10}
          showsUserHeadingIndicator={true}
          requestsAlwaysUse={true}
          onUpdate={loc => {
            if (followUserLocation) {
              setUserLocation([loc?.coords?.longitude, loc?.coords.latitude]);
            }
          }}
        />
        <Mapbox.Camera
          maxZoomLevel={19}
          minZoomLevel={0}
          ref={cameraRef}
          centerCoordinate={userLocation || [-80.1918, 25.7617]}
          defaultSettings={{
            centerCoordinate: userLocation,
            zoomLevel: 8,
            animationDuration: 1,
          }}
          allowUpdates={true}
          animationMode="flyTo"
        />
      </Mapbox.MapView>

      {/* Compass */}
      <TouchableOpacity
        activeOpacity={0.8}
        style={styles.compass}
        onPress={resetNorth}>
      </TouchableOpacity>

      {/* Zoom controls */}
      <View style={styles.zoomControls}>
        <TouchableOpacity style={styles.zoomButton} onPress={zoomIn}>
        </TouchableOpacity>
        <TouchableOpacity style={styles.zoomButton} onPress={zoomOut}>
        </TouchableOpacity>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  page: {
    flex: 1,
  },
  map: {
    flex: 1,
  },
  compass: {
    position: 'absolute',
    top: 20,
    right: 20,
    backgroundColor: 'white',
    borderRadius: 20,
    padding: 8,
    elevation: 5,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
  },
  zoomControls: {
    position: 'absolute',
    right: 20,
    bottom: 100,
    backgroundColor: 'white',
    borderRadius: 10,
    elevation: 5,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
  },
  zoomButton: {
    padding: 10,
  },
});

export default Home;
Observed behavior and steps to reproduce

The issue is that I get a black screen when offline although there are map styles that were downloaded in the region. I followed the example in the documentation: https://rnmapbox.github.io/docs/examples/Map/OfflineExample.

image

Note that the issue happens if the user exists and kills the app and then opens it again or when cache is cleared. Clearing cache consistently leads to this issue of the black screen appearing if device is offline.

Expected behavior

The expected behavior is for mapbox to use the downloaded map styles when the user is offline.

Notes / preliminary analysis

No response

Additional links and references

I think that the issue that I'm facing is similar to the following issue from the MB Flutter repo: https://github.com/mapbox/mapbox-maps-flutter/issues/661

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the standalone React Native component in the issue and the linked OfflineExample documentation; reproduce on iOS and Android after downloading a pack, killing and reopening the app, and clearing the cache. Done means the downloaded style renders offline in those restart and cache scenarios.

Written by the indexing model from the issue text.

Assessment

Tech stack
react-native, typescript
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.