UNPKG

react-crop-upload-pictures

Version:
305 lines (266 loc) 10.4 kB
import React, {useCallback, useState, forwardRef, useImperativeHandle, useEffect} from "react" import { Button } from 'react-bootstrap'; import ApiCall from './Helper/ApiCall.js'; import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props' import { LazyLoadImage } from "react-lazy-load-image-component"; import "../assets/style/index.scss" const ERROR = { NOT_SUPPORTED_EXTENSION: 'NOT_SUPPORTED_EXTENSION', FILE_SIZE_TOO_LARGE: 'FILE_SIZE_TOO_LARGE', DIMENSION_IMAGE: "DIMENSION_IMAGE" } const LoadingIndicator = () => ( <div className="d-flex justify-content-center mt-3"> <div className="spinner-border" role="status"> <span className="sr-only">Loading...</span> </div> </div> ) const SocialMediaImportPopup = forwardRef(( { title = null, modalSource = null, height = "200px", width = "200px", classStyle = "", iconSize = "lg", handleClose = () => { }, setPhotosCallback = () => { }, token = '', multiple = false, }, ref ) => { useImperativeHandle(ref, () => ({ getErrors() { return []; }, getPictures() { return pictures; }, })); const [instagramUserId, setInstagramUserId] = useState(null); const [pictures, setPictures] = useState([]); const [selected, setSelected] = useState([]); const [currentToken, setCurrentToken] = useState(token); const [isPulled, setIsPulled] = useState(false); const [loading, setLoading] = useState(false) const readTokenAndTimestamp = useCallback(() => { const rawData = localStorage.getItem(`${modalSource}Token`); if (!rawData) { return [null, null]; } const pieces = rawData.split('~~'); const accessToken = pieces?.[0]; const timestamp = parseInt(pieces?.[1]); if (Date.now() - timestamp > 3598 * 1000) { localStorage.removeItem(`${modalSource}Token`); return [null, null] } return [accessToken, timestamp]; }, [modalSource]); const setTokenAndTimestamp = useCallback((accessToken) => { localStorage.setItem(`${modalSource}Token`, `${accessToken}~~${Date.now()}`); setCurrentToken(accessToken); }, [modalSource]); const getInstagramAccessCode = () => { const params = new URL(document.location.toString()).searchParams; return params.get("code"); } useEffect(() => { setPhotosCallback([], []); const pieces = readTokenAndTimestamp(); const accessToken = pieces?.[0]; const instagramAccessCode = getInstagramAccessCode(); if (accessToken && !currentToken) { setCurrentToken(accessToken); } else if (!accessToken && !instagramAccessCode && modalSource === 'instagram') { window.location.href = "https://api.instagram.com/oauth/authorize?client_id=" + process.env.REACT_APP_INSTAGRAM_APP_ID + "&redirect_uri=" + window.location.origin + "/callback/instagram" + "&scope=user_profile,user_media&response_type=code"; } else if (!accessToken && instagramAccessCode && modalSource === 'instagram') { const getAndSetInstagramAccessToken = async (accessCode) => { try { const result = await ApiCall.post('account/social/exchange/instagram', { accessCode, redirectUri: window.location.origin + "/callback/instagram"}); if (result?.data && result?.data?.access_token) { setTokenAndTimestamp(result?.data?.access_token); } else { handleClose(); } } catch { handleClose(); } } getAndSetInstagramAccessToken(instagramAccessCode); } if (!isPulled) { switch (modalSource) { case "facebook": FBGetPhotos(); break; case "instagram": InstagramGetPhotos(); break; } } }, [modalSource, currentToken, isPulled]) async function fetchFBPic(path, accessToken) { const response = await fetch(`https://graph.facebook.com/${path}&access_token=${accessToken}`); return await response.json(); } async function fetchInstagramPic(accessToken, userId) { const response = await fetch(`https://graph.instagram.com/${userId?.id}/media?access_token=${accessToken}`); return await response.json(); } const getAlbums = useCallback(async (takeFromFB=true) => { const userId = await(await fetch(`https://graph.instagram.com/me?fields=id,username&access_token=${currentToken}`))?.json(); setInstagramUserId(userId?.username); const response = takeFromFB ? await fetchFBPic('me/albums?fields=id,name', currentToken) : await fetchInstagramPic(currentToken, userId) if(response.data && response.data.length > 0) { if (takeFromFB) { await response.data.forEach(async album => { if(album.name === "Profile pictures") { let data = await getPhotosForAlbumId(album.id, currentToken) let results = [] results = data.map((item, index) =>({src: item.source})); if (results) { setPictures([...pictures, ...results]); } } }) } else { const results = await Promise.all(response.data.map(async (item, index) =>{ const data = await getPhotoForInstaPhotoId(item?.id ,currentToken); return {src: data?.media_url} })); if (results) { setPictures([...pictures, ...results]); } } } setLoading(false); setIsPulled(true); }, [[...pictures], currentToken]) async function getPhotosForAlbumId(albumId, accessToken) { const response = await fetchFBPic(`${albumId}/photos?fields=source`, accessToken); return await response.data && response.data.length > 0 ? response.data : [] } async function getPhotoForInstaPhotoId(photoId, accessToken) { const response = await fetch(`https://graph.instagram.com/${photoId}?access_token=${accessToken}&fields=media_url,permalink`) return await response.json(); } async function FBGetPhotos() { setLoading(true) if (currentToken) { await getAlbums() } else { setLoading(false) } } async function InstagramGetPhotos() { setLoading(true) if (currentToken) { await getAlbums(false) } else { setLoading(false) } } const toggleMarkSelected = useCallback((pos) => { let result; if (!multiple) { if (selected.includes(pos)) { setSelected([]); setPhotosCallback(pictures, []); } else { result = [pos]; setSelected(result); setPhotosCallback(pictures, result); } } else { if (selected.includes(pos)) { result = selected.filter(el => el !== pos); setSelected(result); setPhotosCallback(pictures, result); } else { result = [...selected, pos]; setSelected(result); setPhotosCallback(pictures, result); } } }, [pictures, selected, multiple]) const responseFacebook = (response) => { if (response && response?.accessToken) { setTokenAndTimestamp(response.accessToken); } else { handleClose(); } } return ( <> <div ref={ref} style={{minHeight: 480, maxHeight: 800, overflowY: "auto", overflowX: "hidden"}}> { <div className={classStyle}> <div className="upload-content"> {title !== null ? <div className="upload-header"> <h1 className="upload-title fs-5">{title}</h1> </div> : ""} <div className="mb-2"> <div className="alert alert-info" role="alert"> {instagramUserId !== null ? `Photos of ${instagramUserId}. (${selected?.length}) pictures selected` : `Loading...`} </div> </div> <div className="mb-5 upload-body"> <div className="row justify-content-center mb-5"> <div className="col" style={{width: "auto"}}> </div> </div> { loading && <LoadingIndicator /> } {!currentToken && modalSource === 'facebook' && ( <div className="d-flex justify-content-center" style={{marginTop: 104}}> <FacebookLogin appId={process.env.REACT_APP_FACEBOOK_APP_ID} autoLoad={true} fields="name,email,picture" scope="public_profile,user_friends,user_photos" render={renderProps => ( <Button variant="primary" className="text-white" onClick={renderProps.onClick}>Please click here to authorize your Facebook profile</Button> )} callback={responseFacebook} /> </div> )} <div className="row d-flex justify-content-center space-photos"> { pictures.length > 0 && ( <div className="row d-flex justify-content-center"> { pictures.map((picture, index) => ( <div className={`position-relative p-0 mx-2 mb-4 social-media ${selected.includes(index) ? 'selected' : ''}`} key={index} style={{ width: width, height: height }} onClick={() => toggleMarkSelected(index)} > <ImageDisplay picture={picture} height={height} width={width} className="mb-4" /> </div> )) } </div> ) } </div> </div> </div> </div> } </div> </> ) }) function ImageDisplay({ picture, width, height }) { return <LazyLoadImage src={picture?.src} style={{ height: height, width: width, }} alt="Social media picture" effect="opacity" width={width} height={height} placeholder={<LoadingIndicator />} /> } export default SocialMediaImportPopup;