fix: reload the entire file list after adding or deleting files

This commit is contained in:
James Murdza 2024-11-17 13:25:16 -05:00
parent 062e8d9226
commit 07d3802baa
5 changed files with 20 additions and 141 deletions

View File

@ -125,6 +125,19 @@ export class FileManager {
return this.files return this.files
} }
private async loadLocalFiles() {
// Reload file list from the container to include template files
const result = await this.sandbox.commands.run(
`find "${this.dirName}" -type f`
) // List all files recursively
const localPaths = result.stdout.split("\n").filter((path) => path) // Split the output into an array and filter out empty strings
const relativePaths = localPaths.map((filePath) =>
path.posix.relative(this.dirName, filePath)
) // Convert absolute paths to relative paths
this.files = generateFileStructure(relativePaths)
}
// Initialize the FileManager // Initialize the FileManager
async initialize() { async initialize() {
// Download files from remote file storage // Download files from remote file storage
@ -146,15 +159,7 @@ export class FileManager {
}) })
await Promise.all(promises) await Promise.all(promises)
// Reload file list from the container to include template files await this.loadLocalFiles()
const result = await this.sandbox.commands.run(
`find "${this.dirName}" -type f`
) // List all files recursively
const localPaths = result.stdout.split("\n").filter((path) => path) // Split the output into an array and filter out empty strings
const relativePaths = localPaths.map((filePath) =>
path.posix.relative(this.dirName, filePath)
) // Convert absolute paths to relative paths
this.files = generateFileStructure(relativePaths)
// Make the logged in user the owner of all project files // Make the logged in user the owner of all project files
this.fixPermissions() this.fixPermissions()
@ -221,77 +226,13 @@ export class FileManager {
// Handle file/directory creation event // Handle file/directory creation event
if (event.type === "create") { if (event.type === "create") {
const folder = findFolderById( await this.loadLocalFiles()
this.files,
sandboxDirectory
) as TFolder
const isDir = await this.isDirectory(containerFilePath)
const newItem = isDir
? ({
id: sandboxFilePath,
name: event.name,
type: "folder",
children: [],
} as TFolder)
: ({
id: sandboxFilePath,
name: event.name,
type: "file",
} as TFile)
if (folder) {
// If the folder exists, add the new item (file/folder) as a child
folder.children.push(newItem)
} else {
// If folder doesn't exist, add the new item to the root
this.files.push(newItem)
}
if (!isDir) {
const fileData = await this.sandbox.files.read(
containerFilePath
)
const fileContents =
typeof fileData === "string" ? fileData : ""
this.fileData.push({
id: sandboxFilePath,
data: fileContents,
})
}
console.log(`Create ${sandboxFilePath}`) console.log(`Create ${sandboxFilePath}`)
} }
// Handle file/directory removal or rename event // Handle file/directory removal or rename event
else if (event.type === "remove" || event.type == "rename") { else if (event.type === "remove" || event.type == "rename") {
const folder = findFolderById( await this.loadLocalFiles()
this.files,
sandboxDirectory
) as TFolder
const isDir = await this.isDirectory(containerFilePath)
const isFileMatch = (file: TFolder | TFile | TFileData) =>
file.id === sandboxFilePath ||
file.id.startsWith(containerFilePath + "/")
if (folder) {
// Remove item from its parent folder
folder.children = folder.children.filter(
(file: TFolder | TFile) => !isFileMatch(file)
)
} else {
// Remove from the root if it's not inside a folder
this.files = this.files.filter(
(file: TFolder | TFile) => !isFileMatch(file)
)
}
// Also remove any corresponding file data
this.fileData = this.fileData.filter(
(file: TFileData) => !isFileMatch(file)
)
console.log(`Removed: ${sandboxFilePath}`) console.log(`Removed: ${sandboxFilePath}`)
} }
@ -458,17 +399,6 @@ export class FileManager {
await this.sandbox.files.write(path.posix.join(this.dirName, id), "") await this.sandbox.files.write(path.posix.join(this.dirName, id), "")
await this.fixPermissions() await this.fixPermissions()
this.files.push({
id,
name,
type: "file",
})
this.fileData.push({
id,
data: "",
})
await RemoteFileStorage.createFile(this.getRemoteFileId(id)) await RemoteFileStorage.createFile(this.getRemoteFileId(id))
return true return true
@ -568,7 +498,6 @@ export class FileManager {
if (!file) return this.files if (!file) return this.files
await this.sandbox.files.remove(path.posix.join(this.dirName, fileId)) await this.sandbox.files.remove(path.posix.join(this.dirName, fileId))
this.fileData = this.fileData.filter((f) => f.id !== fileId)
await RemoteFileStorage.deleteFile(this.getRemoteFileId(fileId)) await RemoteFileStorage.deleteFile(this.getRemoteFileId(fileId))
return this.updateFileStructure() return this.updateFileStructure()
@ -583,7 +512,6 @@ export class FileManager {
await Promise.all( await Promise.all(
files.map(async (file) => { files.map(async (file) => {
await this.sandbox.files.remove(path.posix.join(this.dirName, file)) await this.sandbox.files.remove(path.posix.join(this.dirName, file))
this.fileData = this.fileData.filter((f) => f.id !== file)
await RemoteFileStorage.deleteFile(this.getRemoteFileId(file)) await RemoteFileStorage.deleteFile(this.getRemoteFileId(file))
}) })
) )

View File

@ -23,7 +23,6 @@ import { useSocket } from "@/context/SocketContext"
import { parseTSConfigToMonacoOptions } from "@/lib/tsconfig" import { parseTSConfigToMonacoOptions } from "@/lib/tsconfig"
import { Sandbox, TFile, TFolder, TTab, User } from "@/lib/types" import { Sandbox, TFile, TFolder, TTab, User } from "@/lib/types"
import { import {
addNew,
cn, cn,
debounce, debounce,
deepMerge, deepMerge,
@ -856,13 +855,7 @@ export default function CodeEditor({
} }
const handleDeleteFile = (file: TFile) => { const handleDeleteFile = (file: TFile) => {
socket?.emit( socket?.emit("deleteFile", { fileId: file.id })
"deleteFile",
{ fileId: file.id },
(response: (TFolder | TFile)[]) => {
setFiles(response)
}
)
closeTab(file.id) closeTab(file.id)
} }
@ -878,7 +871,6 @@ export default function CodeEditor({
"deleteFolder", "deleteFolder",
{ folderId: folder.id }, { folderId: folder.id },
(response: (TFolder | TFile)[]) => { (response: (TFolder | TFile)[]) => {
setFiles(response)
setDeletingFolderId("") setDeletingFolderId("")
} }
) )
@ -1047,7 +1039,6 @@ export default function CodeEditor({
handleDeleteFolder={handleDeleteFolder} handleDeleteFolder={handleDeleteFolder}
socket={socket!} socket={socket!}
setFiles={setFiles} setFiles={setFiles}
addNew={(name, type) => addNew(name, type, setFiles, sandboxData)}
deletingFolderId={deletingFolderId} deletingFolderId={deletingFolderId}
toggleAIChat={toggleAIChat} toggleAIChat={toggleAIChat}
isAIChatOpen={isAIChatOpen} isAIChatOpen={isAIChatOpen}

View File

@ -25,7 +25,6 @@ export default function Sidebar({
handleDeleteFolder, handleDeleteFolder,
socket, socket,
setFiles, setFiles,
addNew,
deletingFolderId, deletingFolderId,
toggleAIChat, toggleAIChat,
isAIChatOpen, isAIChatOpen,
@ -43,7 +42,6 @@ export default function Sidebar({
handleDeleteFolder: (folder: TFolder) => void handleDeleteFolder: (folder: TFolder) => void
socket: Socket socket: Socket
setFiles: (files: (TFile | TFolder)[]) => void setFiles: (files: (TFile | TFolder)[]) => void
addNew: (name: string, type: "file" | "folder") => void
deletingFolderId: string deletingFolderId: string
toggleAIChat: () => void toggleAIChat: () => void
isAIChatOpen: boolean isAIChatOpen: boolean
@ -176,7 +174,6 @@ export default function Sidebar({
stopEditing={() => { stopEditing={() => {
setCreatingNew(null) setCreatingNew(null)
}} }}
addNew={addNew}
/> />
) : null} ) : null}
</> </>

View File

@ -9,12 +9,10 @@ export default function New({
socket, socket,
type, type,
stopEditing, stopEditing,
addNew,
}: { }: {
socket: Socket socket: Socket
type: "file" | "folder" type: "file" | "folder"
stopEditing: () => void stopEditing: () => void
addNew: (name: string, type: "file" | "folder") => void
}) { }) {
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
@ -25,19 +23,9 @@ export default function New({
const valid = validateName(name, "", type) const valid = validateName(name, "", type)
if (valid.status) { if (valid.status) {
if (type === "file") { if (type === "file") {
socket.emit( socket.emit("createFile", { name })
"createFile",
{ name },
({ success }: { success: boolean }) => {
if (success) {
addNew(name, type)
}
}
)
} else { } else {
socket.emit("createFolder", { name }, () => { socket.emit("createFolder", { name })
addNew(name, type)
})
} }
} }
} }

View File

@ -2,7 +2,7 @@ import { type ClassValue, clsx } from "clsx"
// import { toast } from "sonner" // import { toast } from "sonner"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
import fileExtToLang from "./file-extension-to-language.json" import fileExtToLang from "./file-extension-to-language.json"
import { Sandbox, TFile, TFolder } from "./types" import { TFile, TFolder } from "./types"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
@ -38,31 +38,6 @@ export function validateName(
return { status: true, message: "" } return { status: true, message: "" }
} }
export function addNew(
name: string,
type: "file" | "folder",
setFiles: React.Dispatch<React.SetStateAction<(TFolder | TFile)[]>>,
sandboxData: Sandbox
) {
if (type === "file") {
setFiles((prev) => [
...prev,
{ id: `projects/${sandboxData.id}/${name}`, name, type: "file" },
])
} else {
console.log("adding folder")
setFiles((prev) => [
...prev,
{
id: `projects/${sandboxData.id}/${name}`,
name,
type: "folder",
children: [],
},
])
}
}
export function debounce<T extends (...args: any[]) => void>( export function debounce<T extends (...args: any[]) => void>(
func: T, func: T,
wait: number wait: number