From 899487bb3b9d4dfd6694c4de3d473e7be5725ca4 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sat, 9 Nov 2024 15:15:54 +0700 Subject: [PATCH] chore: fix pages and links --- .../routes/_layout/_pages/(topic)/-header.tsx | 4 +- .../routes/_layout/_pages/(topic)/-item.tsx | 384 +++++++++--------- .../_pages/_protected/links/-header.tsx | 12 +- .../_pages/_protected/pages/$pageId/index.tsx | 88 ++-- .../_pages/_protected/pages/-header.tsx | 48 +-- .../_layout/_pages/_protected/pages/-list.tsx | 40 +- .../_pages/_protected/topics/-header.tsx | 9 +- .../routes/_layout/_pages/search/index.tsx | 6 +- 8 files changed, 285 insertions(+), 306 deletions(-) diff --git a/web/app/routes/_layout/_pages/(topic)/-header.tsx b/web/app/routes/_layout/_pages/(topic)/-header.tsx index 6b410235..506a0ad3 100644 --- a/web/app/routes/_layout/_pages/(topic)/-header.tsx +++ b/web/app/routes/_layout/_pages/(topic)/-header.tsx @@ -19,7 +19,7 @@ interface TopicDetailHeaderProps { setSearchQuery: (query: string) => void } -export const TopicDetailHeader = React.memo(function TopicDetailHeader({ +export const TopicDetailHeader = function TopicDetailHeader({ topic, searchQuery, setSearchQuery, @@ -163,6 +163,6 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ ) -}) +} TopicDetailHeader.displayName = "TopicDetailHeader" diff --git a/web/app/routes/_layout/_pages/(topic)/-item.tsx b/web/app/routes/_layout/_pages/(topic)/-item.tsx index 83e4fca6..a0048a53 100644 --- a/web/app/routes/_layout/_pages/(topic)/-item.tsx +++ b/web/app/routes/_layout/_pages/(topic)/-item.tsx @@ -33,216 +33,214 @@ interface LinkItemProps extends React.ComponentPropsWithoutRef<"div"> { personalLinks?: PersonalLinkLists } -export const LinkItem = React.memo( - React.forwardRef( - ( - { - topic, - link, - isActive, - index, - setActiveIndex, - className, - personalLinks, - ...props +export const LinkItem = React.forwardRef( + ( + { + topic, + link, + isActive, + index, + setActiveIndex, + className, + personalLinks, + ...props + }, + ref, + ) => { + const clerk = useClerk() + const { pathname } = useLocation() + const navigate = useNavigate() + const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false) + const { me } = useAccountOrGuest() + + const personalLink = React.useMemo(() => { + return personalLinks?.find((pl) => pl?.link?.id === link.id) + }, [personalLinks, link.id]) + + const selectedLearningState = React.useMemo(() => { + return LEARNING_STATES.find( + (ls) => ls.value === personalLink?.learningState, + ) + }, [personalLink?.learningState]) + + const handleClick = React.useCallback( + (e: React.MouseEvent) => { + e.preventDefault() + setActiveIndex(index) }, - ref, - ) => { - const clerk = useClerk() - const { pathname } = useLocation() - const navigate = useNavigate() - const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) - const [isPopoverOpen, setIsPopoverOpen] = React.useState(false) - const { me } = useAccountOrGuest() + [index, setActiveIndex], + ) - const personalLink = React.useMemo(() => { - return personalLinks?.find((pl) => pl?.link?.id === link.id) - }, [personalLinks, link.id]) + const handleSelectLearningState = React.useCallback( + (learningState: LearningStateValue) => { + if (!personalLinks || !me || me?._type === "Anonymous") { + return clerk.redirectToSignIn({ + signInFallbackRedirectUrl: pathname, + }) + } - const selectedLearningState = React.useMemo(() => { - return LEARNING_STATES.find( - (ls) => ls.value === personalLink?.learningState, - ) - }, [personalLink?.learningState]) + const defaultToast = { + duration: 5000, + position: "bottom-right" as const, + closeButton: true, + action: { + label: "Go to list", + onClick: () => + navigate({ + to: "/links", + }), + }, + } - const handleClick = React.useCallback( - (e: React.MouseEvent) => { - e.preventDefault() - setActiveIndex(index) - }, - [index, setActiveIndex], - ) - - const handleSelectLearningState = React.useCallback( - (learningState: LearningStateValue) => { - if (!personalLinks || !me || me?._type === "Anonymous") { - return clerk.redirectToSignIn({ - signInFallbackRedirectUrl: pathname, - }) - } - - const defaultToast = { - duration: 5000, - position: "bottom-right" as const, - closeButton: true, - action: { - label: "Go to list", - onClick: () => - navigate({ - to: "/links", - }), - }, - } - - 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(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) } - - setOpenPopoverForId(null) - setIsPopoverOpen(false) - }, - [ - personalLink, - personalLinks, - me, - link, - navigate, - topic, - setOpenPopoverForId, - clerk, - pathname, - ], - ) - - const handlePopoverOpenChange = React.useCallback( - (open: boolean) => { - setIsPopoverOpen(open) - setOpenPopoverForId(open ? link.id : null) - }, - [link.id, setOpenPopoverForId], - ) - - return ( -
-
-
- - - - - { + setIsPopoverOpen(open) + setOpenPopoverForId(open ? link.id : null) + }, + [link.id, setOpenPopoverForId], + ) + + return ( +
+
+
+ + + + + + + handleSelectLearningState(value as LearningStateValue) + } + /> + + - e.stopPropagation()} - className="text-xs text-muted-foreground hover:text-primary" - > - {link.url} - -
+
+
+

+ {link.title} +

+ +
+
- ) - }, - ), +
+ ) + }, ) LinkItem.displayName = "LinkItem" diff --git a/web/app/routes/_layout/_pages/_protected/links/-header.tsx b/web/app/routes/_layout/_pages/_protected/links/-header.tsx index 45b5d54b..176dde76 100644 --- a/web/app/routes/_layout/_pages/_protected/links/-header.tsx +++ b/web/app/routes/_layout/_pages/_protected/links/-header.tsx @@ -26,7 +26,7 @@ const ALL_STATES = [ ...LEARNING_STATES, ] -const LearningTab: React.FC = React.memo(() => { +const LearningTab: React.FC = () => { const navigate = useNavigate() const { state } = useSearch({ from: "/_layout/_pages/_protected/links/" }) @@ -55,9 +55,9 @@ const LearningTab: React.FC = React.memo(() => { highlighterIncludeMargin={true} /> ) -}) +} -const FilterAndSort: React.FC = React.memo(() => { +const FilterAndSort: React.FC = () => { const [sort, setSort] = useAtom(linkSortAtom) const [sortOpen, setSortOpen] = React.useState(false) @@ -113,9 +113,9 @@ const FilterAndSort: React.FC = React.memo(() => {
) -}) +} -export const LinkHeader: React.FC = React.memo(() => { +export const LinkHeader: React.FC = () => { const isTablet = useMedia("(max-width: 1024px)") return ( @@ -144,7 +144,7 @@ export const LinkHeader: React.FC = React.memo(() => { )} ) -}) +} LinkHeader.displayName = "LinkHeader" LearningTab.displayName = "LearningTab" diff --git a/web/app/routes/_layout/_pages/_protected/pages/$pageId/index.tsx b/web/app/routes/_layout/_pages/_protected/pages/$pageId/index.tsx index f082182d..bed1bba7 100644 --- a/web/app/routes/_layout/_pages/_protected/pages/$pageId/index.tsx +++ b/web/app/routes/_layout/_pages/_protected/pages/$pageId/index.tsx @@ -30,7 +30,13 @@ const TITLE_PLACEHOLDER = "Untitled" function PageDetailComponent() { const { pageId } = Route.useParams() const { me } = useAccount({ - root: { personalPages: [{}] }, + root: { + personalPages: [ + { + topic: {}, + }, + ], + }, }) const isMobile = useMedia("(max-width: 770px)") const page = useCoState(PersonalPage, pageId as ID) @@ -81,49 +87,47 @@ function PageDetailComponent() { ) } -const SidebarActions = React.memo( - ({ - page, - handleDelete, - }: { - page: PersonalPage - handleDelete: () => void - }) => ( -
-
-
- - Page actions - -
-
- { - page.topic = topic - page.updatedAt = new Date() - }} - variant="ghost" - className="-ml-1.5" - renderSelectedText={() => ( - - {page.topic?.prettyName || "Select a topic"} - - )} - /> - -
+const SidebarActions = ({ + page, + handleDelete, +}: { + page: PersonalPage + handleDelete: () => void +}) => ( +
+
+
+ + Page actions + +
+
+ { + page.topic = topic + page.updatedAt = new Date() + }} + variant="ghost" + className="-ml-1.5" + renderSelectedText={() => ( + + {page.topic?.prettyName || "Select a topic"} + + )} + /> +
- ), +
) SidebarActions.displayName = "SidebarActions" diff --git a/web/app/routes/_layout/_pages/_protected/pages/-header.tsx b/web/app/routes/_layout/_pages/_protected/pages/-header.tsx index 53f19b46..11b4335a 100644 --- a/web/app/routes/_layout/_pages/_protected/pages/-header.tsx +++ b/web/app/routes/_layout/_pages/_protected/pages/-header.tsx @@ -7,30 +7,37 @@ import { import { LaIcon } from "@/components/custom/la-icon" import { useAccount } from "@/lib/providers/jazz-provider" import { usePageActions } from "~/hooks/actions/use-page-actions" -import { useNavigate } from "@tanstack/react-router" interface PageHeaderProps {} -export const PageHeader: React.FC = React.memo(() => { +export const PageHeader: React.FC = () => { const { me } = useAccount() - const navigate = useNavigate() const { newPage } = usePageActions() if (!me) return null - const handleNewPageClick = () => { - const page = newPage(me) - navigate({ to: `/pages/${page.id}` }) - } - return (
- + +
+
+ +
+
) -}) +} PageHeader.displayName = "PageHeader" @@ -42,24 +49,3 @@ const HeaderTitle: React.FC = () => (
) - -interface NewPageButtonProps { - onClick: () => void -} - -const NewPageButton: React.FC = ({ onClick }) => ( -
-
- -
-
-) diff --git a/web/app/routes/_layout/_pages/_protected/pages/-list.tsx b/web/app/routes/_layout/_pages/_protected/pages/-list.tsx index 7ab7ca7b..d12199c3 100644 --- a/web/app/routes/_layout/_pages/_protected/pages/-list.tsx +++ b/web/app/routes/_layout/_pages/_protected/pages/-list.tsx @@ -11,20 +11,19 @@ interface PageListProps {} export const PageList: React.FC = () => { const isTablet = useMedia("(max-width: 640px)") - const { me } = useAccount({ root: { personalPages: [] } }) + const { me } = useAccount({ root: { personalPages: [{ topic: {} }] } }) const [activeItemIndex, setActiveItemIndex] = React.useState( null, ) const [keyboardActiveIndex, setKeyboardActiveIndex] = React.useState< number | null >(null) - const personalPages = React.useMemo( - () => me?.root?.personalPages, - [me?.root?.personalPages], - ) const next = () => - Math.min((activeItemIndex ?? 0) + 1, (personalPages?.length ?? 0) - 1) + Math.min( + (activeItemIndex ?? 0) + 1, + (me?.root.personalPages.length ?? 0) - 1, + ) const prev = () => Math.max((activeItemIndex ?? 0) - 1, 0) @@ -58,22 +57,19 @@ export const PageList: React.FC = () => { tabIndex={-1} role="list" > - {personalPages?.map( - (page, index) => - page?.id && ( - setElementRef(el, index)} - page={page} - isActive={index === activeItemIndex} - onPointerMove={() => { - setKeyboardActiveIndex(null) - setActiveItemIndex(index) - }} - data-keyboard-active={keyboardActiveIndex === index} - /> - ), - )} + {me?.root.personalPages.map((page, index) => ( + setElementRef(el, index)} + page={page} + isActive={index === activeItemIndex} + onPointerMove={() => { + setKeyboardActiveIndex(null) + setActiveItemIndex(index) + }} + data-keyboard-active={keyboardActiveIndex === index} + /> + ))}
) diff --git a/web/app/routes/_layout/_pages/_protected/topics/-header.tsx b/web/app/routes/_layout/_pages/_protected/topics/-header.tsx index e0162575..94f27d54 100644 --- a/web/app/routes/_layout/_pages/_protected/topics/-header.tsx +++ b/web/app/routes/_layout/_pages/_protected/topics/-header.tsx @@ -3,22 +3,17 @@ import { ContentHeader, SidebarToggleButton, } from "@/components/custom/content-header" -import { useAccount } from "@/lib/providers/jazz-provider" interface TopicHeaderProps {} -export const TopicHeader: React.FC = React.memo(() => { - const { me } = useAccount() - - if (!me) return null - +export const TopicHeader: React.FC = () => { return (
) -}) +} TopicHeader.displayName = "TopicHeader" diff --git a/web/app/routes/_layout/_pages/search/index.tsx b/web/app/routes/_layout/_pages/search/index.tsx index c0a888fc..a3c5271a 100644 --- a/web/app/routes/_layout/_pages/search/index.tsx +++ b/web/app/routes/_layout/_pages/search/index.tsx @@ -86,7 +86,7 @@ const SearchComponent = () => { }>({ topics: [], links: [], pages: [] }) const { me } = useAccountOrGuest({ - root: { personalLinks: [], personalPages: [] }, + root: { personalLinks: [{}], personalPages: [{}] }, }) const globalGroup = useCoState(PublicGlobalGroup, JAZZ_GLOBAL_GROUP_ID, { @@ -105,7 +105,7 @@ const SearchComponent = () => { } setSearchResults({ topics: - globalGroup?.root.topics?.filter( + globalGroup?.root.topics.filter( (topic: Topic | null): topic is Topic => topic !== null && topic.prettyName.toLowerCase().startsWith(value), ) || [], @@ -119,7 +119,7 @@ const SearchComponent = () => { pages: me?._type === "Anonymous" ? [] - : me?.root.personalPages?.filter( + : me?.root.personalPages.filter( (page): page is PersonalPage => page !== null && page.title !== undefined &&