Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix open folders in sidebar #55

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions apps/web/src/app/(private)/_data-access/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { cache } from 'react'

import {
getDocumentAtCommit,
findCommitByUuid as originalfindCommit,
findProject as originalFindProject,
getFirstProject as originalGetFirstProject,
type FindCommitByUuidProps,
type FindProjectProps,
type GetDocumentAtCommitProps,
} from '@latitude-data/core'

export const getFirstProject = cache(
Expand Down Expand Up @@ -34,3 +36,12 @@ export const findCommit = cache(
return commit
},
)

export const getDocumentByUuid = cache(
async ({ documentUuid, commitId }: GetDocumentAtCommitProps) => {
const result = await getDocumentAtCommit({ documentUuid, commitId })
const document = result.unwrap()

return document
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import { useRouter } from 'next/navigation'

export default function ClientFilesTree({
documents,
documentUuid,
documentPath,
}: {
documents: SidebarDocument[]
documentPath: string | undefined
documentUuid: string | undefined
}) {
const router = useRouter()
Expand All @@ -35,7 +36,7 @@ export default function ClientFilesTree({
return (
<FilesTree
documents={documents}
currentDocumentUuid={documentUuid}
currentPath={documentPath}
navigateToDocument={navigateToDocument}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ import ClientFilesTree from './ClientFilesTree'
export default async function Sidebar({
commit,
documentUuid,
documentPath,
}: {
commit: Commit
documentPath?: string
documentUuid?: string
}) {
const documents = await getDocumentsAtCommit({ commitId: commit.id })
return (
<Suspense fallback={<div>Loading...</div>}>
<DocumentSidebar>
<ClientFilesTree
documentPath={documentPath}
documents={documents.unwrap()}
documentUuid={documentUuid}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'

import { DocumentDetailWrapper } from '@latitude-data/web-ui'
import { findCommit } from '$/app/(private)/_data-access'
import { findCommit, getDocumentByUuid } from '$/app/(private)/_data-access'

import Sidebar from '../../_components/Sidebar'

Expand All @@ -16,9 +16,17 @@ export default async function DocumentLayout({
projectId: Number(params.projectId),
uuid: params.commitUuid,
})
const document = await getDocumentByUuid({
documentUuid: params.documentUuid,
commitId: commit.id,
})
return (
<DocumentDetailWrapper>
<Sidebar commit={commit} documentUuid={params.documentUuid} />
<Sidebar
commit={commit}
documentUuid={params.documentUuid}
documentPath={document.path}
/>
<div className='p-32'>{children}</div>
</DocumentDetailWrapper>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,34 @@ export async function getDocumentsAtCommit(
return Result.ok(totalDocuments)
}

export type GetDocumentAtCommitProps = {
commitId: number
documentUuid: string
}
export async function getDocumentAtCommit(
{ commitId, documentUuid }: GetDocumentAtCommitProps,
tx = database,
): Promise<TypedResult<DocumentVersion, LatitudeError>> {
const documentInCommit = await tx.query.documentVersions.findFirst({
where: and(
eq(documentVersions.commitId, commitId),
eq(documentVersions.documentUuid, documentUuid),
),
})
if (documentInCommit !== undefined) return Result.ok(documentInCommit)

const documentsAtCommit = await getDocumentsAtCommit({ commitId }, tx)
if (documentsAtCommit.error) return Result.error(documentsAtCommit.error)

const document = documentsAtCommit.value.find(
(d) => d.documentUuid === documentUuid,
)

if (!document) return Result.error(new LatitudeError('Document not found'))

return Result.ok(document)
}

export async function listCommitChanges(
{ commitId }: { commitId: number },
tx = database,
Expand Down
3 changes: 2 additions & 1 deletion packages/web-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"react-dom": "18.3.0",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
"zod": "^3.23.8",
"zustand": "^4.5.4"
},
"devDependencies": {
"@latitude-data/eslint-config": "workspace:*",
Expand Down
78 changes: 55 additions & 23 deletions packages/web-ui/src/sections/Document/Sidebar/Files/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client'

import { ReactNode, useCallback, useState } from 'react'
import { ReactNode, useCallback, useEffect, useState } from 'react'

import { Icons } from '$ui/ds/atoms/Icons'
import Text from '$ui/ds/atoms/Text'
import { ReactStateDispatch } from '$ui/lib/commonTypes'
import { cn } from '$ui/lib/utils'

import { useOpenPaths } from './useOpenPaths'
import { Node, SidebarDocument, useTree } from './useTree'

const ICON_CLASS = 'w-6 h-6 text-muted-foreground'
Expand Down Expand Up @@ -45,20 +45,20 @@ function IndentationBar({

function NodeHeaderWrapper({
open,
node,
selected = false,
children,
indentation,
}: {
open: boolean
selected?: boolean
children: ReactNode
node: Node
indentation: IndentType[]
}) {
return (
<div
className={cn('flex flex-row my-0.5 cursor-pointer', {
'hover:bg-muted': !node.selected,
'bg-accent': node.selected,
'hover:bg-muted': !selected,
'bg-accent': selected,
})}
>
<IndentationBar indentation={indentation} open={open} />
Expand All @@ -70,21 +70,23 @@ function NodeHeaderWrapper({
function FolderHeader({
node,
open,
onClick,
indentation,
}: {
isLast: boolean
node: Node
open: boolean
onClick: ReactStateDispatch<boolean>
indentation: IndentType[]
}) {
const togglePath = useOpenPaths((state) => state.togglePath)
const FolderIcon = open ? Icons.folderOpen : Icons.folderClose
const ChevronIcon = open ? Icons.chevronDown : Icons.chevronRight
const onTooglePath = useCallback(() => {
togglePath(node.path)
}, [togglePath, node.path])
return (
<NodeHeaderWrapper open={open} node={node} indentation={indentation}>
<NodeHeaderWrapper open={open} indentation={indentation}>
<div
onClick={() => onClick(!open)}
onClick={onTooglePath}
className='flex flex-row items-center gap-x-1'
>
<div className='w-6 flex justify-center'>
Expand All @@ -99,32 +101,40 @@ function FolderHeader({

function FileHeader({
open,
selected,
node,
indentation,
navigateToDocument,
}: {
open: boolean
selected: boolean
node: Node
indentation: IndentType[]
navigateToDocument: (documentUuid: string) => void
}) {
const handleClick = useCallback(() => {
if (selected) return

navigateToDocument(node.doc!.documentUuid)
}, [node.doc])
}, [node.doc!.documentUuid, selected])
return (
<NodeHeaderWrapper open={open} node={node} indentation={indentation}>
<NodeHeaderWrapper
open={open}
selected={selected}
indentation={indentation}
>
<div
className='flex flex-row items-center gap-x-1 py-0.5'
onClick={handleClick}
>
<Icons.file
className={cn(ICON_CLASS, {
'text-accent-foreground': node.selected,
'text-accent-foreground': selected,
})}
/>
<Text.H5M
userSelect={false}
color={node.selected ? 'accentForeground' : 'foreground'}
color={selected ? 'accentForeground' : 'foreground'}
>
{node.name}
</Text.H5M>
Expand All @@ -135,16 +145,16 @@ function FileHeader({

function NodeHeader({
isLast,
selected,
node,
open,
onClick,
indentation,
navigateToDocument,
}: {
isLast: boolean
selected: boolean
node: Node
open: boolean
onClick: ReactStateDispatch<boolean>
indentation: IndentType[]
navigateToDocument: (documentUuid: string) => void
}) {
Expand All @@ -153,6 +163,7 @@ function NodeHeader({
return (
<FileHeader
open={open}
selected={selected}
node={node}
indentation={indentation}
navigateToDocument={navigateToDocument}
Expand All @@ -165,7 +176,6 @@ function NodeHeader({
isLast={isLast}
node={node}
open={open}
onClick={onClick}
indentation={indentation}
/>
)
Expand All @@ -174,24 +184,31 @@ function NodeHeader({
function FileNode({
isLast = false,
node,
currentPath,
indentation = [],
navigateToDocument,
}: {
node: Node
currentPath: string | undefined
isLast?: boolean
indentation?: IndentType[]
navigateToDocument: (documentUuid: string) => void
}) {
const [open, setOpen] = useState(node.containsSelected)
const [selected, setSelected] = useState(currentPath === node.path)
const openPaths = useOpenPaths((state) => state.openPaths)
const open = node.isRoot || openPaths.includes(node.path)
const lastIdx = node.children.length - 1
useEffect(() => {
setSelected(currentPath === node.path)
}, [currentPath])
return (
<div className='w-full'>
<NodeHeader
isLast={isLast}
indentation={indentation}
node={node}
selected={selected}
open={open}
onClick={setOpen}
navigateToDocument={navigateToDocument}
/>

Expand All @@ -204,6 +221,7 @@ function FileNode({
{node.children.map((node, idx) => (
<li key={node.id}>
<FileNode
currentPath={currentPath}
indentation={[...indentation, { isLast: idx === lastIdx }]}
node={node}
isLast={idx === lastIdx}
Expand All @@ -218,14 +236,28 @@ function FileNode({
}

export function FilesTree({
currentPath,
documents,
currentDocumentUuid,
navigateToDocument,
}: {
documents: SidebarDocument[]
currentDocumentUuid: string | undefined
currentPath: string | undefined
navigateToDocument: (documentUuid: string) => void
}) {
const rootNode = useTree({ documents, currentDocumentUuid })
return <FileNode node={rootNode} navigateToDocument={navigateToDocument} />
const togglePath = useOpenPaths((state) => state.togglePath)
const rootNode = useTree({ documents })

useEffect(() => {
if (currentPath) {
togglePath(currentPath)
}
}, [currentPath, togglePath])

return (
<FileNode
currentPath={currentPath}
node={rootNode}
navigateToDocument={navigateToDocument}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { act, renderHook } from '@testing-library/react'
import { describe, expect, it } from 'vitest'

import { useOpenPaths } from './index'

describe('useOpenPaths', () => {
it('shout add the paths', async () => {
const { result } = renderHook(() => useOpenPaths((state) => state))
act(() => {
result.current.togglePath('some-folder/nested-folder/doc1')
})

expect(result.current.openPaths).toEqual([
'',
'some-folder',
'some-folder/nested-folder',
'some-folder/nested-folder/doc1',
])
})

it('shout remove nested paths', async () => {
const { result } = renderHook(() => useOpenPaths((state) => state))
act(() => {
result.current.togglePath('some-folder/nested-folder/doc1')
})

act(() => {
result.current.togglePath('some-folder/nested-folder')
})
expect(result.current.openPaths).toEqual(['', 'some-folder'])
})
Comment on lines +21 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

})
Loading
Loading