Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import About from "@/components/community/about/About";
import CommunityHeader from "@/components/community/community-header/CommunityHeader";
import PageContent from "@/components/layout/PageContent";
import Posts from "@/components/posts/Posts";
import useCommunityPermissions from "@/hooks/community/useCommunityPermissions";
import RestrictedCommunityBanner from "@/components/community/RestrictedCommunityBanner";
import { Community } from "@/types/community";
import { Stack } from "@chakra-ui/react";
import { useAtom } from "jotai";
import React, { useEffect } from "react";

Expand Down Expand Up @@ -39,17 +42,23 @@ const CommunityClientPage: React.FC<CommunityPageProps> = ({
? communityStateValue.currentCommunity
: communityData;

const { canView } = useCommunityPermissions(currentCommunity);

return (
<>
<CommunityHeader communityData={currentCommunity} />
<PageContent>
<>
<CreatePostLink />
<Posts communityData={currentCommunity} />
</>
<>
<About communityData={currentCommunity} />
{canView ? (
<>
<CreatePostLink />
<Posts communityData={currentCommunity} />
</>
) : (
<RestrictedCommunityBanner />
)}
</>
<>{canView && <About communityData={currentCommunity} />}</>
</PageContent>
</>
);
Expand Down
78 changes: 41 additions & 37 deletions app/community/[communityId]/comments/[pid]/PostClientPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import usePostDeletion from "@/hooks/posts/usePostDeletion";
import usePostState from "@/hooks/posts/usePostState";
import usePostVote from "@/hooks/posts/usePostVote";
import usePostVoteSync from "@/hooks/posts/usePostVoteSync";
import RestrictedCommunityBanner from "@/components/community/RestrictedCommunityBanner";
import { Community } from "@/types/community";
import { Post } from "@/types/post";
import { Stack } from "@chakra-ui/react";
Expand Down Expand Up @@ -41,7 +42,8 @@ const PostPage: React.FC<PostPageProps> = ({ communityData, postData }) => {
useAtom(communityStateAtom);
const currentCommunity =
communityStateValue.currentCommunity || communityData;
const { isAdmin } = useCommunityPermissions(currentCommunity);
const { isAdmin, canView, canPost } =
useCommunityPermissions(currentCommunity);
const [user] = useAuthState(auth);

useEffect(() => {
Expand All @@ -60,47 +62,49 @@ const PostPage: React.FC<PostPageProps> = ({ communityData, postData }) => {
}
}, [postData, setPostStateValue]);

if (!canView) {
return (
<PageContent>
<RestrictedCommunityBanner />
<></>
</PageContent>
);
}

return (
<PageContent>
{/* Left */}
<>
{!postData && !postStateValue.selectedPost ? (
<PostLoader />
) : (
<>
<Stack gap={3} direction="column">
{postStateValue.selectedPost && (
<PostItem
post={postStateValue.selectedPost}
onVote={onVote}
onDeletePost={onDeletePost}
userVoteValue={
postStateValue.postVotes.find(
(item) => item.postId === postStateValue.selectedPost?.id
)?.voteValue
}
userIsCreator={
user?.uid === postStateValue.selectedPost?.creatorId
}
userIsAdmin={isAdmin}
showCommunityImage={true}
/>
)}

<Comments
user={user as User}
selectedPost={postStateValue.selectedPost}
communityId={postStateValue.selectedPost?.communityId as string}
isCommunityAdmin={isAdmin}
/>
</Stack>
</>
)}
<Stack gap={4}>
{postStateValue.selectedPost && (
<PostItem
post={postStateValue.selectedPost}
onVote={onVote}
onDeletePost={onDeletePost}
userVoteValue={
postStateValue.postVotes.find(
(item) => item.postId === postStateValue.selectedPost!.id
)?.voteValue
}
userIsCreator={
user?.uid === postStateValue.selectedPost.creatorId
}
userIsAdmin={isAdmin}
votingDisabled={!canPost}
/>
)}
<Comments
user={user as User}
selectedPost={postStateValue.selectedPost}
communityId={postStateValue.selectedPost?.communityId as string}
isCommunityAdmin={isAdmin}
/>
</Stack>
</>
<>
<About communityData={communityData} />
</>
{currentCommunity && <About communityData={currentCommunity} />}
{/* Right */}
<></>
</PageContent>
);
};

export default PostPage;
2 changes: 1 addition & 1 deletion app/community/[communityId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getCommunityData } from "@/lib/community/getCommunityData";
import { notFound } from "next/navigation";
import CommunityClientPage from "./CommunityClientPage";
import CommunityClientPage from "./comments/CommunityClientPage";

/**
* Server component that loads community data and renders the client view.
Expand Down
15 changes: 15 additions & 0 deletions app/community/[communityId]/submit/SubmitPostClientPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { Box, Stack, Text } from "@chakra-ui/react";
import { useAtom, useSetAtom } from "jotai";
import React, { useEffect } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import useCommunityPermissions from "@/hooks/community/useCommunityPermissions";
import RestrictedCommunityBanner from "@/components/community/RestrictedCommunityBanner";

type SubmitPostPageProps = {
communityData: Community;
Expand All @@ -37,6 +39,19 @@ const SubmitPostPage: React.FC<SubmitPostPageProps> = ({ communityData }) => {

const currentCommunity =
communityStateValue.currentCommunity || communityData;
const { canPost } = useCommunityPermissions(currentCommunity);

if (!canPost) {
return (
<PageContent>
<RestrictedCommunityBanner
title="Restricted Access"
description="Only subscribers can create posts in this community."
/>
<About communityData={currentCommunity} />
</PageContent>
);
}

return (
<PageContent>
Expand Down
10 changes: 10 additions & 0 deletions components/community/CreatePostLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import React from "react";
import { BsLink45Deg } from "react-icons/bs";
import { IoIosCreate } from "react-icons/io";
import { IoImageOutline } from "react-icons/io5";
import useCommunityState from "@/hooks/community/useCommunityState";
import useCommunityPermissions from "@/hooks/community/useCommunityPermissions";

type CreatePostProps = {};

Expand All @@ -14,6 +16,14 @@ type CreatePostProps = {};
*/
const CreatePostLink: React.FC<CreatePostProps> = () => {
const { onClick } = useCallCreatePost(); // hook for creating a new post
const { communityStateValue } = useCommunityState();
const { canPost } = useCommunityPermissions(
communityStateValue.currentCommunity
);

if (communityStateValue.currentCommunity && !canPost) {
return null;
}

return (
<Flex
Expand Down
34 changes: 34 additions & 0 deletions components/community/RestrictedCommunityBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Flex, Icon, Text } from "@chakra-ui/react";
import React from "react";
import { FaLock } from "react-icons/fa";

type RestrictedCommunityBannerProps = {
title?: string;
description?: string;
};

const RestrictedCommunityBanner: React.FC<RestrictedCommunityBannerProps> = ({
title = "This community is private",
description = "Posts are only shown to subscribers.",
}) => {
return (
<Flex
direction="column"
justify="center"
align="center"
border="1px solid"
borderColor={{ base: "gray.300", _dark: "gray.600" }}
borderRadius={"xl"}
p={10}
bg={{ base: "white", _dark: "gray.800" }}
>
<Icon as={FaLock} fontSize={50} color="gray.400" mb={4} />
<Text fontWeight={600} fontSize="lg">
{title}
</Text>
<Text color="gray.500">{description}</Text>
</Flex>
);
};

export default RestrictedCommunityBanner;
24 changes: 16 additions & 8 deletions components/community/about/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CommunityMembersModal from "../../modal/community-members/CommunityMember
import AboutCommunity from "./AboutCommunity";
import AboutHeaderBar from "./AboutHeaderBar";
import AdminSectionAbout from "./AdminSectionAbout";
import useCommunityPermissions from "@/hooks/community/useCommunityPermissions";

/**
* @param {string} communityName - Name of the community
Expand Down Expand Up @@ -37,6 +38,11 @@ const About: React.FC<AboutProps> = ({ communityData }) => {
(item) => item.communityId === communityData.id
);
const [isMembersModalOpen, setMembersModalOpen] = useState(false);
const { canView, canPost } = useCommunityPermissions(communityData);

if (!canView) {
return null;
}

return (
// sticky position for the about section
Expand All @@ -59,14 +65,16 @@ const About: React.FC<AboutProps> = ({ communityData }) => {
)}
<Stack>
<AboutCommunity communityData={communityData} />
<Button
width="100%"
onClick={() => {
router.push(`/community/${communityData.id}/submit`);
}}
>
Create Post
</Button>
{canPost && (
<Button
width="100%"
onClick={() => {
router.push(`/community/${communityData.id}/submit`);
}}
>
Create Post
</Button>
)}
{isJoined && (
<Button
width="100%"
Expand Down
22 changes: 21 additions & 1 deletion components/navbar/right-content/Icons.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { savedPostStateAtom } from "@/atoms/savedPostsAtom";
import { useColorMode } from "@/components/ui/color-mode";
import useCommunityState from "@/hooks/community/useCommunityState";
import useCommunityPermissions from "@/hooks/community/useCommunityPermissions";
import useCallCreatePost from "@/hooks/posts/useCallCreatePost";
import useCustomToast from "@/hooks/useCustomToast";
import { Flex, IconButton, Icon } from "@chakra-ui/react";
import { useSetAtom } from "jotai";
import React from "react";
Expand All @@ -19,6 +22,23 @@ const icons: React.FC = () => {
const { onClick } = useCallCreatePost();
const { colorMode, toggleColorMode } = useColorMode();
const setSavedPostState = useSetAtom(savedPostStateAtom);
const { communityStateValue } = useCommunityState();
const { canPost } = useCommunityPermissions(
communityStateValue.currentCommunity
);
const showToast = useCustomToast();

const handleCreatePostClick = () => {
if (communityStateValue.currentCommunity && !canPost) {
showToast({
title: "Restricted Community",
description: "You must be a member to post in this community.",
status: "error",
});
return;
}
onClick();
};

return (
<Flex align="center" p={1}>
Expand Down Expand Up @@ -52,7 +72,7 @@ const icons: React.FC = () => {
aria-label="Create post"
variant="ghost"
fontSize={22}
onClick={onClick}
onClick={handleCreatePostClick}
mr={1.5}
ml={1.5}
>
Expand Down
3 changes: 2 additions & 1 deletion components/posts/Posts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const Posts: React.FC<PostsProps> = ({ communityData }) => {
const { onVote } = usePostVote(postStateValue, setPostStateValue);
const { onDeletePost } = usePostDeletion(setPostStateValue);
usePostVoteSync(setPostStateValue);
const { isAdmin } = useCommunityPermissions(communityData);
const { isAdmin, canPost } = useCommunityPermissions(communityData);

const { loading, fetchPosts, ref, noMorePosts } = usePostsFeed({
communityId: communityData.id,
Expand Down Expand Up @@ -62,6 +62,7 @@ const Posts: React.FC<PostsProps> = ({ communityData }) => {
onVote={onVote}
onSelectPost={onSelectPost}
onDeletePost={onDeletePost}
votingDisabled={!canPost}
/>
))}
{!noMorePosts ? (
Expand Down
4 changes: 3 additions & 1 deletion components/posts/comments/CommentItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type CommentItemProps = {
depth: number
) => Promise<void>;
user?: User;
canComment?: boolean;
};

/**
Expand All @@ -60,6 +61,7 @@ const CommentItem: React.FC<CommentItemProps> = ({
isCommunityAdmin,
onCreateComment,
user,
canComment = true,
}) => {
const [isReplying, setIsReplying] = useState(false);
const [replyText, setReplyText] = useState("");
Expand Down Expand Up @@ -111,7 +113,7 @@ const CommentItem: React.FC<CommentItemProps> = ({
borderColor={{ base: "gray.100", _dark: "gray.600" }}
>
<Stack direction="row" align="center" gap={2}>
{(comment.depth || 0) < 2 && (
{(comment.depth || 0) < 2 && canComment && (
<Button
size="sm"
variant="ghost"
Expand Down
Loading
Loading