Search code examples
reactjsfacebook-graph-apinext.js

Emoji not rendering in Facebook, Instagram and LinkedIn when I post from my Next Js app


I am building a social media management app that allows users autopost to their social profile using the Facebook, Instagram and LinkedIn API, everything works well but my Emojis do not render correctly on Facebook, and Instagram when I share it from my Next JS app.

  1. Here is how it renders when I try to post it from my Next JS app From my Next JS App

  2. How It comes out on Facebook when I post it.

enter image description here

  1. Here is the code that handles my Post Logic.
import React, { useState, useContext } from 'react';
import {
    FormGroup,
    FormControlLabel,
    Box,
    TextField,
    Button,
    Switch,
    LinearProgress,
    Alert,
    AlertTitle
} from '@mui/material';
import { makeStyles } from '@mui/styles';
import { useNavigate } from 'react-router-dom';
import SendIcon from '@material-ui/icons/Send';
import PencilAlt from 'src/packages/icons/PencilAlt';
import { useUserStore } from 'src/packages/state';
import { CustomCaptionsContext } from 'src/packages/custom-captions';
import { FacebookContext } from 'src/packages/facebook';
import { InstagramContext } from 'src/packages/instagram';
import { LinkedinContext } from 'src/packages/linkedin';
import { ProfileSelectContext } from 'src/packages/social-profile-selector';
import { MediaUploadContext } from 'src/packages/media-upload';
import ArrowLeft from 'src/packages/icons/ArrowLeft';
import useSettings from 'src/packages/hooks/useSettings';
import { DateTimePicker } from 'src/packages/lib/date-time-picker';
import { SocialProfileSelect } from 'src/packages/social-profile-selector';
import { ImageUploadButton } from 'src/packages/media-upload/components/ImageUploadButton';

const useStyles = makeStyles((theme) => ({
    socialProfilesAlert: {
        [theme.breakpoints.down('md')]: {
            marginTop: '1rem'
        }
    },
    captionButtonsContainer: {
        rowGap: '1rem',
        columnGap: '1rem',
        marginBottom: '1rem',
        justifyContent: 'flex-end',
        [theme.breakpoints.down('md')]: {
            flexDirection: 'column'
        }
    },
    scheduleContainer: {
        marginBottom: '0.5rem',
        [theme.breakpoints.down('md')]: {
            flexDirection: 'column'
        }
    },
    footer: {
        display: 'flex',
        marginTop: '0.5rem',
        columnGap: '1rem',
        rowGap: '0.5rem',
        [theme.breakpoints.down('md')]: {
            margin: 0,
            flexDirection: 'column'
        }
    }
}));

