Subreddit filtering #4
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { fetchBySub } from "./postsSlice";
|
||||
import { selectAllSubs } from "../reddit/redditSlice";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { updatePosts } from "./postsSlice";
|
||||
import { v4 } from "uuid";
|
||||
import Post from "./Post";
|
||||
|
||||
@@ -28,7 +29,7 @@ export default function Feed() {
|
||||
if (subArray) {
|
||||
prepareData();
|
||||
}
|
||||
}, [setEndpoints]);
|
||||
}, [setEndpoints, subs]);
|
||||
|
||||
|
||||
|
||||
@@ -69,9 +70,21 @@ export default function Feed() {
|
||||
}
|
||||
};
|
||||
|
||||
extractedPosts = extractedPosts.sort((x,y) => x.created_utc > y.created_utc); // sorts posts by sort time (to do: fix this)
|
||||
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;
|
||||
} else if (a.data.created_utc < b.data.created_utc) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let newFeed = extractedPosts.map((post) => {
|
||||
let sortedPosts = extractedPosts.sort(comparePosts); // implements sorting function
|
||||
|
||||
console.log(sortedPosts);
|
||||
|
||||
let newFeed = sortedPosts.map((post) => {
|
||||
return (
|
||||
<Post
|
||||
title={post.data.title} // each variable passed in as props
|
||||
@@ -81,10 +94,10 @@ export default function Feed() {
|
||||
comments={post.data.num_comments}
|
||||
time={post.data.created_utc}
|
||||
key={v4()}
|
||||
media={post.data.post_hint === 'image' && post.url}
|
||||
media={post.data.post_hint === 'image' && post.data.url}
|
||||
permalink={post.data.permalink}
|
||||
selftext={post.data.selftext}
|
||||
video={post.data.is_video ? post.data.media.reddit_video.fallback_url : null}
|
||||
video={post.data.is_video ? post.data.media.reddit_video.fallback_url : null} // to do: handle media edge cases, especially video
|
||||
/>
|
||||
);
|
||||
})
|
||||
@@ -94,13 +107,15 @@ export default function Feed() {
|
||||
|
||||
}
|
||||
|
||||
mapPosts();
|
||||
if (isActive) {
|
||||
mapPosts();
|
||||
}
|
||||
|
||||
return () => {
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
}, [data, setFeed])
|
||||
}, [data, setFeed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
height: 15rem;
|
||||
}
|
||||
|
||||
a {
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ img, video {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
|
||||
@@ -4,42 +4,61 @@ import './Post.css';
|
||||
export default function Post({title,author,subreddit,ups,comments,time,key,media,permalink,selftext,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();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (selftext.length === 0) { // in the case that the post body is empty, it does not include an ellipsis on mouseout
|
||||
// return;
|
||||
// } else if (selftext.length > limit) {
|
||||
// setBody(selftext.substring(0,limit) + '...');
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// }, [setBody, selftext]);
|
||||
useEffect(() => {
|
||||
if (selftext.length === 0) { // in the case that the post body is empty, it does not include an ellipsis on mouseout
|
||||
return;
|
||||
} else if (selftext.length > limit) {
|
||||
setBody(selftext.substring(0,limit) + '...');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}, [setBody, selftext]);
|
||||
|
||||
// const handleHover = () => {
|
||||
// setBody(selftext);
|
||||
// }
|
||||
const handleHover = () => {
|
||||
setBody(selftext);
|
||||
}
|
||||
|
||||
// const handleMouseOut = () => {
|
||||
// if (selftext.length === 0) { // ...and then doesn't add it in after a mouseover/out
|
||||
// return;
|
||||
// }
|
||||
const handleMouseOut = () => {
|
||||
if (selftext.length === 0) { // ...and then doesn't add it in after a mouseover/out
|
||||
return;
|
||||
}
|
||||
|
||||
// setBody(selftext.substring(0,limit) + '...');
|
||||
// }
|
||||
setBody(selftext.substring(0,limit) + '...');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="post-body">
|
||||
<a className="title" href={`https://reddit.com${permalink}`}>{title ? title : 'title'}</a>
|
||||
{media ? <img alt={title} src={media} /> : ''}
|
||||
{video ? <video controls type="video/mp4" src={video}></video> : ''}
|
||||
<p className="post-text">{body}</p>
|
||||
|
||||
{title ?
|
||||
<a className="title" href={`https://reddit.com${permalink}`}>{title}</a>
|
||||
: <p>[untitled]</p>}
|
||||
|
||||
{media ? <img alt={title} src={media} /> : ''}
|
||||
|
||||
{video ?
|
||||
<video controls type="video/mp4" src={video}></video>
|
||||
: ''}
|
||||
|
||||
{body ?
|
||||
<p onMouseOver={handleHover} onMouseOut={handleMouseOut}>{body}</p>
|
||||
: ''}
|
||||
|
||||
<div className="post-metadata">
|
||||
<p>{subreddit ? 'r/' + subreddit : ''}</p>
|
||||
<a href={`https://www.reddit.com${permalink}`}>
|
||||
{subreddit ? 'r/' + subreddit : ''}
|
||||
</a>
|
||||
|
||||
<p className="user">{author ? 'u/' + author : 'u/username'}</p>
|
||||
<p className="time-posted">posted at {time ? time : '...?'}</p>
|
||||
<p className="time-posted">posted at {time ? (localTime + ' on ' + localDate) : '...?'}</p>
|
||||
<p className="num-comments">{comments ? comments : 'no'} comments</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -52,5 +52,4 @@ 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)
|
||||
// exports also includes fetchFromAll (takes argument of an array of subs)
|
||||
// exports also includes fetchBySub (takes argument of a sub)
|
||||
@@ -64,9 +64,8 @@ export const redditSlice = createSlice({
|
||||
},
|
||||
},
|
||||
reducers: {
|
||||
updateSubVisibility(state,action) {
|
||||
// reads state of buttons in Sidebar component to determine whether each is active
|
||||
// connects with post rendering, filtering out posts belonging to inactive subreddits
|
||||
updateSubVisibility(state,action) { // receives a subreddit name as action.payload
|
||||
state.subreddits[action.payload].isSelected = !state.subreddits[action.payload].isSelected;
|
||||
}
|
||||
},
|
||||
extraReducers: {},
|
||||
@@ -74,4 +73,15 @@ export const redditSlice = createSlice({
|
||||
|
||||
export default redditSlice.reducer;
|
||||
export const selectAllSubs = state => state.redditSlice.subreddits;
|
||||
export const selectActiveSubs = state => {
|
||||
let activeSubs = [];
|
||||
for (let it in state.redditSlice.subreddits) {
|
||||
if (it.isSelected) {
|
||||
activeSubs.push(it);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return activeSubs;
|
||||
}
|
||||
export const { updateSubVisibility } = redditSlice.actions;
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { selectAllSubs } from "../reddit/redditSlice";
|
||||
import SidebarItem from "./SidebarItem";
|
||||
import './Sidebar.css';
|
||||
|
||||
export default function Sidebar({isCollapsed}) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const allSubs = useSelector(selectAllSubs);
|
||||
let arrayOfSubs = Object.keys(allSubs);
|
||||
@@ -24,16 +26,17 @@ export default function Sidebar({isCollapsed}) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleClick = (e) => {
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={isCollapsed ? 'sidebar-hidden' : 'sidebar'}> {/* 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 (
|
||||
<div className="individual-sub">
|
||||
<button className="toggle-sub-active">X</button> {/* This button will dispatch an action to change the state of this specific subreddit */}
|
||||
<p>{sub}</p>
|
||||
</div>
|
||||
<SidebarItem sub={sub} isChecked={true}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
23
src/features/sidebar/SidebarItem.js
Normal file
23
src/features/sidebar/SidebarItem.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import React, { useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { updateSubVisibility, selectAllSubs } from "../reddit/redditSlice";
|
||||
|
||||
export default function SidebarItem({sub, isChecked}) {
|
||||
const [checked, setChecked] = useState(isChecked);
|
||||
const dispatch = useDispatch();
|
||||
const allSubs = useSelector(selectAllSubs);
|
||||
|
||||
const handleClick = () => {
|
||||
setChecked(!checked);
|
||||
dispatch(updateSubVisibility(sub));
|
||||
}
|
||||
|
||||
console.log(allSubs);
|
||||
|
||||
return (
|
||||
<div className="individual-sub">
|
||||
<input type="checkbox" id={sub} htmlFor={sub} checked={checked} onChange={handleClick}></input>
|
||||
<label htmlFor={sub}>{sub}</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user