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.
Here is how it renders when I try to post it from my Next JS app
How It comes out on Facebook when I post it.
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>
)}
</>
);
};
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>
)}
</>
);
};