From 31d89cb11aeb563a2cedb448811c7b3fd7192e0c Mon Sep 17 00:00:00 2001 From: Pouria Delfanazari Date: Wed, 30 Oct 2024 11:10:03 -0700 Subject: [PATCH] Add topic page --- src/app/dashboard/topics/[topic]/layout.tsx | 14 ++++ src/app/dashboard/topics/[topic]/page.tsx | 20 +++++ src/app/dashboard/topics/layout.tsx | 15 ++++ .../contentDisplay/feedHeader/FeedHeader.tsx | 2 +- .../topicHeader/TopicHeader.tsx | 62 +++++++++++++++ .../topicHeader/TopicHeaderSkeleton.tsx | 18 +++++ .../dataDisplay/postEmbed/ExternalEmbed.tsx | 77 ++++++++++++------- .../dataDisplay/postEmbed/FeedEmbed.tsx | 2 +- .../dataDisplay/postEmbed/ListEmbed.tsx | 2 +- src/containers/posts/TopicContainer.tsx | 45 +++++++++++ src/containers/search/PostSearchContainer.tsx | 7 +- 11 files changed, 228 insertions(+), 36 deletions(-) create mode 100644 src/app/dashboard/topics/[topic]/layout.tsx create mode 100644 src/app/dashboard/topics/[topic]/page.tsx create mode 100644 src/app/dashboard/topics/layout.tsx create mode 100644 src/components/contentDisplay/topicHeader/TopicHeader.tsx create mode 100644 src/components/contentDisplay/topicHeader/TopicHeaderSkeleton.tsx create mode 100644 src/containers/posts/TopicContainer.tsx diff --git a/src/app/dashboard/topics/[topic]/layout.tsx b/src/app/dashboard/topics/[topic]/layout.tsx new file mode 100644 index 00000000..9653ef74 --- /dev/null +++ b/src/app/dashboard/topics/[topic]/layout.tsx @@ -0,0 +1,14 @@ +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Topic", + description: "Topic", +}; + +export default async function TopicsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} diff --git a/src/app/dashboard/topics/[topic]/page.tsx b/src/app/dashboard/topics/[topic]/page.tsx new file mode 100644 index 00000000..5273ed2a --- /dev/null +++ b/src/app/dashboard/topics/[topic]/page.tsx @@ -0,0 +1,20 @@ +import TopicHeader from "@/components/contentDisplay/topicHeader/TopicHeader"; +import TopicContainer from "@/containers/posts/TopicContainer"; + +interface Props { + params: { + topic: string; + }; +} + +export default function Page(props: Props) { + const { params } = props; + const url = decodeURIComponent(params.topic); + + return ( + <> + + + + ); +} diff --git a/src/app/dashboard/topics/layout.tsx b/src/app/dashboard/topics/layout.tsx new file mode 100644 index 00000000..b9406213 --- /dev/null +++ b/src/app/dashboard/topics/layout.tsx @@ -0,0 +1,15 @@ +import Layout from "@/containers/Layout"; +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Topics", + description: "Topics", +}; + +export default async function TopicsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} diff --git a/src/components/contentDisplay/feedHeader/FeedHeader.tsx b/src/components/contentDisplay/feedHeader/FeedHeader.tsx index 67272abe..5f1097d6 100644 --- a/src/components/contentDisplay/feedHeader/FeedHeader.tsx +++ b/src/components/contentDisplay/feedHeader/FeedHeader.tsx @@ -114,7 +114,7 @@ export default function FeedHeader(props: Props) { {isFetchingFeedInfo && } {!isFetchingFeedInfo && feedInfo && ( <> -
+
; + + return ( +
+ {error && ( + + )} + + {data?.image && !hideImage && ( + {`Image setHideImage(true)} + /> + )} + + {data && ( + <> +
+ + Topic +
+
+ {data?.title && ( +

+ {data.title} +

+ )} + {data?.description && ( +

{data.description}

+ )} +
+ + )} +
+ ); +} diff --git a/src/components/contentDisplay/topicHeader/TopicHeaderSkeleton.tsx b/src/components/contentDisplay/topicHeader/TopicHeaderSkeleton.tsx new file mode 100644 index 00000000..18d5e4f6 --- /dev/null +++ b/src/components/contentDisplay/topicHeader/TopicHeaderSkeleton.tsx @@ -0,0 +1,18 @@ +export default function TopicHeaderSkeleton() { + return ( +
+
+ +
+
+
+
+ +
+
+
+
+
+
+ ); +} diff --git a/src/components/dataDisplay/postEmbed/ExternalEmbed.tsx b/src/components/dataDisplay/postEmbed/ExternalEmbed.tsx index c5a74325..3898107c 100644 --- a/src/components/dataDisplay/postEmbed/ExternalEmbed.tsx +++ b/src/components/dataDisplay/postEmbed/ExternalEmbed.tsx @@ -1,7 +1,9 @@ import { getHostname } from "@/lib/utils/text"; import { AppBskyEmbedExternal } from "@atproto/api"; +import { SiGooglemessages } from "react-icons/si"; import Image from "next/image"; import Link from "next/link"; +import { BiRightArrowAlt } from "react-icons/bi"; interface Props { embed: AppBskyEmbedExternal.View; @@ -13,36 +15,53 @@ export default function ExternalEmbed(props: Props) { return ( <> {depth < 2 && ( -
- e.stopPropagation()} - > - {embed.external.thumb && ( - {embed.external.description} - )} -
- - {getHostname(embed.external.uri)} - - - {embed.external.title} - - {embed.external.description && ( - - {embed.external.description} - +
+
+ e.stopPropagation()} + > + {embed.external.thumb && ( + {embed.external.description} )} -
- +
+ + {getHostname(embed.external.uri)} + + + {embed.external.title} + + {embed.external.description && ( + + {embed.external.description} + + )} +
+ +
+
+ e.stopPropagation()} + className="flex flex-wrap items-center gap-2 text-skin-tertiary px-3 py-2 hover:text-skin-secondary" + > + +
+ View topic + +
+ +
)} diff --git a/src/components/dataDisplay/postEmbed/FeedEmbed.tsx b/src/components/dataDisplay/postEmbed/FeedEmbed.tsx index 3068290e..044e9e9d 100644 --- a/src/components/dataDisplay/postEmbed/FeedEmbed.tsx +++ b/src/components/dataDisplay/postEmbed/FeedEmbed.tsx @@ -25,7 +25,7 @@ export default function FeedEmbed(props: Props) { onClick={(e) => { e.stopPropagation(); }} - className="border-skin-base bg-skin-base mt-2 flex flex-col gap-2 rounded-2xl border p-3 hover:brightness-95" + className="border-skin-base bg-skin-base mt-2 flex flex-col gap-2 rounded-2xl border p-3 hover:bg-skin-secondary" >
diff --git a/src/components/dataDisplay/postEmbed/ListEmbed.tsx b/src/components/dataDisplay/postEmbed/ListEmbed.tsx index 00102915..c43f87aa 100644 --- a/src/components/dataDisplay/postEmbed/ListEmbed.tsx +++ b/src/components/dataDisplay/postEmbed/ListEmbed.tsx @@ -32,7 +32,7 @@ export default function ListEmbed(props: Props) { onClick={(e) => { e.stopPropagation(); }} - className="border-skin-base bg-skin-base mt-2 block cursor-pointer rounded-xl border p-3 hover:brightness-95" + className="border-skin-base bg-skin-base mt-2 block cursor-pointer rounded-xl border p-3 hover:bg-skin-secondary" >
{selectedIcon}
diff --git a/src/containers/posts/TopicContainer.tsx b/src/containers/posts/TopicContainer.tsx new file mode 100644 index 00000000..75a9a454 --- /dev/null +++ b/src/containers/posts/TopicContainer.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useState } from "react"; +import TabItem from "@/components/navigational/tabs/TabItem"; +import Tabs from "@/components/navigational/tabs/Tabs"; +import PostSearchContainer from "../search/PostSearchContainer"; + +interface Props { + url: string; +} + +export default function TopicContainer(props: Props) { + const { url } = props; + const [currentTab, setCurrentTab] = useState<"top" | "latest">("top"); + + const handleTabChange = (tab: "top" | "latest") => { + setCurrentTab(tab); + }; + + return ( +
+ + handleTabChange("top")} + label="Top" + isActive={currentTab === "top"} + /> + handleTabChange("latest")} + label="Latest" + isActive={currentTab === "latest"} + /> + + + {currentTab === "latest" && ( + + )} + {currentTab === "top" && ( + + )} +
+ ); +} diff --git a/src/containers/search/PostSearchContainer.tsx b/src/containers/search/PostSearchContainer.tsx index e262cb71..70d37a36 100644 --- a/src/containers/search/PostSearchContainer.tsx +++ b/src/containers/search/PostSearchContainer.tsx @@ -1,3 +1,5 @@ +"use client"; + import { searchPosts } from "@/lib/api/bsky/actor"; import useAgent from "@/lib/hooks/bsky/useAgent"; import { useInfiniteQuery } from "@tanstack/react-query"; @@ -64,10 +66,7 @@ export default function PostSearchContainer(props: Props) { {isFetching && !isFetchingNextPage && } {isEmpty && !hasNextPage && (
- +
)} {error && (