Skip to content

Commit

Permalink
Rename files
Browse files Browse the repository at this point in the history
  • Loading branch information
csansoon committed Sep 19, 2024
1 parent dd50acf commit 1a96d74
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 10 deletions.
32 changes: 32 additions & 0 deletions apps/web/src/actions/documents/renamePathsAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use server'

import { CommitsRepository } from '@latitude-data/core/repositories'
import { z } from 'zod'

import { withProject } from '../procedures'
import { renameDocumentPaths } from '@latitude-data/core/services/documents/renameDocumentPaths'

export const renameDocumentPathsAction = withProject
.createServerAction()
.input(
z.object({
commitUuid: z.string(),
oldPath: z.string(),
newPath: z.string(),
}),
{ type: 'json' },
)
.handler(async ({ input, ctx }) => {
const commitsScope = new CommitsRepository(ctx.project.workspaceId)
const commit = await commitsScope
.getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id })
.then((r) => r.unwrap())

const result = await renameDocumentPaths({
commit,
oldPath: input.oldPath,
newPath: input.newPath,
})

return result.unwrap()
})
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function ClientFilesTree({
[selectedSegment, project.id, commit.uuid, isHead],
)

const { createFile, destroyFile, destroyFolder, isDestroying, data } =
const { createFile, destroyFile, destroyFolder, renamePaths, isDestroying, data } =
useDocumentVersions(
{ commitUuid: commit.uuid, projectId: project.id },
{
Expand All @@ -68,6 +68,7 @@ export default function ClientFilesTree({
navigateToDocument={navigateToDocument}
onMergeCommitClick={onMergeCommitClick}
createFile={createFile}
renamePaths={renamePaths}
destroyFile={destroyFile}
destroyFolder={destroyFolder}
isDestroying={isDestroying}
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { envClient } from '$/envClient'
import posthog from 'posthog-js'
import { PostHogProvider } from 'posthog-js/react'

if (typeof window !== 'undefined') {
if (
typeof window !== 'undefined' &&
envClient.NEXT_PUBLIC_POSTHOG_KEY &&
envClient.NEXT_PUBLIC_POSTHOG_HOST
) {
posthog.init(envClient.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: envClient.NEXT_PUBLIC_POSTHOG_HOST,
person_profiles: 'identified_only', // or 'always' to create profiles for anonymous users as well
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/envClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const envClient = createEnv({
NEXT_PUBLIC_POSTHOG_HOST: z.string(),
},
runtimeEnv: {
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY ?? '',
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST ?? '',
},
})
27 changes: 27 additions & 0 deletions apps/web/src/stores/documentVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ROUTES } from '$/services/routes'
import { useRouter } from 'next/navigation'
import useSWR, { SWRConfiguration } from 'swr'
import { useServerAction } from 'zsa-react'
import { renameDocumentPathsAction } from '$/actions/documents/renamePathsAction'

export default function useDocumentVersions(
{
Expand All @@ -35,6 +36,9 @@ export default function useDocumentVersions(
},
},
)
const { execute: executeRenamePaths } = useServerAction(
renameDocumentPathsAction,
)
const { execute: executeDestroyDocument, isPending: isDestroyingFile } =
useServerAction(destroyDocumentAction)
const { execute: executeDestroyFolder, isPending: isDestroyingFolder } =
Expand Down Expand Up @@ -105,6 +109,28 @@ export default function useDocumentVersions(
[executeCreateDocument, mutate, data, commitUuid],
)

const renamePaths = useCallback(
async ({ oldPath, newPath }: { oldPath: string; newPath: string }) => {
if (!projectId) return

const [_, error] = await executeRenamePaths({
oldPath,
newPath,
projectId,
commitUuid,
})

if (error) {
toast({
title: 'Error renaming paths',
description: error.formErrors?.[0] || error.message,
variant: 'destructive',
})
}
},
[executeRenamePaths, mutate, data, commitUuid],
)

const destroyFile = useCallback(
async (documentUuid: string) => {
if (!projectId) return
Expand Down Expand Up @@ -190,6 +216,7 @@ export default function useDocumentVersions(
isLoading: isLoading,
error: swrError,
createFile,
renamePaths,
destroyFile,
destroyFolder,
updateContent,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/services/documents/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './destroyDocument'
export * from './destroyFolder'
export * from './recomputeChanges'
export * from './getResolvedContent'
export * from './renameDocumentPaths'
51 changes: 51 additions & 0 deletions packages/core/src/services/documents/renameDocumentPaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Commit, DocumentVersion } from '../../browser'
import { database } from '../../client'
import { findWorkspaceFromCommit } from '../../data-access'
import { Result, Transaction, TypedResult } from '../../lib'
import { BadRequestError } from '../../lib/errors'
import { DocumentVersionsRepository } from '../../repositories'
import { updateDocument } from './update'

export async function renameDocumentPaths(
{
commit,
oldPath,
newPath,
}: {
commit: Commit
oldPath: string
newPath: string
},
db = database,
): Promise<TypedResult<DocumentVersion[], Error>> {
return await Transaction.call(async (tx) => {
if (commit.mergedAt !== null) {
return Result.error(new BadRequestError('Cannot modify a merged commit'))
}

const workspace = await findWorkspaceFromCommit(commit, tx)
const docsScope = new DocumentVersionsRepository(workspace!.id, tx)

const currentDocs = await docsScope
.getDocumentsAtCommit(commit)
.then((r) => r.unwrap())

const docsToUpdate = currentDocs.filter((d) =>
oldPath.endsWith('/') ? d.path.startsWith(oldPath) : d.path === oldPath,
)

const updatedDocs = await Promise.all(
docsToUpdate.map(async (document) => {
const updatedPath = document.path.replace(oldPath, newPath)
const updatedDoc = await updateDocument({
commit,
document,
path: updatedPath,
})
return updatedDoc.unwrap()
}),
)

return Result.ok(updatedDocs)
}, db)
}
2 changes: 2 additions & 0 deletions packages/env/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export const env = createEnv({
SENTRY_DSN: z.string().optional(),
SENTRY_ORG: z.string().optional(),
SENTRY_PROJECT: z.string().optional(),
NEXT_PUBLIC_POSTHOG_KEY: z.string(),
NEXT_PUBLIC_POSTHOG_HOST: z.string(),
},
runtimeEnv: {
...process.env,
Expand Down
2 changes: 2 additions & 0 deletions packages/web-ui/src/ds/atoms/Icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
LoaderCircle,
Lock,
Moon,
Pencil,
RefreshCcw,
SquareDot,
SquareMinus,
Expand Down Expand Up @@ -63,6 +64,7 @@ const Icons = {
sun: Sun,
eye: Eye,
externalLink: ExternalLink,
pencil: Pencil,
refresh: RefreshCcw,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react'
import { useCallback, useMemo, useState } from 'react'

import { Icon } from '../../../../../ds/atoms'
import { MenuOption } from '../../../../../ds/atoms/DropdownMenu'
Expand Down Expand Up @@ -57,8 +57,22 @@ export default function DocumentHeader({

onNavigateToDocument(node.doc!.documentUuid)
}, [node.doc!.documentUuid, selected, node.isPersisted, onNavigateToDocument])
const [isEditing, setIsEditing] = useState(node.name === " ")
const actions = useMemo<MenuOption[]>(
() => [
{
label: 'Rename',
disabled: isMerged,
iconProps: { name: 'pencil' },
onClick: () => {
if (isMerged) {
onMergeCommitClick()
return
}

setIsEditing(true)
},
},
{
label: 'Delete file',
type: 'destructive',
Expand All @@ -80,6 +94,8 @@ export default function DocumentHeader({
isFile
open={open}
name={node.name}
isEditing={isEditing}
setIsEditing={setIsEditing}
hasChildren={false}
actions={actions}
selected={selected}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react'
import { useCallback, useMemo, useState } from 'react'

import { Icon } from '../../../../../ds/atoms'
import { MenuOption } from '../../../../../ds/atoms/DropdownMenu'
Expand Down Expand Up @@ -72,8 +72,22 @@ export default function FolderHeader({
},
[node.path, togglePath, open, isMerged, onMergeCommitClick, addFolder],
)
const [isEditing, setIsEditing] = useState(node.name === ' ')
const actions = useMemo<MenuOption[]>(
() => [
{
label: 'Rename',
disabled: isMerged,
iconProps: { name: 'pencil' },
onClick: () => {
if (isMerged) {
onMergeCommitClick()
return
}

setIsEditing(true)
},
},
{
label: 'New folder',
disabled: isMerged,
Expand Down Expand Up @@ -120,6 +134,8 @@ export default function FolderHeader({
return (
<NodeHeaderWrapper
name={node.name}
isEditing={isEditing}
setIsEditing={setIsEditing}
hasChildren={node.children.length > 0}
onClick={onToggleOpen}
onSaveValue={({ path }) => updateFolder({ id: node.id, path })}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type Props = {
hasChildren?: boolean
isFile?: boolean
selected?: boolean
isEditing: boolean
setIsEditing: (isEditing: boolean) => void
onClick?: () => void
actions?: MenuOption[]
icons: ReactNode
Expand All @@ -60,6 +62,8 @@ function NodeHeaderWrapper({
open,
hasChildren = false,
isFile = false,
isEditing,
setIsEditing,
onSaveValue,
onSaveValueAndTab,
onLeaveWithoutSave,
Expand All @@ -72,10 +76,12 @@ function NodeHeaderWrapper({
const [tmpName, setTmpName] = useState(name)
const inputRef = useRef<HTMLInputElement>(null)
const nodeRef = useRef<HTMLDivElement>(null)
const { isEditing, error, onInputChange, onInputKeyDown } = useNodeValidator({
const { error, inputValue, onInputChange, onInputKeyDown } = useNodeValidator({
name,
nodeRef,
inputRef,
isEditing,
setIsEditing,
saveValue: ({ path }) => {
setTmpName(path)
onSaveValue({ path })
Expand Down Expand Up @@ -123,6 +129,7 @@ function NodeHeaderWrapper({
tabIndex={0}
ref={inputRef}
autoFocus
value={inputValue}
onKeyDown={onInputKeyDown}
onChange={onInputChange}
errors={error ? [error] : undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,23 @@ export function useNodeValidator({
name,
inputRef,
nodeRef,
isEditing,
setIsEditing,
leaveWithoutSave,
saveValue,
saveAndAddOther,
}: {
name: string | undefined
inputRef: RefObject<HTMLInputElement>
nodeRef: RefObject<HTMLDivElement>
isEditing: boolean
setIsEditing: (isEditing: boolean) => void
saveValue: (args: { path: string }) => Promise<void> | void
saveAndAddOther?: (args: { path: string }) => void
leaveWithoutSave?: () => void
}) {
const [isEditing, setIsEditing] = useState(name === ' ')
const [validationError, setError] = useState<string>()
const [inputValue, setInputValue] = useState(name)
const onInputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
(event) => {
const value = event.target.value
Expand All @@ -70,8 +74,9 @@ export function useNodeValidator({
}

setError(error)
setInputValue(value)
},
[setError],
[setError, setInputValue],
)
const onClickOutside = useCallback(async () => {
const val = inputRef.current?.value ?? ''
Expand Down Expand Up @@ -119,6 +124,7 @@ export function useNodeValidator({
)

return {
inputValue,
isEditing,
onInputChange,
onInputKeyDown,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export function TreeToolbar() {
open={false}
hasChildren={false}
isFile={isFile}
name=' '
name=''
isEditing={true}
setIsEditing={() => {}}
icons={isFile ? <DocumentIcon /> : <FolderIcons open={false} />}
indentation={[{ isLast: true }]}
onSaveValue={async ({ path }) => {
Expand Down
Loading

0 comments on commit 1a96d74

Please sign in to comment.