From 9d08b6371b1bf7fc693d796c3c36d51688129d52 Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 13:13:12 -0600 Subject: [PATCH 1/7] will return to sidebaritem --- src/features/posts/Feed.js | 36 +++++++++++++++-------------- src/features/posts/Post.js | 2 +- src/features/reddit/redditSlice.js | 17 +++++++++++--- src/features/sidebar/Sidebar.js | 7 ++---- src/features/sidebar/SidebarItem.js | 25 ++++++++++---------- 5 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/features/posts/Feed.js b/src/features/posts/Feed.js index 0fd42a7..126419e 100644 --- a/src/features/posts/Feed.js +++ b/src/features/posts/Feed.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { fetchBySub } from "./postsSlice"; +import { fetchBySub, selectPosts } from "./postsSlice"; import { selectAllSubs } from "../reddit/redditSlice"; import { useSelector, useDispatch } from "react-redux"; import { updatePosts } from "./postsSlice"; @@ -11,17 +11,21 @@ export default function Feed() { const [data, setData] = useState(null); // Receives data from getPosts and them maps it onto Post components const [feed, setFeed] = useState(null); // Expects to receive an array of Post components mapped with data from fetchBySub const dispatch = useDispatch(); - - const subs = useSelector(selectAllSubs); // Selects subreddits from redditSlice - const subArray = Object.values(subs); // Places the values within an array + const posts = useSelector(selectPosts); + const subs = useSelector(selectAllSubs); // Selects subreddits from redditSlice - - useEffect(() => { // this useEffect loop pulls the endpoints from the selected subreddits and stores them as an array in "endpoints" + useEffect(() => { // this useEffect loop pulls the endpoints from the selected subreddits and stores them as an array in "endpoints" + const subArray = Object.values(subs); // extracts values from selected subs + const prepareData = () => { let myEndpoints = []; - for (let sub of subArray) { - myEndpoints.push(sub.access); + for (let sub of subArray) { // this loop filters subs which are set to isSelected in state + if (sub.isSelected) { + myEndpoints.push(sub.access); + } else { + continue; + } } setEndpoints(myEndpoints); } @@ -38,14 +42,14 @@ export default function Feed() { const getPosts = async(arr) => { if (endpoints) { - const mappedResults = arr.map(each => dispatch(fetchBySub(each))); + const mappedResults = arr.map(each => dispatch(fetchBySub(each))); // maps each endpoint into a call to dispatch fetchBySub return Promise.all([ - ...mappedResults - ]).then((results) => setData(results)); // data is now set to an object (returned promise) + ...mappedResults // ...then promises each of the calls within this array, + ]).then((results) => setData(results)); // and stores the returned promises in state as data } } - getPosts(endpoints); // calls this logic on the current value of endpoints + getPosts(endpoints); return () => { isActive = false; @@ -82,8 +86,6 @@ export default function Feed() { let sortedPosts = extractedPosts.sort(comparePosts); // implements sorting function - console.log(sortedPosts); - let newFeed = sortedPosts.map((post) => { return ( ); }) - - setFeed(newFeed); // stores this array of posts in feed + // dispatch(updatePosts(newFeed)); // stores current feed in state of postsSlice + setFeed(newFeed); } } @@ -115,7 +117,7 @@ export default function Feed() { isActive = false; } - }, [data, setFeed]); + }, [data, setFeed, dispatch]); return ( <> diff --git a/src/features/posts/Post.js b/src/features/posts/Post.js index a5a1ef0..49c394d 100644 --- a/src/features/posts/Post.js +++ b/src/features/posts/Post.js @@ -33,7 +33,7 @@ export default function Post({title,author,subreddit,ups,comments,time,key,media return ( <> -
+
{title ? {title} diff --git a/src/features/reddit/redditSlice.js b/src/features/reddit/redditSlice.js index adc9cba..e92887a 100644 --- a/src/features/reddit/redditSlice.js +++ b/src/features/reddit/redditSlice.js @@ -66,12 +66,22 @@ export const redditSlice = createSlice({ reducers: { updateSubVisibility(state,action) { // receives a subreddit name as action.payload state.subreddits[action.payload].isSelected = !state.subreddits[action.payload].isSelected; - } + }, + getActiveSubs(state,action) { + let activeSubs = []; + let allSubs = state.redditSlice.subreddits; + for (let sub in allSubs) { + if (sub.isSelected) { + activeSubs.push(sub); + } else { + continue; + } + } + }, }, extraReducers: {}, }); -export default redditSlice.reducer; export const selectAllSubs = state => state.redditSlice.subreddits; export const selectActiveSubs = state => { let activeSubs = []; @@ -84,4 +94,5 @@ export const selectActiveSubs = state => { } return activeSubs; } -export const { updateSubVisibility } = redditSlice.actions; \ No newline at end of file +export const { updateSubVisibility, getActiveSubs } = redditSlice.actions; +export default redditSlice.reducer; \ No newline at end of file diff --git a/src/features/sidebar/Sidebar.js b/src/features/sidebar/Sidebar.js index 0bb5e8e..7a69d44 100644 --- a/src/features/sidebar/Sidebar.js +++ b/src/features/sidebar/Sidebar.js @@ -1,6 +1,7 @@ import React, { useRef, useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { selectAllSubs } from "../reddit/redditSlice"; +import { v4 } from 'uuid'; import SidebarItem from "./SidebarItem"; import './Sidebar.css'; @@ -26,17 +27,13 @@ export default function Sidebar({isCollapsed}) { } } - const handleClick = (e) => { - - } - return ( <>
{/* Is collapsed is passed from the parent component, and is mutable within the navbar */} { subs.map((sub) => { // Maps each sub to its own line within the sidebar, along with a button that toggles its "isSelected" property return ( - + ) }) } diff --git a/src/features/sidebar/SidebarItem.js b/src/features/sidebar/SidebarItem.js index 3d0ba87..a9658c5 100644 --- a/src/features/sidebar/SidebarItem.js +++ b/src/features/sidebar/SidebarItem.js @@ -1,23 +1,24 @@ -import React, { useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { updateSubVisibility, selectAllSubs } from "../reddit/redditSlice"; +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { updateSubVisibility } from "../reddit/redditSlice"; -export default function SidebarItem({sub, isChecked}) { - const [checked, setChecked] = useState(isChecked); +export default function SidebarItem({sub}) { + const [visible, setVisible] = useState('hide'); const dispatch = useDispatch(); - const allSubs = useSelector(selectAllSubs); const handleClick = () => { - setChecked(!checked); - dispatch(updateSubVisibility(sub)); + if (visible === 'hide') { + setVisible('show'); + } else if (visible === 'show') { + setVisible('hide'); + } } - console.log(allSubs); - return (
- - + {/* */} + +
); } \ No newline at end of file From 9648976bc9b30b9fc34b22a9b8589c4b9bce6ce5 Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 13:54:29 -0600 Subject: [PATCH 2/7] about to checkout --- src/features/posts/Feed.js | 2 ++ src/features/posts/Post.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/features/posts/Feed.js b/src/features/posts/Feed.js index 126419e..5eb696b 100644 --- a/src/features/posts/Feed.js +++ b/src/features/posts/Feed.js @@ -74,6 +74,8 @@ export default function Feed() { } }; + console.log(extractedPosts); + const comparePosts = (a,b) => { // sorting function: compares time posted within each object in array if (a.data.created_utc > b.data.created_utc) { return -1; diff --git a/src/features/posts/Post.js b/src/features/posts/Post.js index 49c394d..bd8e3ed 100644 --- a/src/features/posts/Post.js +++ b/src/features/posts/Post.js @@ -59,6 +59,9 @@ export default function Post({title,author,subreddit,ups,comments,time,key,media

{comments ? comments : 'no'} comments

+
+
+
); From ff673fd7e23b40d1ffac030b8ae35384d9d43fb4 Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 13:55:31 -0600 Subject: [PATCH 3/7] beginning to define fetchComments --- src/features/posts/postsSlice.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/features/posts/postsSlice.js b/src/features/posts/postsSlice.js index 2ef0e54..d23b57a 100644 --- a/src/features/posts/postsSlice.js +++ b/src/features/posts/postsSlice.js @@ -15,6 +15,17 @@ export const fetchBySub = createAsyncThunk( } ); +export const fetchComments = createAsyncThunk( + 'posts/fetchComments', + async(permalink) => { + try { + // stuff + } catch(e) { + console.log(e); + } + } +) + export const postsSlice = createSlice({ name: 'posts', initialState: { From 669e8d301732468b536afa3e812e68e1881dfa51 Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 13:57:09 -0600 Subject: [PATCH 4/7] method defined. now need to integrate --- src/features/posts/postsSlice.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/features/posts/postsSlice.js b/src/features/posts/postsSlice.js index d23b57a..99bc23d 100644 --- a/src/features/posts/postsSlice.js +++ b/src/features/posts/postsSlice.js @@ -19,7 +19,11 @@ export const fetchComments = createAsyncThunk( 'posts/fetchComments', async(permalink) => { try { - // stuff + const myRequest = new Request(`${permalink}.json`); + let response = await fetch(myRequest); + let json = await response.json(); + let postData = json.data.children; + return postData; } catch(e) { console.log(e); } From bb8de386bb5a108a09b7afd6ac8e2938e617b1cb Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 14:06:18 -0600 Subject: [PATCH 5/7] Post.js refactored, imports consolidated --- src/features/posts/Feed.js | 11 +---------- src/features/posts/Post.js | 15 ++++++++++++++- src/features/posts/postsSlice.js | 5 +++-- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/features/posts/Feed.js b/src/features/posts/Feed.js index 5eb696b..528ffef 100644 --- a/src/features/posts/Feed.js +++ b/src/features/posts/Feed.js @@ -91,17 +91,8 @@ export default function Feed() { let newFeed = sortedPosts.map((post) => { return ( ); }) diff --git a/src/features/posts/Post.js b/src/features/posts/Post.js index bd8e3ed..663096d 100644 --- a/src/features/posts/Post.js +++ b/src/features/posts/Post.js @@ -1,10 +1,23 @@ import React, { useState, useEffect } from "react"; import './Post.css'; -export default function Post({title,author,subreddit,ups,comments,time,key,media,permalink,selftext,video}) { +export default function Post({data, key}) { + + let title = data.title; // imports from data passed in from Feed + let author = data.author; + let subreddit = data.subreddit; + let ups = data.ups; + let comments = data.num_comments; + let time = data.created_utc; + let media = data.post_hint === 'image' && data.url; + let permalink = data.permalink; + let selftext= data.selftext; + let video = data.is_video ? data.media.reddit_video.fallback_url : null; // to do: handle media edge cases, especially video + const limit = 300; const [body, setBody] = useState(selftext); + const postDate = new Date(time * 1000); // handles conversion from unix timestamp to local time and date strings const localTime = postDate.toLocaleTimeString(); const localDate = postDate.toLocaleDateString(); diff --git a/src/features/posts/postsSlice.js b/src/features/posts/postsSlice.js index 99bc23d..6fbacdf 100644 --- a/src/features/posts/postsSlice.js +++ b/src/features/posts/postsSlice.js @@ -28,7 +28,7 @@ export const fetchComments = createAsyncThunk( console.log(e); } } -) +); export const postsSlice = createSlice({ name: 'posts', @@ -67,4 +67,5 @@ export const postsSlice = createSlice({ export default postsSlice.reducer; export const selectPosts = state => state.postsSlice.posts; export const { filterPosts, updatePosts } = postsSlice.actions; -// exports also includes fetchBySub (takes argument of a sub) \ No newline at end of file +// exports also includes fetchBySub (takes argument of a sub) +// exports also includes fetchComments (takes argument of a post permalink) \ No newline at end of file From f9913a0dd0859434db6e0592e3424e8491a846ec Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 15:05:21 -0600 Subject: [PATCH 6/7] added extra reducers for fetchComments --- src/features/discussion/Discussion.js | 35 +++++++++++++++++++++++++ src/features/posts/Post.css | 10 ++++++++ src/features/posts/Post.js | 37 ++++++++++++++++++++++----- src/features/posts/postsSlice.js | 22 +++++++++++++--- 4 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 src/features/discussion/Discussion.js diff --git a/src/features/discussion/Discussion.js b/src/features/discussion/Discussion.js new file mode 100644 index 0000000..fb24b07 --- /dev/null +++ b/src/features/discussion/Discussion.js @@ -0,0 +1,35 @@ +import React, { useState, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { fetchComments } from '../posts/postsSlice'; + +export default function Discussion({permalink, isVisible}) { + const [thread, setThread] = useState(null); + const [data, setData] = useState(null); + const dispatch = useDispatch(); + + const formattedLink = permalink.substring(0,(permalink.length-1)); + + useEffect(() => { + let isActive = true; + if (isActive) { + if (isVisible === 'hide ') { + setData(dispatch(fetchComments(formattedLink))); + } + } + return () => { + isActive = false; + } + }, [isVisible, thread, formattedLink, dispatch]); + + useEffect(() => { + if (data) { + console.log(data); + } + }, [data, thread]); + + return ( +
+ {thread} +
+ ) +} \ No newline at end of file diff --git a/src/features/posts/Post.css b/src/features/posts/Post.css index e603e2e..6c4b8a3 100644 --- a/src/features/posts/Post.css +++ b/src/features/posts/Post.css @@ -32,4 +32,14 @@ img, video { .post-text { font-size: 1.2rem; +} + +.comments-visible { + display: flex; + flex-direction: column; + width: 100%; +} + +.comments-hidden { + display: none; } \ No newline at end of file diff --git a/src/features/posts/Post.js b/src/features/posts/Post.js index 663096d..2fb14ea 100644 --- a/src/features/posts/Post.js +++ b/src/features/posts/Post.js @@ -1,8 +1,11 @@ import React, { useState, useEffect } from "react"; +import { useDispatch } from "react-redux"; +import { fetchComments } from "./postsSlice"; +import Discussion from "../discussion/Discussion"; import './Post.css'; export default function Post({data, key}) { - + let title = data.title; // imports from data passed in from Feed let author = data.author; let subreddit = data.subreddit; @@ -11,16 +14,38 @@ export default function Post({data, key}) { let time = data.created_utc; let media = data.post_hint === 'image' && data.url; let permalink = data.permalink; - let selftext= data.selftext; + let selftext = data.selftext; let video = data.is_video ? data.media.reddit_video.fallback_url : null; // to do: handle media edge cases, especially video const limit = 300; const [body, setBody] = useState(selftext); - + const [visible, setVisible] = useState('show '); + const [commentStyle, setCommentStyle] = useState('comments-hidden'); const postDate = new Date(time * 1000); // handles conversion from unix timestamp to local time and date strings const localTime = postDate.toLocaleTimeString(); const localDate = postDate.toLocaleDateString(); + + /********* + * Handles hiding/showing comment threads. When this is clicked, the thread is fetched, parsed, and displayed. + * Doing so for all posts at once would reduce rendering times. + *********/ + + const handleClick = () => { + if (visible === 'hide ') { + setVisible('show '); + setCommentStyle('comments-hidden'); + } else if (visible === 'show ') { + setVisible('hide '); + setCommentStyle('comments-visible'); + } else { + throw new Error('error in button handling in Post.js'); + } + } + + /********* + * Functions below to handle post body so as not to clog visual space + *********/ useEffect(() => { if (selftext.length === 0) { // in the case that the post body is empty, it does not include an ellipsis on mouseout @@ -40,7 +65,6 @@ export default function Post({data, key}) { if (selftext.length === 0) { // ...and then doesn't add it in after a mouseover/out return; } - setBody(selftext.substring(0,limit) + '...'); } @@ -69,10 +93,11 @@ export default function Post({data, key}) {

{author ? 'u/' + author : 'u/username'}

posted at {time ? (localTime + ' on ' + localDate) : '...?'}

-

{comments ? comments : 'no'} comments

+
-
+
+
diff --git a/src/features/posts/postsSlice.js b/src/features/posts/postsSlice.js index 6fbacdf..1918fce 100644 --- a/src/features/posts/postsSlice.js +++ b/src/features/posts/postsSlice.js @@ -19,11 +19,10 @@ export const fetchComments = createAsyncThunk( 'posts/fetchComments', async(permalink) => { try { - const myRequest = new Request(`${permalink}.json`); + const myRequest = new Request(`https://www.reddit.com${permalink}.json`); let response = await fetch(myRequest); - let json = await response.json(); - let postData = json.data.children; - return postData; + let postData = await response.json(); // returns an array of two objects, the first containing data about + return postData; // the post, the second containing data about the discussion thread } catch(e) { console.log(e); } @@ -34,6 +33,7 @@ export const postsSlice = createSlice({ name: 'posts', initialState: { posts: [], + activeComments: [], requestsPending: false, requestDenied: false, }, @@ -61,6 +61,20 @@ export const postsSlice = createSlice({ state.posts.push(sub); // nesting arrays within the state's posts } }) + + builder.addCase(fetchComments.pending, (state,action) => { + state.requestsPending = true; + state.requestDenied = false; + }) + builder.addCase(fetchComments.rejected, (state,action) => { + state.requestsPending = false; + state.requestDenied = true; + }) + builder.addCase(fetchComments.fulfilled, (state,action) => { + state.requestsPending = false; + state.requestDenied = false; + state.activeComments.push(action.payload); + }) } }); From f3a9860e9e11783d94e0f7e1f29e4db72ee188f2 Mon Sep 17 00:00:00 2001 From: Mikayla Dobson Date: Sat, 29 Jan 2022 16:54:47 -0600 Subject: [PATCH 7/7] renders and displays comments for postsf --- src/features/discussion/Discussion.js | 29 +++++++++++++++++++++++---- src/features/posts/postsSlice.js | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/features/discussion/Discussion.js b/src/features/discussion/Discussion.js index fb24b07..af02f65 100644 --- a/src/features/discussion/Discussion.js +++ b/src/features/discussion/Discussion.js @@ -1,19 +1,24 @@ import React, { useState, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { fetchComments } from '../posts/postsSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import { unwrapResult } from '@reduxjs/toolkit'; +import { fetchComments, isPending } from '../posts/postsSlice'; export default function Discussion({permalink, isVisible}) { const [thread, setThread] = useState(null); const [data, setData] = useState(null); const dispatch = useDispatch(); + const isLoading = useSelector(isPending); + const formattedLink = permalink.substring(0,(permalink.length-1)); useEffect(() => { let isActive = true; if (isActive) { if (isVisible === 'hide ') { - setData(dispatch(fetchComments(formattedLink))); + dispatch(fetchComments(formattedLink)) + .then(unwrapResult) + .then((result) => setData(result)); } } return () => { @@ -23,7 +28,23 @@ export default function Discussion({permalink, isVisible}) { useEffect(() => { if (data) { - console.log(data); + let commentData = data[1]; + console.log(commentData); + + let commentArray = commentData.data.children; + console.log(commentArray); + + let toExport = []; + for (let comment of commentArray) { + toExport.push( + <> +

{'u/' + comment.data.author}

+

{comment.data.body}

+ + ); + } + + setThread(toExport); } }, [data, thread]); diff --git a/src/features/posts/postsSlice.js b/src/features/posts/postsSlice.js index 1918fce..0c57117 100644 --- a/src/features/posts/postsSlice.js +++ b/src/features/posts/postsSlice.js @@ -80,6 +80,7 @@ export const postsSlice = createSlice({ export default postsSlice.reducer; export const selectPosts = state => state.postsSlice.posts; +export const isPending = state => state.postsSlice.requestsPending; export const { filterPosts, updatePosts } = postsSlice.actions; // exports also includes fetchBySub (takes argument of a sub) // exports also includes fetchComments (takes argument of a post permalink) \ No newline at end of file