diff --git a/epoch_backend/persistence/epoch/epoch_post_persistence.py b/epoch_backend/persistence/epoch/epoch_post_persistence.py index 9241064..3f687cb 100644 --- a/epoch_backend/persistence/epoch/epoch_post_persistence.py +++ b/epoch_backend/persistence/epoch/epoch_post_persistence.py @@ -111,12 +111,11 @@ def get_all_user_posts(self, user_id: int): def get_all_hashtag_posts(self, hashtag: str): connection = get_db_connection() cursor = connection.cursor() - hashtag_pattern = f"%{hashtag}%" - cursor.execute("SELECT * FROM posts WHERE caption LIKE %s", (hashtag_pattern,)) + hashtag_pattern = f"% {hashtag} %" + cursor.execute("SELECT * FROM posts WHERE CONCAT(' ', caption, ' ') LIKE %s", (hashtag_pattern,)) posts = cursor.fetchall() posts_media = get_posts_media(posts) posts_users_info = get_posts_users_info(posts) - all_posts = [] for i in range(len(posts)): diff --git a/epoch_frontend/src/modules/Post.js b/epoch_frontend/src/modules/Post.js index ce708bf..43be498 100644 --- a/epoch_frontend/src/modules/Post.js +++ b/epoch_frontend/src/modules/Post.js @@ -13,6 +13,7 @@ import ArrowCircleDownSharpIcon from '@mui/icons-material/ArrowCircleDownSharp'; import {favoritePost, removeFavoritePost, votePost, removeVotePost} from "../services/post"; import ForumOutlinedIcon from '@mui/icons-material/ForumOutlined'; import PopupUserList from "./PopupUserList"; +import {animated, useSpring} from "react-spring"; export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isInFavorites}) { @@ -52,7 +53,18 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI const [voteByUsernameList, setVoteByUsernameList] = useState((post && post.votes_by_usernames) ? post.votes_by_usernames : []); const [showFavoritedByList, setShowFavoritedByList] = useState(false); const [showVoteByList, setShowVoteByList] = useState(false); - + const [showDeletePostPopup, setShowDeletePostPopup] = useState(false); + const [deletePostError, setDeletePostError] = useState(false); + const [deletePostErrorPrompt, setDeletePostErrorPrompt] = useState(''); + const {transform: inTransformDelete} = useSpring({ + transform: `translateY(${showDeletePostPopup ? 0 : 100}vh)`, + config: {duration: 300}, + }); + + const {transform: outTransformDelete} = useSpring({ + transform: `translateY(${showDeletePostPopup ? 0 : -100}vh)`, + config: {duration: 300}, + }); useEffect(() => { let postTime = new Date(post.created_at) @@ -86,7 +98,6 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI setEditing(false); } }, [showPostPopup]); - const handleProfilePhotoClick = (imageUrl) => { setOverlayImageUrl(imageUrl); setShowOverlay(true); @@ -190,24 +201,27 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI } const onDeletePost = (postId, userId) => { - setError(true); - setErrorMessage('Deleting post...'); + + setDeletePostError(true); + setDeletePostErrorPrompt('Deleting post...'); + deletePost(postId, userId) .then(() => { - setError(false); - setErrorMessage(''); + setDeletePostError(false); + setDeletePostErrorPrompt(''); + setShowDeletePostPopup(false); setPostAdmin(false); - setError(false); - setErrorMessage(''); setDeleted(true); setRefreshFeed(true); }) .catch((error) => { - setError(true); - setErrorMessage(error); + setShowDeletePostPopup(true); + setDeletePostError(true); + setDeletePostErrorPrompt(error); + setTimeout(() => { - setError(false); - setErrorMessage(''); + setDeletePostError(false); + setDeletePostErrorPrompt(''); }, 5000); }); } @@ -225,8 +239,6 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI setShowPostPopup(true); } - - const getReleaseFormat = () => { let date = new Date(post.release); date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())); @@ -248,11 +260,11 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI const diff = date - now; const diffInSeconds = Math.floor(diff / 1000); if (diffInSeconds < 60) { - return "In " + Math.floor(diffInSeconds) + " seconds"; + return "In " + Math.floor(diffInSeconds) + (Math.floor(diffInSeconds) > 1 ? " seconds" : " second"); } if (diffInSeconds < 3600) { - return "In " + Math.floor(diffInSeconds / 60) + " minutes"; + return "In " + Math.floor(diffInSeconds / 60) + (Math.floor(diffInSeconds / 60) > 1 ? " minutes" : " minute"); } if (diffInSeconds < 86400) { @@ -267,7 +279,7 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI } if (diffInSeconds < 3600) { - return Math.floor(diffInSeconds / 60) + " minutes ago"; + return Math.floor(diffInSeconds / 60) + (Math.floor(diffInSeconds / 60) > 1 ? " minutes ago" : " minute ago"); } if (diffInSeconds < 86400) { @@ -518,7 +530,6 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI }, [post.votes, postViewer]); - useEffect(() => { if (favorited) { @@ -553,6 +564,7 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI } }, [vote, voteByUsernameList, postViewer, post, voteResult]); + return (
@@ -579,7 +591,7 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI }}>)} {(postViewer && postAdmin && !editing) && ( { - onDeletePost(post.post_id, postViewer.id); + setShowDeletePostPopup(true); }}>)}
@@ -587,24 +599,24 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI
{post.caption && post.caption.length > 0 && ( -

- {(showFullCaption && post.caption) ? renderCaptionWithHashtags(post.caption) : ( - <> - {renderCaptionWithHashtags(truncatedCaption)} - +

+ {(showFullCaption && post.caption) ? renderCaptionWithHashtags(post.caption) : ( + <> + {renderCaptionWithHashtags(truncatedCaption)} + See more - - )} - {(showFullCaption && post.caption && post.caption.length >= captionCharLimit) && ( - <> - {' '} - + + )} + {(showFullCaption && post.caption && post.caption.length >= captionCharLimit) && ( + <> + {' '} + See less - - )} -

+ + )} +

)} {(post.file && showFullCaption) &&
{postViewer && (
- { - if(vote === 1) - { - onVotePost(post.post_id, postViewer.id, vote, 'removeUpVote'); - } - else - { - onVotePost(post.post_id, postViewer.id, vote, 'upVote'); - } - }}> + { + if (vote === 1) { + onVotePost(post.post_id, postViewer.id, vote, 'removeUpVote'); + } else { + onVotePost(post.post_id, postViewer.id, vote, 'upVote'); + } + }}> - { - if(vote === -1) - { - onVotePost(post.post_id, postViewer.id, vote, 'removeDownVote'); - } - else - { - onVotePost(post.post_id, postViewer.id, vote, 'downVote'); - } - }}> + { + if (vote === -1) { + onVotePost(post.post_id, postViewer.id, vote, 'removeDownVote'); + } else { + onVotePost(post.post_id, postViewer.id, vote, 'downVote'); + } + }}>
)} {((!showCommentsSection) && postViewer) && ( - + )} - + {postViewer && (
- toggleFavorite()}> + toggleFavorite()}>
{(post.file) ? - (showPostPopup && fileBlob && postViewer && postAdmin) && () - : - (showPostPopup && postViewer && postAdmin) && () + (showPostPopup && fileBlob && postViewer && postAdmin) && ( + ) + : + (showPostPopup && postViewer && postAdmin) && ( + ) } {showOverlay && (
@@ -674,25 +695,62 @@ export default function Post({post, postViewer, refreshFeed, setRefreshFeed, isI )}
- - + + + - {(post.file) ? (showPostPopup && fileBlob && postViewer && postAdmin) && ( ) : (showPostPopup && postViewer && postAdmin) && ( ) } + + +
setShowDeletePostPopup(false)}>
+ +
+

Are you sure you want to delete this post?

+ {deletePostError &&

{deletePostErrorPrompt}

} + +
+ + +
+
+
); } diff --git a/epoch_frontend/src/pages/profile.js b/epoch_frontend/src/pages/profile.js index e6c63e1..29cd42f 100644 --- a/epoch_frontend/src/pages/profile.js +++ b/epoch_frontend/src/pages/profile.js @@ -50,7 +50,6 @@ function Profile() { const [deleteAccountErrorPrompt, setDeleteAccountErrorPrompt] = useState(""); const [deletingAccount, setDeletingAccount] = useState(false); - const {transform: inTransformDelete} = useSpring({ transform: `translateY(${showDeleteAccountPopup ? 0 : 100}vh)`, config: {duration: 300}, diff --git a/epoch_frontend/src/styles/NavBar.css b/epoch_frontend/src/styles/NavBar.css index fcf40f9..a685c93 100644 --- a/epoch_frontend/src/styles/NavBar.css +++ b/epoch_frontend/src/styles/NavBar.css @@ -11,7 +11,7 @@ padding: 10px 20px; height: 3rem; border-bottom: 1px solid lightgrey; - z-index: 9000; + z-index: 1000; } .navbar .left { diff --git a/epoch_frontend/src/styles/Post.css b/epoch_frontend/src/styles/Post.css index bd8e864..923287b 100644 --- a/epoch_frontend/src/styles/Post.css +++ b/epoch_frontend/src/styles/Post.css @@ -323,6 +323,75 @@ cursor: pointer; } +.delete-post-overlay { + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 0.5rem; + z-index: 1000; +} + +.delete-post-modal { + position: absolute; + border-radius: 1rem; + padding: 1rem; + background-color: white; + width: 30%; + max-height: 50%; + z-index: 1000; + overflow-y: auto; +} + +.delete-post-header { + +} + +.delete-post-error { + color: red; + font-weight: bold; + margin-bottom: 1rem; +} + +.delete-post-buttons-wrapper { + display: flex; + justify-content: center; + margin-bottom: 1rem; + gap: 1rem; +} + +.delete-post-button-no { + padding: 0.5rem; + border-radius: 10%; + cursor: pointer; + background-color: white; + color: green; + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5); + border: none; +} + +.delete-post-button-yes { + padding: 0.5rem; + border-radius: 10%; + cursor: pointer; + background-color: white; + color: red; + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5); + border: none; +} + +.delete-post-button-no:hover { + background-color: green; + color: white; +} + +.delete-post-button-yes:hover { + background-color: red; + color: white; +} + @media (max-width: 768px) { .post-header { @@ -435,6 +504,10 @@ justify-content: center; gap: 0.5rem; } + + .delete-post-modal { + width: 90%; + } } @media (prefers-color-scheme: dark) { @@ -495,4 +568,9 @@ color: white; } + .delete-post-modal { + background-color: #333333; + color: white; + } + }