Expo React-Native Image upload to firebase in the background

78 views Asked by At

I am trying to make an app that allows users to take pictures with their device camera (ios for now) and then have that auto upload to set of events in firebase. The upload should either happen in the background (preferably) or queue for upload when the user next opens the app to the foreground

I have tried a few different things:

  • I have tried having it so the update photos callback on the medialibrary listener updates a list of images in async storage, and then the uploader uploads from that list and tracks uploads with another uploaded list in async storage. The uploader ran every X seconds
  • I have tried to use several job queue libraries, couldnt get any to work (this feels like it would be the best option as the listener could create a job to upload and then the upload could tick off one at a time)
  • I have also tried making my own make-shift uploader (see code below)

most of my efforts allow me to successfully upload images to my selected events, but the app crashes if I take too many pictures at once. It also is somewhat unreliable in the background.

I would really appreciate any advice or any help anyone can give, I am fairly new to react-native.

Also I am using expo-router, so I have the code in my _layout so that it runs on all pages of my app when isLive is triggered, is that advisable? Please let me know if you need further clarification

Below id the third attempt described above, trying to make my own makeshift uploader:

import React, { createContext, useState, useEffect, useRef } from "react";
import { Slot } from "expo-router";
import { useFonts } from "expo-font";
import {
  Montserrat_400Regular,
  Montserrat_400Regular_Italic,
  Montserrat_600SemiBold,
} from "@expo-google-fonts/montserrat";
import * as MediaLibrary from "expo-media-library";
import AsyncStorage from "@react-native-async-storage/async-storage";
import uploadImageToEvents from "../services/uploadImageToEvents";
import EventListeners from "../services/EventListeners";

export const AppContext = createContext(null);

function FontLoader({ children }) {
  const [loadedFonts] = useFonts({
    MontserratRegular: Montserrat_400Regular,
    MontserratItalic: Montserrat_400Regular_Italic,
    MontserratSemiBold: Montserrat_600SemiBold,
  });
  if (!loadedFonts) {
    return null;
  }
  return children;
}

export default function HomeLayout() {
  const [userDetails, setUserDetails] = useState<UserDetails>(
    {} as UserDetails
  );
  const [userEvents, setUserEvents] = useState<UserEvents>({} as UserEvents);
  const [selectedEvent, setSelectedEvent] = useState({});
  const [homeTabState, setHomeTabState] = useState<HomeTabState>("memories");
  const [isLive, setIsLive] = useState(false);
  const [liveEventIds, setLiveEventIds] = useState([]);
  const uploadQueue = useRef([]);
  const [imagesToUpload, setImagesToUpload] = useState(false);

  useEffect(() => {
    setImagesToUpload(uploadQueue.current.length > 0);
  }, [imagesToUpload]);

  useEffect(() => {
    if (!imagesToUpload || liveEventIds.length === 0) {
      return;
    }

    const uploadImages = async () => {
      const photoUris = uploadQueue.current.shift();
      try {
        await uploadImageToEvents(photoUris, liveEventIds);
        setImagesToUpload(false);
      } catch (err) {
        console.log(err);
      }
    };

    uploadImages();
  }, [imagesToUpload]);

  const updatePhotos = async (insertedAssets: MediaLibrary.Asset[]) => {
    try {
      const newUris = insertedAssets.map((asset) => asset.uri);
      uploadQueue.current.push(newUris);

      setImagesToUpload(true);
    } catch (err) {
      alert(err);
    }
  };

  useEffect(() => {
    let newSubscription: MediaLibrary.Subscription | null = null;

    (async () => {
      try {
        if (isLive) {
          const { status } = await MediaLibrary.requestPermissionsAsync();
          if (status !== "granted") {
            alert("User needs to grant permission to photos.");
            return;
          }
          newSubscription = MediaLibrary.addListener(
            async (event: MediaLibrary.MediaLibraryAssetsChangeEvent) => {
              await updatePhotos(event.insertedAssets);
            }
          );
        } else {
          newSubscription?.remove();
        }
      } catch (err) {
        alert(err);
      }
    })();

    return () => {
      newSubscription?.remove();
    };
  }, [isLive]);

  return (
    <FontLoader>
      <AppContext.Provider
        value={{
          userDetails,
          setUserDetails,
          userEvents,
          setUserEvents,
          selectedEvent,
          setSelectedEvent,
          homeTabState,
          setHomeTabState,
        }}
      >
        <EventListeners
          events={userEvents}
          setIsLive={setIsLive}
          setLiveEventIds={setLiveEventIds}
        />
        <Slot />
      </AppContext.Provider>
    </FontLoader>
  );
}

0

There are 0 answers