Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/components/Announcements/SocialMediaComposer.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React, { useState } from 'react';
import RedditPanel from './platforms/reddit/RedditPanel.jsx';
import Scheduled from './platforms/reddit/Scheduled.jsx';
import SubmittedPosts from './platforms/reddit/SubmittedPosts.jsx';

export default function SocialMediaComposer({ platform }) {
const [postContent, setPostContent] = useState('');
Expand Down Expand Up @@ -27,6 +30,26 @@ export default function SocialMediaComposer({ platform }) {
};
};

const renderPlatformComponent = () => {
const platformMap = {
reddit: {
composer: <RedditPanel />,
scheduled: <Scheduled />,
history: <SubmittedPosts />,
},
// add other platforms here:
// facebook: { composer: <FacebookComposer />, scheduled: <FacebookScheduled />, ... }
};

return (
platformMap[platform]?.[activeSubTab] ?? (
<div>
No component available for {platform} / {activeSubTab}
</div>
)
);
};

return (
<div style={{ padding: '1rem' }}>
<h3>{platform} </h3>
Expand All @@ -48,7 +71,7 @@ export default function SocialMediaComposer({ platform }) {
))}
</div>

{activeSubTab === 'composer' && (
{activeSubTab === 'composer' && platform !== 'reddit' && (
<div>
<textarea
value={postContent}
Expand Down Expand Up @@ -122,7 +145,7 @@ export default function SocialMediaComposer({ platform }) {
</div>
</div>
)}
{activeSubTab === 'scheduled' && (
{activeSubTab === 'scheduled' && platform !== 'reddit' && (
<div>
<p>
<strong>Scheduled Posts for {platform}</strong>
Expand All @@ -134,7 +157,7 @@ export default function SocialMediaComposer({ platform }) {
</div>
)}

{activeSubTab === 'history' && (
{activeSubTab === 'history' && platform !== 'reddit' && (
<div>
<p>
<strong>Post History for {platform}</strong>
Expand All @@ -146,7 +169,7 @@ export default function SocialMediaComposer({ platform }) {
</div>
)}

{activeSubTab === 'details' && (
{activeSubTab === 'details' && platform !== 'reddit' && (
<div>
<p>
<strong>{platform}-Specific Details</strong>
Expand All @@ -159,6 +182,8 @@ export default function SocialMediaComposer({ platform }) {
</ul>
</div>
)}

{renderPlatformComponent()}
</div>
);
}
49 changes: 49 additions & 0 deletions src/components/Announcements/platforms/reddit/RedditPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import './reddit.css';
import { useEffect, useState } from 'react';
import { ENDPOINTS } from '../../../../utils/URL';
import axios from 'axios';
import SubmitPost from './SubmitPost';

export default function RedditPanel() {
const [hasToken, setHasToken] = useState(false);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently hasToken initializes as false, which means authenticated users will briefly see the "Connect to Reddit" button while the token validation request is in progress. Consider using null as a third state to represent loading, so nothing renders until the validation is complete.


useEffect(() => {
async function validateToken() {
try {
const res = await axios.get(`${ENDPOINTS.AP_REDDIT_AUTH_TOKEN}`);
setHasToken(res.data.exists);
} catch (error) {
setHasToken(false);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently if the token validation request fails, the catch block silently sets hasToken to false, which means a network error looks exactly the same as "not connected" to the user. Worth adding a separate error state so we can show something more helpful in that case rather than just defaulting to the connect button.

}

Check warning on line 17 in src/components/Announcements/platforms/reddit/RedditPanel.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle this exception or don't catch it at all.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfdC0VwhrarSxxa0p&open=AZrSfdC0VwhrarSxxa0p&pullRequest=4478
}
validateToken();
}, []);

const handleConnectToReddit = () => {
// Code to display Reddit Manager
const authorizationUrl = `https://www.reddit.com/api/v1/authorize?
client_id=${process.env.REACT_APP_REDDIT_CLIENT_ID}&response_type=code
&state=random-string
&redirect_uri=${process.env.REACT_APP_REDDIT_REDIRECT_URL}
&duration=permanent&scope=identity,submit,save`;
window.open(authorizationUrl, '_self');
};

return (
<div style={{ padding: '1rem' }}>
<div>
{hasToken === false && (
<button type="button" className="reddit-btn mt-6" onClick={handleConnectToReddit}>
Connect to reddit
</button>
)}
{hasToken === true && (
<div>
{/* Render Reddit post editor here */}
<SubmitPost />
</div>
)}
</div>
</div>
);
}
33 changes: 33 additions & 0 deletions src/components/Announcements/platforms/reddit/Redirect.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect, useState } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import { toast } from 'react-toastify';
import axios from 'axios';
import { ENDPOINTS } from '../../../../utils/URL';

function Redirect() {
const { search } = useLocation();
const [redditCode, setRedditCode] = useState(null);
const history = useHistory();

const fetchRedditRefreshToken = async oneTimeCode => {
try {
await axios.get(`${ENDPOINTS.AP_REDDIT_AUTH_LOGIN}?code=${encodeURIComponent(oneTimeCode)}`);
history.push('/announcements');
} catch (error) {
toast.error(error.message || ' Failed to connect to Reddit');
}
};

useEffect(() => {
const query = new URLSearchParams(search);
if (query.has('code')) {
const code = query.get('code');
setRedditCode(code);
fetchRedditRefreshToken(code);
}
}, []);

return <div>Recived code from Reddit {redditCode}</div>;
}

export default Redirect;
160 changes: 160 additions & 0 deletions src/components/Announcements/platforms/reddit/Scheduled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { useState, useEffect } from 'react';
import axios from 'axios';
import { toast } from 'react-toastify';
import { ENDPOINTS } from '../../../../utils/URL';
import SubmitPost from './SubmitPost';
import { Modal, ModalFooter, Button } from 'reactstrap';

export default function Scheduled() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);

Check warning on line 10 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'loading' variable.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0Y&open=AZrSfc_WVwhrarSxxa0Y&pullRequest=4478

Check warning on line 10 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "loading".

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0Z&open=AZrSfc_WVwhrarSxxa0Z&pullRequest=4478
const [error, setError] = useState(null);

Check warning on line 11 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'error' variable.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0a&open=AZrSfc_WVwhrarSxxa0a&pullRequest=4478

Check warning on line 11 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "error".

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0b&open=AZrSfc_WVwhrarSxxa0b&pullRequest=4478
const [modal, setModal] = useState(false);

// editing state to open SubmitPost with initial data
const [editingPost, setEditingPost] = useState(null);

useEffect(() => {
fetchScheduledPosts();
}, []);

async function fetchScheduledPosts() {
setLoading(true);
setError(null);
try {
const res = await axios.get(`${ENDPOINTS.AP_REDDIT_POST}`, {
params: { status: 'scheduled' },
});

setPosts(res.data.posts || []);
} catch (error) {
setError('Unable to load scheduled posts');
toast.error('Unable to load scheduled posts for Reddit');
} finally {

Check warning on line 33 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle this exception or don't catch it at all.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0c&open=AZrSfc_WVwhrarSxxa0c&pullRequest=4478
setLoading(false);
}
}

const formatSchedule = dateToFormat => {
const d = new Date(dateToFormat);
const options = { month: 'short', day: 'numeric' };
return d.toLocaleDateString('en-US', options);
};

const handleCancelPost = async postId => {
const confirmDelete = window.confirm(`Are you sure you wnat to delete this post?`);

Check warning on line 45 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0d&open=AZrSfc_WVwhrarSxxa0d&pullRequest=4478
if (!confirmDelete) return;
try {
const res = await axios.delete(`${ENDPOINTS.AP_REDDIT_POST}/${postId}`);

if (res?.status === 200) {
toast.success(res.data?.message || 'Scheduled post cancelled');
fetchScheduledPosts();
} else {
toast.error(res?.data?.message || 'ubable to cancel scheduled post');
}
} catch (err) {
toast.error('Unable to cancel scheduled post');
}

Check warning on line 58 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle this exception or don't catch it at all.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0e&open=AZrSfc_WVwhrarSxxa0e&pullRequest=4478
};

const handleEditPost = post => {
setEditingPost({
id: post.id || post._id,
title: post.title || '',
content: post.content || '',
subreddit: post.subreddit || '',
postType: post.postType || 'text',
link: post.link || post.url || '',
scheduledAt: post.scheduledAt || post.scheduled_at || '',
});
};

// called by SubmitPost after successful update
const handleEditedSaved = () => {
setEditingPost(null);
fetchScheduledPosts();
};

const handleEditClose = () => setEditingPost(null);

const handleEditChange = (field, value) => {

Check warning on line 81 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "handleEditChange".

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0g&open=AZrSfc_WVwhrarSxxa0g&pullRequest=4478

Check warning on line 81 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'handleEditChange' variable.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0f&open=AZrSfc_WVwhrarSxxa0f&pullRequest=4478
setEditingPost(prev => ({ ...prev, [field]: value }));
};

const toggle = () => setModal(!modal);

Check warning on line 85 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'toggle' variable.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0h&open=AZrSfc_WVwhrarSxxa0h&pullRequest=4478

Check warning on line 85 in src/components/Announcements/platforms/reddit/Scheduled.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "toggle".

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZrSfc_WVwhrarSxxa0i&open=AZrSfc_WVwhrarSxxa0i&pullRequest=4478

return (
<>
{/* render SubmitPost as overlay/modal when editingPost is set */}
<Modal isOpen={!!editingPost} onClose={handleEditClose} size="xl">
{editingPost && (
<SubmitPost
initialData={editingPost}
onSaved={handleEditedSaved}
onCancelEdit={handleEditClose}
/>
)}
<ModalFooter>
<Button color="secondary" onClick={handleEditClose}>
Close
</Button>
</ModalFooter>
</Modal>
<div>
<p>
<strong>Scheduled Posts for Reddit</strong>
</p>
<ul style={{ listStyle: 'none', padding: 0 }}>
{posts.map(p => (
<li
key={p._id}
style={{
border: '1px solid #e0e0e0',
borderRadius: 6,
padding: '12px',
marginBottom: '8px',
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '0.5rem' }}>
<div>
{p.scheduled_at ? formatSchedule(p.scheduled_at) : 'No Scheduled time'}
{` - ${p.title}`}
</div>
<div style={{ marginTop: 8 }}>
<button
type="button"
onClick={() => handleCancelPost(p.id || p._id)}
style={{
background: '#6c757d',
color: 'white',
border: 'none',
padding: '6px 10px',
marginRight: '10px',
cursor: 'pointer',
}}
>
Cancel
</button>
<button
type="button"
onClick={() => handleEditPost(p)}
style={{
background: '#6c757d',
color: 'white',
border: 'none',
padding: '6px 10px',
cursor: 'pointer',
}}
>
Edit
</button>
</div>
</div>
</li>
))}
</ul>
</div>
</>
);
}
Loading
Loading