fix: Bug fixing & Enhancement (#161)

* chore: memoize sorted pages

* chore: make link size more precise

* fix(link): disable enter press on create mode

* fix(onboarding): move is base logic and use escape for single quote

* fix(page): on delete success redirect to pages

* fix(sntry): sentry client error report

* chore(page): dynamic focus on title/content

* chore(link): tweak badge class

* chore(link): use nuqs for handling create mode

* fix(link): refs

* feat(palette): implement new link
This commit is contained in:
Aslam
2024-09-11 15:25:21 +07:00
committed by GitHub
parent 0668dd5625
commit 2a637705f2
20 changed files with 181 additions and 231 deletions

View File

@@ -4,7 +4,7 @@ import React, { useEffect, useState, useCallback, useRef } from "react"
import { LinkHeader } from "@/components/routes/link/header"
import { LinkList } from "@/components/routes/link/list"
import { LinkManage } from "@/components/routes/link/manage"
import { useQueryState } from "nuqs"
import { parseAsBoolean, useQueryState } from "nuqs"
import { atom, useAtom } from "jotai"
import { LinkBottomBar } from "./bottom-bar"
import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette"
@@ -14,6 +14,7 @@ export const isDeleteConfirmShownAtom = atom(false)
export function LinkRoute(): React.ReactElement {
const [nuqsEditId] = useQueryState("editId")
const [activeItemIndex, setActiveItemIndex] = useState<number | null>(null)
const [isInCreateMode] = useQueryState("create", parseAsBoolean)
const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom)
const [isDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom)
const [disableEnterKey, setDisableEnterKey] = useState(false)
@@ -32,7 +33,7 @@ export function LinkRoute(): React.ReactElement {
}, [])
useEffect(() => {
if (isDeleteConfirmShown || isCommandPaletteOpen) {
if (isDeleteConfirmShown || isCommandPaletteOpen || isInCreateMode) {
setDisableEnterKey(true)
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
@@ -47,7 +48,7 @@ export function LinkRoute(): React.ReactElement {
clearTimeout(timeoutRef.current)
}
}
}, [isDeleteConfirmShown, isCommandPaletteOpen, handleCommandPaletteClose])
}, [isDeleteConfirmShown, isCommandPaletteOpen, isInCreateMode, handleCommandPaletteClose])
return (
<div className="flex h-full flex-auto flex-col overflow-hidden">

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from "react"
import React, { useCallback, useEffect, useRef } from "react"
import { motion, AnimatePresence } from "framer-motion"
import { icons } from "lucide-react"
import { Button } from "@/components/ui/button"
@@ -6,8 +6,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
import { getSpecialShortcut, formatShortcut, isMacOS } from "@/lib/utils"
import { LaIcon } from "@/components/custom/la-icon"
import { useAtom } from "jotai"
import { linkShowCreateAtom } from "@/store/link"
import { useQueryState } from "nuqs"
import { parseAsBoolean, useQueryState } from "nuqs"
import { useConfirm } from "@omit/react-confirm-dialog"
import { useAccount, useCoState } from "@/lib/providers/jazz-provider"
import { PersonalLink } from "@/lib/schema"
@@ -48,9 +47,8 @@ ToolbarButton.displayName = "ToolbarButton"
export const LinkBottomBar: React.FC = () => {
const [editId, setEditId] = useQueryState("editId")
const [createMode, setCreateMode] = useQueryState("create", parseAsBoolean)
const [, setGlobalLinkFormExceptionRefsAtom] = useAtom(globalLinkFormExceptionRefsAtom)
const [showCreate, setShowCreate] = useAtom(linkShowCreateAtom)
const { me } = useAccount({ root: { personalLinks: [] } })
const personalLink = useCoState(PersonalLink, editId as ID<PersonalLink>)
@@ -67,6 +65,13 @@ export const LinkBottomBar: React.FC = () => {
const { deleteLink } = useLinkActions()
const confirm = useConfirm()
const handleCreateMode = useCallback(() => {
setEditId(null)
setTimeout(() => {
setCreateMode(prev => !prev)
}, 100)
}, [setEditId, setCreateMode])
useEffect(() => {
setGlobalLinkFormExceptionRefsAtom([
overlayRef,
@@ -81,7 +86,7 @@ export const LinkBottomBar: React.FC = () => {
}, [setGlobalLinkFormExceptionRefsAtom])
const handleDelete = async (e: React.MouseEvent) => {
if (!personalLink) return
if (!personalLink || !me) return
const result = await confirm({
title: `Delete "${personalLink.title}"?`,
@@ -106,7 +111,6 @@ export const LinkBottomBar: React.FC = () => {
})
if (result) {
if (!me) return
deleteLink(me, personalLink)
setEditId(null)
}
@@ -114,24 +118,19 @@ export const LinkBottomBar: React.FC = () => {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (isMacOS()) {
if (event.ctrlKey && event.metaKey && event.key.toLowerCase() === "n") {
event.preventDefault()
setShowCreate(true)
}
} else {
// For Windows, we'll use Ctrl + Win + N
// Note: The Windows key is not directly detectable in most browsers
if (event.ctrlKey && event.key.toLowerCase() === "n" && (event.metaKey || event.altKey)) {
event.preventDefault()
setShowCreate(true)
}
const isCreateShortcut = isMacOS()
? event.ctrlKey && event.metaKey && event.key.toLowerCase() === "n"
: event.ctrlKey && event.key.toLowerCase() === "n" && (event.metaKey || event.altKey)
if (isCreateShortcut) {
event.preventDefault()
handleCreateMode()
}
}
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [setShowCreate])
}, [handleCreateMode])
const shortcutKeys = getSpecialShortcut("expandToolbar")
const shortcutText = formatShortcut(shortcutKeys)
@@ -172,11 +171,11 @@ export const LinkBottomBar: React.FC = () => {
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.1 }}
>
{showCreate && <ToolbarButton icon={"ArrowLeft"} onClick={() => setShowCreate(true)} />}
{!showCreate && (
{createMode && <ToolbarButton icon={"ArrowLeft"} onClick={handleCreateMode} />}
{!createMode && (
<ToolbarButton
icon={"Plus"}
onClick={() => setShowCreate(true)}
onClick={handleCreateMode}
tooltip={`New Link (${shortcutText})`}
ref={plusBtnRef}
/>

View File

@@ -166,13 +166,11 @@ const LinkList: React.FC<LinkListProps> = ({ activeItemIndex, setActiveItemIndex
return newIndex
})
} else if (e.key === "Enter" && !disableEnterKey) {
} else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null) {
e.preventDefault()
if (activeItemIndex !== null) {
const activeLink = sortedLinks[activeItemIndex]
if (activeLink) {
setEditId(activeLink.id)
}
const activeLink = sortedLinks[activeItemIndex]
if (activeLink) {
setEditId(activeLink.id)
}
}
}

View File

@@ -1,25 +1,24 @@
"use client"
import React from "react"
import { linkShowCreateAtom } from "@/store/link"
import { useAtom } from "jotai"
import { useKey } from "react-use"
import { LinkForm } from "./partials/form/link-form"
import { motion, AnimatePresence } from "framer-motion"
import { parseAsBoolean, useQueryState } from "nuqs"
interface LinkManageProps {}
const LinkManage: React.FC<LinkManageProps> = () => {
const [showCreate, setShowCreate] = useAtom(linkShowCreateAtom)
const [createMode, setCreateMode] = useQueryState("create", parseAsBoolean)
const handleFormClose = () => setShowCreate(false)
const handleFormClose = () => setCreateMode(false)
const handleFormFail = () => {}
useKey("Escape", handleFormClose)
return (
<AnimatePresence>
{showCreate && (
{createMode && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}

View File

@@ -83,7 +83,7 @@ export const LinkItem: React.FC<LinkItemProps> = ({
"relative cursor-default outline-none",
"grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2",
{
"bg-muted-foreground/10": isActive,
"bg-muted-foreground/5": isActive,
"hover:bg-muted/50": !isActive
}
)}
@@ -148,7 +148,11 @@ export const LinkItem: React.FC<LinkItemProps> = ({
</div>
<div className="flex shrink-0 items-center justify-end">
{personalLink.topic && <Badge variant="secondary">{personalLink.topic.prettyName}</Badge>}
{personalLink.topic && (
<Badge variant="secondary" className="border-muted-foreground/25">
{personalLink.topic.prettyName}
</Badge>
)}
</div>
</li>
)