-
-
Notifications
You must be signed in to change notification settings - Fork 77
Sheetal - reddit auto poster frontend #4478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Changes from all commits
ce3c971
5c928f0
95e00d3
3209370
c70eba1
3dda9e2
e1a5959
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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); | ||
|
|
||
| useEffect(() => { | ||
| async function validateToken() { | ||
| try { | ||
| const res = await axios.get(`${ENDPOINTS.AP_REDDIT_AUTH_TOKEN}`); | ||
| setHasToken(res.data.exists); | ||
| } catch (error) { | ||
| setHasToken(false); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||
| } | ||
| 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> | ||
| ); | ||
| } | ||
| 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; |
| 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
|
||
| const [error, setError] = useState(null); | ||
|
Check warning on line 11 in src/components/Announcements/platforms/reddit/Scheduled.jsx
|
||
| 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
|
||
| 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
|
||
| 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
|
||
| }; | ||
|
|
||
| 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
|
||
| setEditingPost(prev => ({ ...prev, [field]: value })); | ||
| }; | ||
|
|
||
| const toggle = () => setModal(!modal); | ||
|
Check warning on line 85 in src/components/Announcements/platforms/reddit/Scheduled.jsx
|
||
|
|
||
| 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> | ||
| </> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
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.