0

SDK version: 47.0.9 Expo-Go on android/ios

I am following the Expo-Go intro tutorial to create an app called StickerSmash. Here: https://docs.expo.dev/tutorial/screenshot/#capture-a-screenshot-and-save-it

Right now, when I find an image in my cameraroll to edit and add an emoji to it then press the save button… I’ll get this error in my logs:

“Missing MEDIA_LIBRARY write permission”

I’ve been using chatGPT4 to help me debug this for a few days 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 line 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}>

If I change my code to what the expo docs recommend, then certain alignments in my app get misaligned.

This is the complete 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', 
  },
});

EDIT: I -somehow- fixed the issue. After banging my head many times on the code and tutorial documentation. I believe that my issue was that I was using expo SDK 47 rather than expo SDK 48. I updated my SDK version. Furthermore, I updated and or fixed many packages using 'expo update' and 'expo doctor--fix-dependencies' respectively. The screenshots now save to my camera-roll.

0 Answers0