fix: detail topic (#117)

* feat: keyboard nav

* fix: link update

* feat: reusable learning state

* chore: use new learning state

* feat: add to my profile

* .

* .

* feat: on enter open the link

* fix: lint

* fix: use eslint v8 instead of v9

* fix: add to my profile

* chore: update personal link schema

* chore: update personal page schema

* fix: update detail wrapper

* fix: update page section

* removing option for learning status

* removing option for learning status for topic

* feat: add createdAt and updatedAt for personal Page

* chore: update page section component

* chore: remove chevron from sub menu

* fix: sidebar

* chore: add focus and disable toast

* feat: la editor add execption for no command class

* fix: la editor style and fix page detail

* fix: title

* fix: topic learning state

* chore: add showSearch for learning state

* fix: bunch stuff

* chore: link list and item handle learning state

* chore: set expand to false

* feat: personal link for topic detail

* chore: hook use topic data

* chore: go to list

* fix: link and topic

* feat(utils): new keyboard utils

* feat(store): add linkOpenPopoverForIdAtom for link

* chore: using memo for use topic data

* fix: remove duplicate component

* chore: performance for topic detail lint item

* refactor: remove LinkOptions component

* chore: improve performance for list

* feat: added LinkRoute copmonent

* chore: link manage

* feat: bottom bar

* fix: link

* fix: page wrapper

* fix: import thing

* chore: added a displayname

* refactor: page detail

* refactor: page detail

* fix: add topic to personal link form link

* fix: only show page count if more than zero

* fix: sidebar topic section

---------

Co-authored-by: Nikita <github@nikiv.dev>
Co-authored-by: marshennikovaolga <marshennikova@gmail.com>
This commit is contained in:
Aslam
2024-08-29 02:48:48 +07:00
committed by GitHub
parent 94a63bd79b
commit 9e89959dd4
50 changed files with 1667 additions and 1000 deletions

View File

@@ -10,21 +10,19 @@ import { Content, EditorContent, useEditor } from "@tiptap/react"
import { StarterKit } from "@/components/la-editor/extensions/starter-kit"
import { Paragraph } from "@/components/la-editor/extensions/paragraph"
import { useAccount, useCoState } from "@/lib/providers/jazz-provider"
import { toast } from "sonner"
import { EditorView } from "@tiptap/pm/view"
import { Editor } from "@tiptap/core"
import { generateUniqueSlug } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { LaIcon } from "@/components/custom/la-icon"
import { pageTopicSelectorAtom } from "@/store/page"
import { TopicSelector } from "@/components/routes/link/form/partial/topic-selector"
import { TopicSelector } from "@/components/routes/link/partials/form/topic-selector"
import { FocusClasses } from "@tiptap/extension-focus"
import DeletePageModal from "@/components/custom/delete-modal"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"
const TITLE_PLACEHOLDER = "Page title"
const TITLE_PLACEHOLDER = "Untitled"
export function DetailPageWrapper({ pageId }: { pageId: string }) {
export function PageDetailRoute({ pageId }: { pageId: string }) {
const page = useCoState(PersonalPage, pageId as ID<PersonalPage>)
if (!page) return <div>Loading...</div>
@@ -45,32 +43,52 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
const titleEditorRef = useRef<Editor | null>(null)
const contentEditorRef = useRef<LAEditorRef>(null)
const [, setTopicSelectorOpen] = useAtom(pageTopicSelectorAtom)
const [selectedPageTopic, setSelectedPageTopic] = useState<Topic | null>(page.topic || null)
const [, setSelectedPageTopic] = useState<Topic | null>(page.topic || null)
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
const isTitleInitialMount = useRef(true)
const isContentInitialMount = useRef(true)
const updatePageContent = (content: Content, model: PersonalPage) => {
model.content = content
}
const handleTitleBlur = (editor: Editor) => {
const newTitle = editor.getText().trim()
if (!newTitle) {
toast.error("Update failed", {
description: "Title must be longer than or equal to 1 character"
})
editor.commands.setContent(page.title)
if (isContentInitialMount.current) {
isContentInitialMount.current = false
return
}
if (newTitle === page.title) return
console.log("Updating page content")
model.content = content
model.updatedAt = new Date()
}
const handleUpdateTitle = (editor: Editor) => {
if (isTitleInitialMount.current) {
isTitleInitialMount.current = false
return
}
/*
* The logic changed, but we keep this commented code for reference
*/
// const newTitle = editor.getText().trim()
// if (!newTitle) {
// toast.error("Update failed", {
// description: "Title must be longer than or equal to 1 character"
// })
// editor.commands.setContent(page.title || "")
// return
// }
// if (newTitle === page.title) return
console.log("Updating page title")
const personalPages = me.root?.personalPages?.toJSON() || []
const slug = generateUniqueSlug(personalPages, page.slug)
const slug = generateUniqueSlug(personalPages, page.slug || "")
const trimmedTitle = editor.getText().trim()
page.title = trimmedTitle
page.slug = slug
page.updatedAt = new Date()
editor.commands.setContent(trimmedTitle)
}
@@ -108,7 +126,9 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
const titleEditor = useEditor({
immediatelyRender: false,
autofocus: true,
extensions: [
FocusClasses,
Paragraph,
StarterKit.configure({
bold: false,
@@ -137,10 +157,12 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
handleKeyDown: handleTitleKeyDown
},
onCreate: ({ editor }) => {
const capitalizedTitle = page.title.charAt(0).toUpperCase() + page.title.slice(1)
editor.commands.setContent(`<p>${capitalizedTitle}</p>`)
if (page.title) editor.commands.setContent(`<p>${page.title}</p>`)
},
onBlur: ({ editor }) => handleTitleBlur(editor)
onBlur: ({ editor }) => handleUpdateTitle(editor),
onUpdate: ({ editor }) => {
handleUpdateTitle(editor)
}
})
useEffect(() => {
@@ -149,6 +171,11 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
}
}, [titleEditor])
useEffect(() => {
isTitleInitialMount.current = true
isContentInitialMount.current = true
}, [])
return (
<div tabIndex={0} className="relative flex grow flex-col overflow-y-auto">
<div className="relative mx-auto flex h-full w-[calc(100%-40px)] shrink-0 grow flex-col sm:w-[calc(100%-80px)]">
@@ -156,7 +183,7 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
<div className="mb-2 mt-8 flex flex-row justify-between py-1.5">
<EditorContent
editor={titleEditor}
className="la-editor cursor-text select-text text-2xl font-semibold leading-[calc(1.33333)] tracking-[-0.00625rem]"
className="la-editor no-command grow cursor-text select-text text-2xl font-semibold leading-[calc(1.33333)] tracking-[-0.00625rem]"
/>
<div className="items-center space-x-4">
<TopicSelector
@@ -201,7 +228,7 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
onConfirm={() => {
confirmDelete(page)
}}
title={page.title.charAt(0).toUpperCase() + page.title.slice(1)}
title={page.title || ""}
/>
</div>
)

View File

@@ -2,20 +2,11 @@
import * as React from "react"
import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator
} from "@/components/ui/breadcrumb"
import { useCoState } from "@/lib/providers/jazz-provider"
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from "@/components/ui/breadcrumb"
import { PersonalPage } from "@/lib/schema/personal-page"
import { ID } from "jazz-tools"
export const DetailPageHeader = ({ pageId }: { pageId: ID<PersonalPage> }) => {
const page = useCoState(PersonalPage, pageId)
return (
<ContentHeader>
<div className="flex min-w-0 gap-2">