export const ProfileSelect = ({ post, setPublish }) => {
    const { useCustomCaptions, noCustomCaptionDetails } = useContext(
        CustomCaptionsContext
    );
    const { handleFacebookPost, handleScheduledFacebookPost, facebookPages } =
        useContext(FacebookContext);
    const { handleInstagramPost, instagramPages } = useContext(InstagramContext);
    const { handleLinkedinPost, linkedInPages } = useContext(LinkedinContext);
    const { customMediaUrl } = useContext(MediaUploadContext);
    const { selectedProfile } = useContext(ProfileSelectContext);
    const { settings } = useSettings();
    const styles = useStyles();
    const navigate = useNavigate();
    const { userDetails } = useUserStore();
    const [captionValue, setCaptionValue] = useState<any>('');
    const [captionChoice, setCaptionChoice] = useState<any>('');
    const [publishTime, setPublishTime] = useState(new Date());
    const [utcTime, setUtcTime] = useState('');
    const [isScheduled, setIsScheduled] = useState(false);
    const [isPosting, setIsPosting] = useState(false);

    const { preparedCaption, preparedHashtags } = useCustomCaptions(
        userDetails?.user_metadata?.captionSettings,
        post?.node?.featuredPost?.recommendedcaption,
        post?.node?.featuredPost?.popularhashtags
    );

    const allConnectedPages = [
        ...facebookPages,
        ...instagramPages,
        ...linkedInPages
    ];

    const noConnectedProfiles = allConnectedPages.length === 0;

    const handleSwitchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        if (!event.target.checked) {
            setIsScheduled(false);
        }
        setIsScheduled(event.target.checked);
    };

    const handlePost = async () => {
        setIsPosting(true);
        const { access_token, page_id, page_name } = selectedProfile;
        const encodedCaption = escape(captionValue);
        const imageSrc = customMediaUrl
            ? encodeURI(customMediaUrl as string)
            : post.node.featuredPost.postimage.mediaItemUrl;

        if (selectedProfile?.platform === 'linkedin') {
            await handleLinkedinPost(
                selectedProfile,
                captionValue,
                preparedCaption,
                preparedHashtags,
                captionChoice,
                isScheduled,
                utcTime,
                imageSrc
            );
            return setIsPosting(false);
        }

        if (selectedProfile?.platform === 'instagram') {
            await handleInstagramPost(
                selectedProfile,
                encodedCaption,
                imageSrc,
                isScheduled,
                utcTime
            );
            return setIsPosting(false);
        }

        if (isScheduled) {
            await handleScheduledFacebookPost(
                page_id,
                access_token,
                encodedCaption,
                utcTime,
                page_name,
                imageSrc
            );
            return setIsPosting(false);
        }

        await handleFacebookPost(page_id, access_token, encodedCaption, imageSrc);
        setIsPosting(false);
    };

    const getButtonText = () => {
        if (selectedProfile?.platform === 'linkedin') {
            if (isScheduled) {
                if (isPosting) {
                    return 'Scheduling LinkedIn post...';
                }
                return 'Scheduled for LinkedIn';
            }
            if (isPosting) {
                return 'Posting to LinkedIn...';
            }
            return 'Post to LinkedIn';
        }
        if (selectedProfile?.platform === 'instagram') {
            if (isScheduled) {
                if (isPosting) {
                    return 'Scheduling Instagram post...';
                }
                return 'Schedule for Instagram';
            }
            if (isPosting) {
                return 'Posting to Instagram...';
            }
            return 'Post to Instagram';
        }

        if (isScheduled) {
            if (isPosting) {
                return 'Scheduling Facebook post...';
            }
            return 'Schedule for Facebook';
        }

        if (isPosting) {
            return 'Posting to Facebook...';
        }

        return 'Post to Facebook';
    };

    const isValidDate = () => {
        return !(publishTime instanceof Date && !isNaN(publishTime.valueOf()));
    };

    return (
        <>
            {noConnectedProfiles && (
                <Box
                    className={styles.socialProfilesAlert}
                    sx={{ mb: '1rem', cursor: 'pointer' }}
                >
                    <Alert
                        severity="warning"
                        onClick={() => {
                            navigate('/dashboard/social-profiles');
                        }}
                    >
                        <AlertTitle>
                            You need connect your Social Profiles in order to post and schedule
                            posts. Click here to connect your profiles.
                        </AlertTitle>
                    </Alert>
                </Box>
            )}
            <SocialProfileSelect />
            <TextField
                id="caption-value"
                sx={{ marginBottom: '1rem' }}
                placeholder={`Enter your caption & hashtags, or paste in our suggestions using the buttons below...`}
                multiline
                rows={6}
                value={captionValue}
                onChange={(e) => {
                    setCaptionChoice('custom');
                    setCaptionValue(e.target.value);
                }}
            />
            <Box display={'flex'} className={styles.captionButtonsContainer}>
                <ImageUploadButton
                    variant="contained"
                    tooltipPlacement="top"
                    styles={{ borderRadius: '5px' }}
                />
                <Button
                    onClick={() => {
                        setCaptionChoice('caption');
                        setCaptionValue(preparedCaption);
                    }}
                    endIcon={<PencilAlt />}
                    variant="contained"
                    color={settings.theme === 'DARK' ? 'primary' : 'inherit'}
                    sx={{ borderRadius: '5px' }}
                >
                    Caption
                </Button>
                <Button
                    onClick={() => {
                        setCaptionChoice('hashtags');
                        setCaptionValue(preparedHashtags);
                    }}
                    endIcon={<PencilAlt />}
                    variant="contained"
                    color={settings.theme === 'DARK' ? 'primary' : 'inherit'}
                    sx={{ borderRadius: '5px' }}
                >
                    Hashtags
                </Button>
                <Button
                    onClick={() => {
                        setCaptionChoice('both');
                        setCaptionValue(
                            `${preparedCaption}` + '\n' + '\n' + `${preparedHashtags}`
                        );
                    }}
                    endIcon={<PencilAlt />}
                    variant="contained"
                    color={settings.theme === 'DARK' ? 'primary' : 'inherit'}
                    sx={{ borderRadius: '5px' }}
                >
                    Both
                </Button>
            </Box>
            <Box display="flex" className={styles.scheduleContainer}>
                <div style={{ marginTop: '10px' }}>
                    <FormGroup>
                        <FormControlLabel
                            control={<Switch onChange={handleSwitchChange} />}
                            label="Scheduled"
                        />
                    </FormGroup>
                </div>
                <Box sx={{ cursor: 'pointer' }} flex="1">
                    <DateTimePicker
                        isScheduled={isScheduled}
                        setUtcTime={setUtcTime}
                        setPublishTime={setPublishTime}
                    />
                </Box>
            </Box>
            <Box className={styles.footer}>
                <Button
                    className={'publish-media-button'}
                    sx={{ flex: 0.7 }}
                    disabled={
                        isPosting ||
                        noConnectedProfiles ||
                        noCustomCaptionDetails ||
                        // make sure the date is a valid date
                        isValidDate()
                    }
                    variant="contained"
                    endIcon={<SendIcon />}
                    onClick={handlePost}
                >
                    {getButtonText()}
                </Button>
                <Button
                    startIcon={<ArrowLeft />}
                    sx={{ flex: 0.3 }}
                    variant="contained"
                    onClick={() => setPublish()}
                >
                    Go Back
                </Button>
            </Box>
            {isPosting && (
                <Box sx={{ mt: 2, mb: 2 }}>
                    <LinearProgress />
                </Box>
            )}
        </>
    );
};



