diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index e4639ccc..46a6e359 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -6,7 +6,6 @@ import { cn } from "@/lib/utils" import { PersonalLinkLists } from "@/lib/schema/personal-link" import { useQueryState, parseAsStringLiteral } from "nuqs" import { LEARNING_STATES } from "@/lib/constants" -import { useRouter } from "next/navigation" export const LinkSection: React.FC<{ pathname: string }> = ({ pathname }) => { const { me } = useAccount({ diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 68f6bc2c..2f0809eb 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -1,7 +1,6 @@ -import { useAccount } from "@/lib/providers/jazz-provider" -import { LaIcon } from "../../la-icon" +import { LaIcon } from "@/components/custom/la-icon" import { useState } from "react" -import { useAuth } from "@clerk/nextjs" +import { SignInButton, useAuth, useUser } from "@clerk/nextjs" import { DropdownMenu, DropdownMenuContent, @@ -11,97 +10,68 @@ import { } from "@/components/ui/dropdown-menu" import { Avatar, AvatarImage } from "@/components/ui/avatar" import Link from "next/link" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { usePathname } from "next/navigation" -const MenuItem = ({ - icon, - text, - href, - onClick, - onClose -}: { - icon: string - text: string - href?: string - onClick?: () => void - onClose: () => void -}) => { - const handleClick = () => { - onClose() - if (onClick) { - onClick() - } +export const ProfileSection: React.FC = () => { + const { user, isSignedIn } = useUser() + const { signOut } = useAuth() + const [menuOpen, setMenuOpen] = useState(false) + const pathname = usePathname() + + if (!isSignedIn) { + return ( +
+ + + +
+ ) } return ( -
- - {href ? ( - - {text} - - ) : ( - - {text} - - )} -
- ) -} -export const ProfileSection: React.FC = () => { - const { me } = useAccount({ - profile: true - }) - const { signOut, isSignedIn } = useAuth() - const [menuOpen, setMenuOpen] = useState(false) - - const closeMenu = () => setMenuOpen(false) - - return ( -
+
- + - {isSignedIn ? ( - <> - - - - - - - - - - - - - ) : ( - - - - )} + + +
+ + My profile +
+ +
+ + signOut()}> +
+ + Log out +
+
@@ -109,7 +79,3 @@ export const ProfileSection: React.FC = () => {
) } - -/* - - */ diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index 54c4d1c7..3679128a 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -15,7 +15,6 @@ import { PageSection } from "./partial/page-section" import { TopicSection } from "./partial/topic-section" import { ProfileSection } from "./partial/profile-section" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" -import { SignInButton } from "@clerk/nextjs" interface SidebarContextType { isCollapsed: boolean @@ -126,15 +125,9 @@ const SidebarContent: React.FC = React.memo(() => { {me._type === "Account" && } {me._type === "Account" && }
- - {me._type === "Account" ? ( - ) : ( -
- Fake profile section -
- )} + ) }) diff --git a/web/components/routes/topics/detail/Header.tsx b/web/components/routes/topics/detail/Header.tsx index 244dbc06..461f3176 100644 --- a/web/components/routes/topics/detail/Header.tsx +++ b/web/components/routes/topics/detail/Header.tsx @@ -6,13 +6,16 @@ import { ListOfTopics, Topic } from "@/lib/schema" import { LearningStateSelector } from "@/components/custom/learning-state-selector" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LearningStateValue } from "@/lib/constants" -import { toast } from "sonner" +import { useClerk } from "@clerk/nextjs" +import { usePathname } from "next/navigation" interface TopicDetailHeaderProps { topic: Topic } export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }: TopicDetailHeaderProps) { + const clerk = useClerk() + const pathname = usePathname() const { me } = useAccountOrGuest({ root: { topicsWantToLearn: [], @@ -32,7 +35,6 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic } if (wantToLearnIndex !== -1) { p = { index: wantToLearnIndex, - // TODO: fix this type error by doing better conditionals on both index and p topic: me && me._type !== "Anonymous" ? me.root.topicsWantToLearn[wantToLearnIndex] : undefined, learningState: "wantToLearn" } @@ -60,9 +62,9 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic } const handleAddToProfile = (learningState: LearningStateValue) => { if (me?._type === "Anonymous") { - // TODO: handle better - toast.error("You need to sign in to add links to your personal list.") - return + return clerk.redirectToSignIn({ + redirectUrl: pathname + }) } const topicLists: Record = { diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 4758f01e..f6a80579 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo, useState } from "react" import Link from "next/link" -import { useRouter } from "next/navigation" +import { usePathname, useRouter } from "next/navigation" import { useAtom } from "jotai" import { toast } from "sonner" @@ -10,10 +10,11 @@ import { Button } from "@/components/ui/button" import { LearningStateSelectorContent } from "@/components/custom/learning-state-selector" import { cn, ensureUrlProtocol, generateUniqueSlug } from "@/lib/utils" -import { LaAccount, Link as LinkSchema, PersonalLink, PersonalLinkLists, Topic, UserRoot } from "@/lib/schema" +import { Link as LinkSchema, PersonalLink, Topic } from "@/lib/schema" import { openPopoverForIdAtom } from "../TopicDetailRoute" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { useClerk } from "@clerk/nextjs" interface LinkItemProps { topic: Topic @@ -24,185 +25,183 @@ interface LinkItemProps { } export const LinkItem = React.memo( - React.forwardRef( - ({ topic, link, isActive, index, setActiveIndex }, ref) => { - const router = useRouter() - const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) - const [isPopoverOpen, setIsPopoverOpen] = useState(false) + React.forwardRef(({ topic, link, isActive, index, setActiveIndex }, ref) => { + const clerk = useClerk() + const pathname = usePathname() + const router = useRouter() + const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) + const [isPopoverOpen, setIsPopoverOpen] = useState(false) - const { me } = useAccountOrGuest({ root: { personalLinks: [] } }); + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) - const personalLinks = useMemo(() => { - if (!me || me._type === "Anonymous") return undefined; - return me?.root?.personalLinks || [] - }, [me]) + const personalLinks = useMemo(() => { + if (!me || me._type === "Anonymous") return undefined + return me?.root?.personalLinks || [] + }, [me]) - const personalLink = useMemo(() => { - return personalLinks?.find(pl => pl?.link?.id === link.id) - }, [personalLinks, link.id]) + const personalLink = useMemo(() => { + return personalLinks?.find(pl => pl?.link?.id === link.id) + }, [personalLinks, link.id]) - const selectedLearningState = useMemo(() => { - return LEARNING_STATES.find(ls => ls.value === personalLink?.learningState) - }, [personalLink?.learningState]) + const selectedLearningState = useMemo(() => { + return LEARNING_STATES.find(ls => ls.value === personalLink?.learningState) + }, [personalLink?.learningState]) - const handleClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault() - setActiveIndex(index) - }, - [index, setActiveIndex] - ) + const handleClick = useCallback( + (e: React.MouseEvent) => { + e.preventDefault() + setActiveIndex(index) + }, + [index, setActiveIndex] + ) - const handleSelectLearningState = useCallback( - (learningState: LearningStateValue) => { - if (!personalLinks || !me || me?._type === "Anonymous") { - if (me?._type === "Anonymous") { - // TODO: handle better - toast.error("You need to sign in to add links to your personal list.") - } - return - }; + const handleSelectLearningState = useCallback( + (learningState: LearningStateValue) => { + if (!personalLinks || !me || me?._type === "Anonymous") { + return clerk.redirectToSignIn({ + redirectUrl: pathname + }) + } - const defaultToast = { - duration: 5000, - position: "bottom-right" as const, - closeButton: true, - action: { - label: "Go to list", - onClick: () => router.push("/") - } + const defaultToast = { + duration: 5000, + position: "bottom-right" as const, + closeButton: true, + action: { + label: "Go to list", + onClick: () => router.push("/") } + } - if (personalLink) { - if (personalLink.learningState === learningState) { - personalLink.learningState = undefined - toast.error("Link learning state removed", defaultToast) - } else { - personalLink.learningState = learningState - toast.success("Link learning state updated", defaultToast) - } + if (personalLink) { + if (personalLink.learningState === learningState) { + personalLink.learningState = undefined + toast.error("Link learning state removed", defaultToast) } else { - const slug = generateUniqueSlug(personalLinks.toJSON(), link.title) - const newPersonalLink = PersonalLink.create( - { - url: link.url, - title: link.title, - slug, - link, - learningState, - sequence: personalLinks.length + 1, - completed: false, - topic, - createdAt: new Date(), - updatedAt: new Date() - }, - { owner: me } - ) - - personalLinks.push(newPersonalLink) - - toast.success("Link added.", { - ...defaultToast, - description: `${link.title} has been added to your personal link.` - }) + personalLink.learningState = learningState + toast.success("Link learning state updated", defaultToast) } + } else { + const slug = generateUniqueSlug(personalLinks.toJSON(), link.title) + const newPersonalLink = PersonalLink.create( + { + url: link.url, + title: link.title, + slug, + link, + learningState, + sequence: personalLinks.length + 1, + completed: false, + topic, + createdAt: new Date(), + updatedAt: new Date() + }, + { owner: me } + ) - setOpenPopoverForId(null) - setIsPopoverOpen(false) - }, - [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic] - ) + personalLinks.push(newPersonalLink) - const handlePopoverOpenChange = useCallback( - (open: boolean) => { - setIsPopoverOpen(open) - setOpenPopoverForId(open ? link.id : null) - }, - [link.id, setOpenPopoverForId] - ) + toast.success("Link added.", { + ...defaultToast, + description: `${link.title} has been added to your personal link.` + }) + } - return ( -
  • -
    -
    - - - - - e.preventDefault()} + setOpenPopoverForId(null) + setIsPopoverOpen(false) + }, + [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic] + ) + + const handlePopoverOpenChange = useCallback( + (open: boolean) => { + setIsPopoverOpen(open) + setOpenPopoverForId(open ? link.id : null) + }, + [link.id, setOpenPopoverForId] + ) + + return ( +
  • +
    +
    + + + + + e.preventDefault()} + > + handleSelectLearningState(value as LearningStateValue)} + /> + + + +
    +
    +

    + {link.title} +

    + +
    +
    -
    -
  • - ) - } - ) +
    +
    + + ) + }) ) LinkItem.displayName = "LinkItem"