Skip to content

Commit 71cdf2a

Browse files
authored
Merge pull request #469 from reorproject/file-sidebar-cleanup
File sidebar cleanup
2 parents 00aab77 + 8051147 commit 71cdf2a

File tree

13 files changed

+251
-200
lines changed

13 files changed

+251
-200
lines changed

electron/main/common/windowManager.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,18 @@ class WindowsManager {
8787
}
8888

8989
appendNewErrorToDisplayInWindow(errorString: string) {
90-
this.errorStringsToSendWindow.push(errorString)
90+
let errorSent = false
91+
const activeWindows = BrowserWindow.getAllWindows()
92+
activeWindows.forEach((window) => {
93+
if (!window.webContents.isLoading()) {
94+
window.webContents.send('error-to-display-in-window', errorString)
95+
errorSent = true
96+
}
97+
})
98+
99+
if (!errorSent) {
100+
this.errorStringsToSendWindow.push(errorString)
101+
}
91102
}
92103

93104
getAndClearErrorStrings(): string[] {

electron/main/filesystem/filesystem.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as fs from 'fs'
2-
import * as fsPromises from 'fs/promises'
32
import * as path from 'path'
43

54
import chokidar from 'chokidar'
@@ -178,28 +177,6 @@ export function readFile(filePath: string): string {
178177
return data
179178
}
180179

181-
export const moveFileOrDirectoryInFileSystem = async (sourcePath: string, destinationPath: string): Promise<string> => {
182-
await fsPromises.access(sourcePath)
183-
184-
let destinationStats
185-
try {
186-
destinationStats = await fsPromises.lstat(destinationPath)
187-
} catch (error) {
188-
// Error means destination path does not exist, which is fine
189-
}
190-
let resolvedDestinationPath = destinationPath
191-
if (destinationStats && destinationStats.isFile()) {
192-
resolvedDestinationPath = path.dirname(destinationPath)
193-
}
194-
195-
await fsPromises.mkdir(resolvedDestinationPath, { recursive: true })
196-
197-
const newPath = path.join(resolvedDestinationPath, path.basename(sourcePath))
198-
await fsPromises.rename(sourcePath, newPath)
199-
200-
return newPath
201-
}
202-
203180
export function splitDirectoryPathIntoBaseAndRepo(fullPath: string) {
204181
const normalizedPath = path.normalize(fullPath)
205182

electron/main/filesystem/ipcHandlers.ts

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
import * as fs from 'fs'
22
import * as path from 'path'
33

4-
import { ipcMain, BrowserWindow, dialog } from 'electron'
4+
import { ipcMain, dialog } from 'electron'
55
import Store from 'electron-store'
66

77
import WindowsManager from '../common/windowManager'
88
import { StoreSchema } from '../electron-store/storeConfig'
9-
import { orchestrateEntryMove, updateFileInTable } from '../vector-database/tableHelperFunctions'
10-
11-
import {
12-
GetFilesInfoTree,
13-
createFileRecursive,
14-
isHidden,
15-
GetFilesInfoListForListOfPaths,
16-
startWatchingDirectory,
17-
updateFileListForRenderer,
18-
} from './filesystem'
9+
import { handleFileRename, updateFileInTable } from '../vector-database/tableHelperFunctions'
10+
11+
import { GetFilesInfoTree, createFileRecursive, isHidden, GetFilesInfoListForListOfPaths } from './filesystem'
1912
import { FileInfoTree, WriteFileProps, RenameFileProps, FileInfoWithContent } from './types'
2013

2114
const registerFileHandlers = (store: Store<StoreSchema>, _windowsManager: WindowsManager) => {
@@ -91,36 +84,7 @@ const registerFileHandlers = (store: Store<StoreSchema>, _windowsManager: Window
9184
throw new Error('Window info not found.')
9285
}
9386

94-
windowsManager.watcher?.unwatch(windowInfo?.vaultDirectoryForWindow)
95-
96-
if (process.platform === 'win32') {
97-
windowsManager.watcher?.close().then(() => {
98-
fs.rename(renameFileProps.oldFilePath, renameFileProps.newFilePath, (err) => {
99-
if (err) {
100-
throw err
101-
}
102-
103-
// Re-start watching all paths in array
104-
const win = BrowserWindow.fromWebContents(event.sender)
105-
if (win) {
106-
windowsManager.watcher = startWatchingDirectory(win, windowInfo.vaultDirectoryForWindow)
107-
updateFileListForRenderer(win, windowInfo.vaultDirectoryForWindow)
108-
}
109-
})
110-
})
111-
} else {
112-
// On non-Windows platforms, directly perform the rename operation
113-
fs.rename(renameFileProps.oldFilePath, renameFileProps.newFilePath, (err) => {
114-
if (err) {
115-
throw err
116-
}
117-
// Re-watch the vault directory after renaming
118-
windowsManager.watcher?.add(windowInfo?.vaultDirectoryForWindow)
119-
})
120-
}
121-
122-
// then need to trigger reindexing of folder
123-
windowInfo.dbTableClient.updateDBItemsWithNewFilePath(renameFileProps.oldFilePath, renameFileProps.newFilePath)
87+
await handleFileRename(windowsManager, windowInfo, renameFileProps, event.sender)
12488
})
12589

12690
ipcMain.handle('index-file-in-database', async (event, filePath: string) => {
@@ -151,14 +115,6 @@ const registerFileHandlers = (store: Store<StoreSchema>, _windowsManager: Window
151115
}
152116
})
153117

154-
ipcMain.handle('move-file-or-dir', async (event, sourcePath: string, destinationPath: string) => {
155-
const windowInfo = windowsManager.getWindowInfoForContents(event.sender)
156-
if (!windowInfo) {
157-
throw new Error('Window info not found.')
158-
}
159-
orchestrateEntryMove(windowInfo.dbTableClient, sourcePath, destinationPath)
160-
})
161-
162118
ipcMain.handle('get-files', async (_event, filePaths: string[]): Promise<FileInfoWithContent[]> => {
163119
const fileItems = GetFilesInfoListForListOfPaths(filePaths)
164120
const fileContents = fileItems.map((fileItem) => fs.readFileSync(fileItem.path, 'utf-8'))

electron/main/path/ipcHandlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const registerPathHandlers = () => {
1313

1414
ipcMain.handle('join-path', (event, ...args) => path.join(...args))
1515

16-
ipcMain.handle('path-dirname', (event, pathString: string) => path.dirname(pathString) + path.sep)
16+
ipcMain.handle('path-dirname', (event, pathString: string) => path.dirname(pathString))
1717

1818
ipcMain.handle('path-relative', (event, from: string, to: string) => path.relative(from, to))
1919

electron/main/vector-database/tableHelperFunctions.ts

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import * as fs from 'fs'
2+
import * as fsPromises from 'fs/promises'
23

4+
import { BrowserWindow } from 'electron'
35
import { chunkMarkdownByHeadingsAndByCharsIfBig } from '../common/chunking'
46
import {
57
GetFilesInfoList,
6-
GetFilesInfoTree,
78
flattenFileInfoTree,
8-
moveFileOrDirectoryInFileSystem,
99
readFile,
10+
updateFileListForRenderer,
11+
startWatchingDirectory,
1012
} from '../filesystem/filesystem'
11-
import { FileInfo, FileInfoTree } from '../filesystem/types'
13+
import { FileInfo, FileInfoTree, RenameFileProps } from '../filesystem/types'
1214

1315
import LanceDBTableWrapper, { convertRecordToDBType } from './lanceTableWrapper'
1416
import { DBEntry, DatabaseFields } from './schema'
17+
import WindowsManager from '../common/windowManager'
1518

1619
const convertFileTypeToDBType = async (file: FileInfo): Promise<DBEntry[]> => {
1720
const fileContent = readFile(file.path)
@@ -27,6 +30,58 @@ const convertFileTypeToDBType = async (file: FileInfo): Promise<DBEntry[]> => {
2730
return entries
2831
}
2932

33+
export const handleFileRename = async (
34+
windowsManager: WindowsManager,
35+
windowInfo: { vaultDirectoryForWindow: string; dbTableClient: any },
36+
renameFileProps: RenameFileProps,
37+
sender: Electron.WebContents,
38+
): Promise<void> => {
39+
windowsManager.watcher?.unwatch(windowInfo.vaultDirectoryForWindow)
40+
41+
try {
42+
await fsPromises.access(renameFileProps.newFilePath)
43+
throw new Error(`A file already exists at destination: ${renameFileProps.newFilePath}`)
44+
} catch (error) {
45+
// If error is ENOENT (file doesn't exist), proceed with rename
46+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
47+
throw error
48+
}
49+
}
50+
51+
if (process.platform === 'win32') {
52+
await windowsManager.watcher?.close()
53+
await new Promise<void>((resolve, reject) => {
54+
fs.rename(renameFileProps.oldFilePath, renameFileProps.newFilePath, (err) => {
55+
if (err) {
56+
reject(err)
57+
return
58+
}
59+
60+
const win = BrowserWindow.fromWebContents(sender)
61+
if (win) {
62+
// eslint-disable-next-line no-param-reassign
63+
windowsManager.watcher = startWatchingDirectory(win, windowInfo.vaultDirectoryForWindow)
64+
updateFileListForRenderer(win, windowInfo.vaultDirectoryForWindow)
65+
}
66+
resolve()
67+
})
68+
})
69+
} else {
70+
await new Promise<void>((resolve, reject) => {
71+
fs.rename(renameFileProps.oldFilePath, renameFileProps.newFilePath, (err) => {
72+
if (err) {
73+
reject(err)
74+
return
75+
}
76+
windowsManager.watcher?.add(windowInfo.vaultDirectoryForWindow)
77+
resolve()
78+
})
79+
})
80+
}
81+
82+
await windowInfo.dbTableClient.updateDBItemsWithNewFilePath(renameFileProps.oldFilePath, renameFileProps.newFilePath)
83+
}
84+
3085
export const convertFileInfoListToDBItems = async (filesInfoList: FileInfo[]): Promise<DBEntry[][]> => {
3186
const promises = filesInfoList.map(convertFileTypeToDBType)
3287
const filesAsChunksToAddToDB = await Promise.all(promises)
@@ -157,16 +212,6 @@ export const addFileTreeToDBTable = async (dbTable: LanceDBTableWrapper, fileTre
157212
await dbTable.add(dbEntries)
158213
}
159214

160-
export const orchestrateEntryMove = async (table: LanceDBTableWrapper, sourcePath: string, destinationPath: string) => {
161-
const fileSystemTree = GetFilesInfoTree(sourcePath)
162-
await removeFileTreeFromDBTable(table, fileSystemTree)
163-
moveFileOrDirectoryInFileSystem(sourcePath, destinationPath).then((newDestinationPath) => {
164-
if (newDestinationPath) {
165-
addFileTreeToDBTable(table, GetFilesInfoTree(newDestinationPath))
166-
}
167-
})
168-
}
169-
170215
export function formatTimestampForLanceDB(date: Date): string {
171216
const year = date.getFullYear()
172217
const month = date.getMonth() + 1 // getMonth() is zero-based

electron/preload/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ const fileSystem = {
9090
createDirectory: createIPCHandler<(dirPath: string) => Promise<void>>('create-directory'),
9191
checkFileExists: createIPCHandler<(filePath: string) => Promise<boolean>>('check-file-exists'),
9292
deleteFile: createIPCHandler<(filePath: string) => Promise<void>>('delete-file'),
93-
moveFileOrDir: createIPCHandler<(sourcePath: string, destinationPath: string) => Promise<void>>('move-file-or-dir'),
9493
getAllFilenamesInDirectory: createIPCHandler<(dirName: string) => Promise<string[]>>('get-files-in-directory'),
9594
getFiles: createIPCHandler<(filePaths: string[]) => Promise<FileInfoWithContent[]>>('get-files'),
9695
}

src/components/Common/Modal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ const ReorModal: React.FC<ModalProps> = ({
3636

3737
const modalContent = (
3838
<div
39-
className={`fixed inset-0 flex h-screen w-screen items-center justify-center bg-black/40 ${tailwindStylesOnBackground}`}
39+
className={`fixed inset-0 z-[9999] flex h-screen w-screen items-center justify-center bg-black/40 ${tailwindStylesOnBackground}`}
4040
>
4141
<div
4242
ref={modalRef}
43-
className="flex flex-col items-center justify-center rounded-lg border border-solid border-gray-700 bg-dark-gray-c-three shadow-xl"
43+
className="relative flex flex-col items-center justify-center rounded-lg border border-solid border-gray-700 bg-dark-gray-c-three shadow-xl"
4444
>
4545
<div className="z-50 h-0 w-full items-end">
4646
{!hideCloseButton && (

src/components/File/NewDirectory.tsx

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,37 @@ interface NewDirectoryComponentProps {
1414
}
1515

1616
const NewDirectoryComponent: React.FC<NewDirectoryComponentProps> = ({ isOpen, onClose, parentDirectoryPath }) => {
17-
const [directoryName, setDirectoryName] = useState<string>('')
17+
const [directoryRelativePath, setDirectoryRelativePath] = useState<string>('')
1818
const [errorMessage, setErrorMessage] = useState<string | null>(null)
1919

20-
const { currentlyOpenFilePath } = useFileContext()
20+
const { selectedDirectory } = useFileContext()
21+
22+
useEffect(() => {
23+
const setupInitialPath = async () => {
24+
const vaultDirectory = await window.electronStore.getVaultDirectoryForWindow()
25+
26+
let fullPath = ''
27+
if (parentDirectoryPath) {
28+
fullPath = parentDirectoryPath
29+
} else if (selectedDirectory) {
30+
fullPath = selectedDirectory
31+
}
32+
33+
if (fullPath) {
34+
const relativePath = await window.path.relative(vaultDirectory, fullPath)
35+
const pathWithSeparator = relativePath ? `${relativePath}${await window.path.pathSep()}` : ''
36+
setDirectoryRelativePath(pathWithSeparator)
37+
}
38+
}
39+
40+
if (isOpen) {
41+
setupInitialPath()
42+
}
43+
}, [isOpen, parentDirectoryPath, selectedDirectory])
2144

2245
useEffect(() => {
2346
if (!isOpen) {
24-
setDirectoryName('')
47+
setDirectoryRelativePath('')
2548
setErrorMessage(null)
2649
}
2750
}, [isOpen])
@@ -39,24 +62,25 @@ const NewDirectoryComponent: React.FC<NewDirectoryComponentProps> = ({ isOpen, o
3962
const handleNameChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
4063
const newName = e.target.value
4164
await handleValidName(newName)
42-
setDirectoryName(newName)
65+
setDirectoryRelativePath(newName)
4366
}
4467

45-
const sendNewDirectoryMsg = async () => {
46-
const validName = await handleValidName(directoryName)
47-
if (!directoryName || errorMessage || !validName) return
68+
const createNewDirectory = async () => {
69+
const validName = await handleValidName(directoryRelativePath)
70+
if (!directoryRelativePath || errorMessage || !validName) return
4871

49-
let directoryPath: string
72+
// let directoryPath: string
73+
const directoryPath = await window.electronStore.getVaultDirectoryForWindow()
5074

51-
if (parentDirectoryPath) {
52-
directoryPath = parentDirectoryPath
53-
} else if (currentlyOpenFilePath && currentlyOpenFilePath !== '') {
54-
directoryPath = await window.path.dirname(currentlyOpenFilePath)
55-
} else {
56-
directoryPath = await window.electronStore.getVaultDirectoryForWindow()
57-
}
75+
// if (parentDirectoryPath) {
76+
// directoryPath = parentDirectoryPath
77+
// } else if (currentlyOpenFilePath && currentlyOpenFilePath !== '') {
78+
// directoryPath = await window.path.dirname(currentlyOpenFilePath)
79+
// } else {
80+
// directoryPath = await window.electronStore.getVaultDirectoryForWindow()
81+
// }
5882

59-
const finalPath = await window.path.join(directoryPath, directoryName)
83+
const finalPath = await window.path.join(directoryPath, directoryRelativePath)
6084
window.fileSystem.createDirectory(finalPath)
6185
posthog.capture('created_new_directory_from_new_directory_modal')
6286
onClose()
@@ -69,11 +93,11 @@ const NewDirectoryComponent: React.FC<NewDirectoryComponentProps> = ({ isOpen, o
6993
<input
7094
type="text"
7195
className=" block w-full rounded-md border border-gray-300 px-3 py-2 transition duration-150 ease-in-out focus:border-blue-300 focus:outline-none"
72-
value={directoryName}
96+
value={directoryRelativePath}
7397
onChange={handleNameChange}
7498
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
7599
if (e.key === 'Enter') {
76-
sendNewDirectoryMsg()
100+
createNewDirectory()
77101
}
78102
}}
79103
placeholder="Directory Name"
@@ -84,7 +108,7 @@ const NewDirectoryComponent: React.FC<NewDirectoryComponentProps> = ({ isOpen, o
84108
<div className="flex items-center gap-3">
85109
<Button
86110
className="mb-2 mt-3 h-10 w-[80px] cursor-pointer border-none bg-blue-500 px-2 py-0 text-center hover:bg-blue-600"
87-
onClick={sendNewDirectoryMsg}
111+
onClick={createNewDirectory}
88112
placeholder=""
89113
>
90114
Create

0 commit comments

Comments
 (0)