Solution

  • I finally fixed it and here is the final code.

    import React, { useState, useContext } from 'react';
    import {
        FormGroup,
        FormControlLabel,
        Box,
        TextField,
        Button,
        Switch,
        LinearProgress,
        Alert,
        AlertTitle
    } from '@mui/material';
    import { makeStyles } from '@mui/styles';
    import { useNavigate } from 'react-router-dom';
    import SendIcon from '@material-ui/icons/Send';
    import PencilAlt from 'src/packages/icons/PencilAlt';
    import { useUserStore } from 'src/packages/state';
    import { CustomCaptionsContext } from 'src/packages/custom-captions';
    import { FacebookContext } from 'src/packages/facebook';
    import { InstagramContext } from 'src/packages/instagram';
    import { LinkedinContext } from 'src/packages/linkedin';
    import { ProfileSelectContext } from 'src/packages/social-profile-selector';
    import { MediaUploadContext } from 'src/packages/media-upload';
    import ArrowLeft from 'src/packages/icons/ArrowLeft';
    import useSettings from 'src/packages/hooks/useSettings';
    import { DateTimePicker } from 'src/packages/lib/date-time-picker';
    import { SocialProfileSelect } from 'src/packages/social-profile-selector';
    import { ImageUploadButton } from 'src/packages/media-upload/components/ImageUploadButton';
    
    const useStyles = makeStyles((theme) => ({
        socialProfilesAlert: {
            [theme.breakpoints.down('md')]: {
                marginTop: '1rem'
            }
        },
        captionButtonsContainer: {
            rowGap: '1rem',
            columnGap: '1rem',
            marginBottom: '1rem',
            justifyContent: 'flex-end',
            [theme.breakpoints.down('md')]: {
                flexDirection: 'column'
            }
        },
        scheduleContainer: {
            marginBottom: '0.5rem',
            [theme.breakpoints.down('md')]: {
                flexDirection: 'column'
            }
        },
        footer: {
            display: 'flex',
            marginTop: '0.5rem',
            columnGap: '1rem',
            rowGap: '0.5rem',
            [theme.breakpoints.down('md')]: {
                margin: 0,
                flexDirection: 'column'
            }
        }
    }));
    
    export const ProfileSelect = ({ post, setPublish }) => {
        const { useCustomCaptions, noCustomCaptionDetails } = useContext(
            CustomCaptionsContext
        );
        const { handleFacebookPost, handleScheduledFacebookPost, facebookPages } =
            useContext(FacebookContext);
        const { handleInstagramPost, instagramPages } = useContext(InstagramContext);
        const { handleLinkedinPost, linkedInPages } = useContext(LinkedinContext);
        const { customMediaUrl } = useContext(MediaUploadContext);
        const { selectedProfile } = useContext(ProfileSelectContext);
        const { settings } = useSettings();
        const styles = useStyles();
        const navigate = useNavigate();
        const { userDetails } = useUserStore();
        const [captionValue, setCaptionValue] = useState<any>('');
        const [captionChoice, setCaptionChoice] = useState<any>('');
        const [publishTime, setPublishTime] = useState(new Date());
        const [utcTime, setUtcTime] = useState('');
        const [isScheduled, setIsScheduled] = useState(false);
        const [isPosting, setIsPosting] = useState(false);
    
        const { preparedCaption, preparedHashtags } = useCustomCaptions(
            userDetails?.user_metadata?.captionSettings,
            post?.node?.featuredPost?.recommendedcaption,
            post?.node?.featuredPost?.popularhashtags
        );
    
        const allConnectedPages = [
            ...facebookPages,
            ...instagramPages,
            ...linkedInPages
        ];
    
        const noConnectedProfiles = allConnectedPages.length === 0;
    
        const handleSwitchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
            if (!event.target.checked) {
                setIsScheduled(false);
            }
            setIsScheduled(event.target.checked);
        };
    
        const handlePost = async () => {
            setIsPosting(true);
            const { access_token, page_id, page_name } = selectedProfile;
            //const encodedCaption = escape(captionValue);
            //  Replaced Escaoe with encodeURIComponent as shown below
            const encodedCaption = encodeURIComponent(captionValue);
            const imageSrc = customMediaUrl
                ? encodeURI(customMediaUrl as string)
                : post.node.featuredPost.postimage.mediaItemUrl;
    
            if (selectedProfile?.platform === 'linkedin') {
                await handleLinkedinPost(
                    selectedProfile,
                    captionValue,
                    preparedCaption,
                    preparedHashtags,
                    captionChoice,
                    isScheduled,
                    utcTime,
                    imageSrc
                );
                return setIsPosting(false);
            }
    
            if (selectedProfile?.platform === 'instagram') {
                await handleInstagramPost(
                    selectedProfile,
                    encodedCaption,
                    imageSrc,
                    isScheduled,
                    utcTime
                );
                return setIsPosting(false);
            }
    
            if (isScheduled) {
                await handleScheduledFacebookPost(
                    page_id,
                    access_token,
                    encodedCaption,
                    utcTime,
                    page_name,
                    imageSrc
                );
                return setIsPosting(false);
            }
    
            await handleFacebookPost(page_id, access_token, encodedCaption, imageSrc);
            setIsPosting(false);
        };
    
        const getButtonText = () => {
            if (selectedProfile?.platform === 'linkedin') {
                if (isScheduled) {
                    if (isPosting) {
                        return 'Scheduling LinkedIn post...';
                    }
                    return 'Scheduled for LinkedIn';
                }
                if (isPosting) {
                    return 'Posting to LinkedIn...';
                }
                return 'Post to LinkedIn';
            }
            if (selectedProfile?.platform === 'instagram') {
                if (isScheduled) {
                    if (isPosting) {
                        return 'Scheduling Instagram post...';
                    }
                    return 'Schedule for Instagram';
                }
                if (isPosting) {
                    return 'Posting to Instagram...';
                }
                return 'Post to Instagram';
            }
    
            if (isScheduled) {
                if (isPosting) {
                    return 'Scheduling Facebook post...';
                }
                return 'Schedule for Facebook';
            }
    
            if (isPosting) {
                return 'Posting to Facebook...';
            }
    
            return 'Post to Facebook';
        };
    
        const isValidDate = () => {
            return !(publishTime instanceof Date && !isNaN(publishTime.valueOf()));
        };
    
        return (
            <>
                {noConnectedProfiles && (
                    <Box
                        className={styles.socialProfilesAlert}
                        sx={{ mb: '1rem', cursor: 'pointer' }}
                    >
                        <Alert
                            severity="warning"
                            onClick={() => {
                                navigate('/dashboard/social-profiles');
                            }}
                        >
                            <AlertTitle>
                                You need connect your Social Profiles in order to post and schedule
                                posts. Click here to connect your profiles.
                            </AlertTitle>
                        </Alert>
                    </Box>
                )}
                <SocialProfileSelect />
                <TextField
                    id="caption-value"
                    sx={{ marginBottom: '1rem' }}
                    placeholder={`Enter your caption & hashtags, or paste in our suggestions using the buttons below...`}
                    multiline
                    rows={6}
                    value={captionValue}
                    onChange={(e) => {
                        setCaptionChoice('custom');
                        setCaptionValue(e.target.value);
                    }}
                />
                <Box display={'flex'} className={styles.captionButtonsContainer}>
                    <ImageUploadButton
                        variant="contained"
                        tooltipPlacement="top"
                        styles={{ borderRadius: '5px' }}
                    />
                    <Button
                        onClick={() => {
                            setCaptionChoice('caption');
                            setCaptionValue(preparedCaption);
                        }}
                        endIcon={<PencilAlt />}
                        variant="contained"
                        color={settings.theme === 'DARK' ? 'primary' : 'inherit'}
                        sx={{ borderRadius: '5px' }}
                    >
                        Caption
                    </Button>
                    <Button
                        onClick={() => {
                            setCaptionChoice('hashtags');
                            setCaptionValue(preparedHashtags);
                        }}
                        endIcon={<PencilAlt />}
                        variant="contained"
                        color={settings.theme === 'DARK' ? 'primary' : 'inherit'}
                        sx={{ borderRadius: '5px' }}
                    >
                        Hashtags
                    </Button>
                    <Button
                        onClick={() => {
                            setCaptionChoice('both');
                            setCaptionValue(
                                `${preparedCaption}` + '\n' + '\n' + `${preparedHashtags}`
                            );
                        }}
                        endIcon={<PencilAlt />}
                        variant="contained"
                        color={settings.theme === 'DARK' ? 'primary' : 'inherit'}
                        sx={{ borderRadius: '5px' }}
                    >
                        Both
                    </Button>
                </Box>
                <Box display="flex" className={styles.scheduleContainer}>
                    <div style={{ marginTop: '10px' }}>
                        <FormGroup>
                            <FormControlLabel
                                control={<Switch onChange={handleSwitchChange} />}
                                label="Scheduled"
                            />
                        </FormGroup>
                    </div>
                    <Box sx={{ cursor: 'pointer' }} flex="1">
                        <DateTimePicker
                            isScheduled={isScheduled}
                            setUtcTime={setUtcTime}
                            setPublishTime={setPublishTime}
                        />
                    </Box>
                </Box>
                <Box className={styles.footer}>
                    <Button
                        className={'publish-media-button'}
                        sx={{ flex: 0.7 }}
                        disabled={
                            isPosting ||
                            noConnectedProfiles ||
                            noCustomCaptionDetails ||
                            // make sure the date is a valid date
                            isValidDate()
                        }
                        variant="contained"
                        endIcon={<SendIcon />}
                        onClick={handlePost}
                    >
                        {getButtonText()}
                    </Button>
                    <Button
                        startIcon={<ArrowLeft />}
                        sx={{ flex: 0.3 }}
                        variant="contained"
                        onClick={() => setPublish()}
                    >
                        Go Back
                    </Button>
                </Box>
                {isPosting && (
                    <Box sx={{ mt: 2, mb: 2 }}>
                        <LinearProgress />
                    </Box>
                )}
            </>
        );
    };