Unable to take and save a screen shot

SDK version: 47.0.9
android

Take a screenshot - Expo Documentation

I’m following this tutorial to take a screenshot of the expoGo app StickerSmash. Right now, when I find an image in my cameraroll to edit and add an emoji to then to save… I’ll get a “Missing MEDIA_LIBRARY write permission” error in my logs. I’ve been using chatGPT4 to help me debug this for a few hours but I still cannot figure out what the problem. I am not getting an alert (which I should be) that says “Saved!” when I click the saved button and the screenshot isn’t getting saved to my cameraroll.

I think the problem arises with the ‘requestPermission();’ and the fact that no dialog box is being offered to the user, in this if statement:

if (status === null) {
    console.log("the status was null");
    requestPermission();
    // console.log("the status1 %s", status);
  }

Also, there is a discrepancy in the expo tutorial in the return lines which may cause some error. Essentially, the code I have is this:

<GestureHandlerRootView style={styles.container}>
      **<View style={styles.container}>**
        <View style={styles.imageContainer}>
           <View ref={imageRef} collapsable={false}>

But the expo docs just have this:

<GestureHandlerRootView style={styles.container}>
      <View style={styles.imageContainer}>
        <View ref={imageRef} collapsable={false}>

Any advice…

This is the code:

import * as MediaLibrary from 'expo-media-library';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Image } from 'react-native';
import Button from './components/Button';
import ImageViewer from './components/ImageViewer';
import * as ImagePicker from 'expo-image-picker';
import {useState, useRef} from 'react';
import CircleButton from './components/CircleButton';
import IconButton from './components/IconButton';
import EmojiPicker from "./components/EmojiPicker";
import EmojiList from './components/EmojiList'
import EmojiSticker from './components/EmojiSticker';

import {GestureHandlerRootView} from "react-native-gesture-handler";
import { captureRef } from 'react-native-view-shot';
// import * as Permissions from 'expo-permissions';

const PlaceholderImage = require('./assets/images/background-image.png');

export default function App() {


  

  

  const imageRef = useRef();
  const [status, requestPermission] = MediaLibrary.usePermissions();
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [showAppOptions, setShowAppOptions] = useState(false);
  const [selectedImage, setSelectedImage] = useState(null);
  const [pickedEmoji, setPickedEmoji] = useState(null);
  // const [status, setStatus] = useState(null);

  if (status === null) {
    console.log("the status was null");
    requestPermission();
    // console.log("the status1 %s", status);
  }
 
  const onSaveImageAsync = async () => {
  
    try {
      // console.log("the status2 %s", status);
      const localUri = await captureRef(imageRef, {
        height: 440,
        quality: 1,
      });
      // console.log("the status3 %s", status);
      await MediaLibrary.saveToLibraryAsync(localUri);
      if (localUri) {
        // console.log("the status4 %s", status);
        alert("Saved!");
      }
    } catch (e) {
      // console.log("the status5 %s", status);
      console.log(e);
    }
  };
  

  const pickImageAsync = async () => {
    let result = await ImagePicker.launchImageLibraryAsync({
      allowsEditing: true, 
      quality: 1,
    });

    if (!result.canceled) {
      setSelectedImage(result.assets[0].uri);
      setShowAppOptions(true);

    } else {
      alert('You did not select any image.');
    }

  }

  const onReset = () => {
    setShowAppOptions(false);
  };

  const onAddSticker = () => {
    setIsModalVisible(true);
  };

  

  const onModalClose = () => {
    setIsModalVisible(false);
  };

  return (
    <GestureHandlerRootView style={styles.container}>
      <View style={styles.container}>
        <View style={styles.imageContainer}>
          <View ref={imageRef} collapsable={false}>
            <ImageViewer placeholderImageSource={PlaceholderImage} selectedImage={selectedImage} />
            {pickedEmoji !== null ? (
              <EmojiSticker imageSize={40} stickerSource={pickedEmoji} /> 
            ) : null}
          </View>
        </View>
        {showAppOptions ? (
          <View style = {styles.optionsContainer}>
            <View style = {styles.optionsRow}>
              <IconButton icon="refresh" label="Reset" onPress={onReset} />
              <CircleButton onPress={onAddSticker} />
              <IconButton icon="save-alt" label="Save" onPress={onSaveImageAsync} />
            </View>
          </View>
          //{/* <View /> */}
        ) : (
          <View style={styles.footerContainer}>
            <Button theme="primary" label="Choose a photo" onPress={pickImageAsync}/>
            <Button label="Use this photo" onPress={() => setShowAppOptions(true)}/>  
          </View> 
        )}
        <EmojiPicker isVisible={isModalVisible} onClose={onModalClose}>
          <EmojiList onSelect={setPickedEmoji} onCloseModal={onModalClose}/>
        </EmojiPicker>
        <StatusBar style="auto" />
      </View>
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  footerContainer: {
    flex: 1 / 3, 
    alignItems: 'center',
  },
  container: {
    flex: 1,
    backgroundColor: '#00008b',
    alignItems: 'center',
  },
  imageContainer: {
    flex: 1, 
    paddingTop: 60,
  },
  image: {
    width: 320, 
    height: 440, 
    borderRadius: 18,
  },

  optionsContainer: {
    position: 'absolute', 
    bottom: 80,
  }, 
  optionsRow: {
    alignItems: 'center', 
    flexDirection: 'row', 
  },
});