Skip to content

Commit

Permalink
Merge pull request #672 from bounswe/frontend/feature/596/tagfollowbu…
Browse files Browse the repository at this point in the history
…tton

[Frontend] Folow/Unfollow Tags
  • Loading branch information
ozdentarikcan authored Dec 12, 2024
2 parents 3870c66 + 33f6690 commit ec93256
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 28 deletions.
83 changes: 83 additions & 0 deletions frontend/src/components/TagFollowButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
useFollowTag,
useGetTagDetails,
useUnfollowTag,
} from "@/services/api/programmingForumComponents";
import { useState } from "react";
import { Button } from "./ui/button";

export default function TagFollowButton({
tag,
}: {
tag: { tagId?: string; following?: boolean };
}) {
const { isLoading, data, error, refetch } = useGetTagDetails(
{
pathParams: {
tagId: tag.tagId!,
},
},
{
enabled: typeof tag.following !== "boolean",
},
);

const [optimisticFollowing, setOptimisticFollowing] = useState(
null as boolean | null,
);

const { mutateAsync: follow } = useFollowTag({
onSuccess: () => {
refetch().then(() => {
setOptimisticFollowing(null);
});
},
onError: () => {
setOptimisticFollowing(null);
},
});
const { mutateAsync: unfollow } = useUnfollowTag({
onSuccess: () => {
refetch().then(() => {
setOptimisticFollowing(null);
});
},
onError: () => {
setOptimisticFollowing(null);
},
});

const following = optimisticFollowing ?? data?.data?.following;

return (
<Button
disabled={!!error || isLoading}
variant={following && !isLoading ? "primary-outline" : "default"}
onClick={() => {
if (following) {
unfollow({
pathParams: {
tagId: tag.tagId!,
},
});
setOptimisticFollowing(false);
} else {
follow({
pathParams: {
tagId: tag.tagId!,
},
});
setOptimisticFollowing(true);
}
}}
>
{isLoading
? "Loading..."
: error
? "Error"
: following
? "Following"
: "Follow"}
</Button>
);
}
48 changes: 20 additions & 28 deletions frontend/src/routes/tag.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ const mockTagData = vi.hoisted(
// Mock the API hook
vi.mock("@/services/api/programmingForumComponents", () => ({
useGetTagDetails: vi.fn(() => {}),
useFollowTag: vi.fn(() => ({
mutateAsync: vi.fn(), // Mock implementation of mutateAsync
})),
useUnfollowTag: vi.fn(() => ({
mutateAsync: vi.fn(), // Mock implementation of mutateAsync
})),
useGetQuestionDetails: vi.fn(() => ({
data: {
data: {
Expand Down Expand Up @@ -88,34 +94,20 @@ describe("TagPage", () => {
).toBeInTheDocument();
});

it("does not render 'Follow' button for unauthenticated users", () => {
render(
<MemoryRouter initialEntries={["/tag/javascript"]}>
<Routes>
<Route path="/tag/:tagName" element={<TagPage />} />
</Routes>
</MemoryRouter>,
);

expect(
screen.queryByRole("button", { name: /follow/i }),
).not.toBeInTheDocument();
});

// it("renders 'Follow' button for authenticated users", () => {
// vi.mocked(useAuthStore).mockReturnValue({
// selfProfile: { id: 1, username: "testuser", profilePicture: "test.jpg" },
// token: "mock-token",
// });
it("renders 'Follow' button for authenticated users", () => {
vi.mocked(useAuthStore).mockReturnValue({
selfProfile: { id: 1, username: "testuser", profilePicture: "test.jpg" },
token: "mock-token",
});

// render(
// <MemoryRouter initialEntries={["/tag/javascript"]}>
// <Routes>
// <Route path="/tag/:tagName" element={<TagPage />} />
// </Routes>
// </MemoryRouter>,
// );
render(
<MemoryRouter initialEntries={["/tag/javascript"]}>
<Routes>
<Route path="/tag/:tagName" element={<TagPage />} />
</Routes>
</MemoryRouter>,
);

// expect(screen.getByRole("button", { name: /follow/i })).toBeInTheDocument();
// });
expect(screen.getByRole("button", { name: /follow/i })).toBeInTheDocument();
});
});
8 changes: 8 additions & 0 deletions frontend/src/routes/tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
useSearchQuestions,
} from "@/services/api/programmingForumComponents";
// import { Recipe } from "@/components/Recipe";
import TagFollowButton from "@/components/TagFollowButton";
import { DifficultyFilter } from "@/components/DifficultyFilter";
import { HighlightedQuestionsBox } from "@/components/HighlightedQuestionsBox";
import { QuestionCard } from "@/components/QuestionCard"; // Import your QuestionCard component
Expand Down Expand Up @@ -99,6 +100,13 @@ export default function TagPage() {
<div className="flex items-center gap-2 ">
<h1>{tag.name}</h1>
</div>
{!!token && (
<TagFollowButton
tag={{
tagId: tagId!,
following: tag.following,
}}
/>)}
</div>
{tag.logoImage && (
<img
Expand Down

0 comments on commit ec93256

Please sign in to comment.