From 65aa1b2e1dbabb3adfd7d5982e9940d67c4384af Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:37:14 +0530 Subject: [PATCH 1/3] fix: resolve nested path creation from parent listings Fixes: #1745 --- src/lib/openFolder.js | 9 +++ src/utils/helpers.js | 159 ++++++++++++++++++++---------------------- 2 files changed, 85 insertions(+), 83 deletions(-) diff --git a/src/lib/openFolder.js b/src/lib/openFolder.js index 522719de1..437955c3a 100644 --- a/src/lib/openFolder.js +++ b/src/lib/openFolder.js @@ -664,6 +664,7 @@ function execOperation(type, action, url, $target, name) { newName = helpers.fixFilename(newName); if (!newName) return; startLoading(); + const isNestedPath = newName.split("/").filter(Boolean).length > 1; let newUrl; if (action === "new file") { @@ -672,6 +673,14 @@ function execOperation(type, action, url, $target, name) { newUrl = await helpers.createFileStructure(url, newName, false); } if (!newUrl) return; + + if (isNestedPath) { + openFolder.find(url)?.reload(); + await FileList.refresh(); + toast(strings.success); + return; + } + newName = Url.basename(newUrl.uri); if ($target.unclasped) { if (newUrl.type === "file") { diff --git a/src/utils/helpers.js b/src/utils/helpers.js index a925bfabf..bbf6bab88 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -418,73 +418,28 @@ export default { const terminalBasePath = isAcodeTerminalPublicSafUri ? decodeURIComponent(treeSegment.split("::")[0] || "") : ""; - - if (isExternalStorageUri) { - baseFolder = decodeURIComponent(currentUri.split("%3A")[1].split("/")[0]); - } else if ( - !(isExternalStorageUri || isTermuxUri || isAcodeTerminalPublicSafUri) - ) { - // Handle nested paths for regular file:// URIs - const pathParts = pathString.split("/").filter(Boolean); - let currentPath = uri; - let firstCreatedPath = null; - let firstCreatedType = null; - - for (let i = 0; i < pathParts.length; i++) { - const isLastPart = i === pathParts.length - 1; - const partName = pathParts[i]; - const newPath = Url.join(currentPath, partName); - - if (isLastPart && isFile) { - // Create file if it's the last part and we're creating a file - if (!(await fsOperation(newPath).exists())) { - await fsOperation(currentPath).createFile(partName); - if (firstCreatedPath === null) { - firstCreatedPath = newPath; - firstCreatedType = "file"; - } - } - } else { - // Create directory for intermediate parts or when creating a folder - if (!(await fsOperation(newPath).exists())) { - await fsOperation(currentPath).createDirectory(partName); - if (firstCreatedPath === null) { - firstCreatedPath = newPath; - firstCreatedType = "folder"; - } - } - } - currentPath = newPath; + const getTargetUri = (baseUri, name, index) => { + if ( + !(isExternalStorageUri || isTermuxUri || isAcodeTerminalPublicSafUri) + ) { + return Url.join(baseUri, name); } - return { - uri: firstCreatedPath || Url.join(uri, pathParts[0]), - type: - firstCreatedType || - (isFile && pathParts.length === 1 ? "file" : "folder"), - }; - } - - for (let i = 0; i < parts.length; i++) { - const isLastElement = i === parts.length - 1; - const name = parts[i]; - let fullUri = currentUri; - - // Adjust URI for special cases + let fullUri = baseUri; if (isExternalStorageUri) { - if (!isSpecialCase && i === 0) { + if (!isSpecialCase && index === 0) { fullUri += `::primary:${baseFolder}/${name}`; } else { fullUri += `/${name}`; } } else if (isTermuxUri) { - if (!isSpecialCase && i === 0) { + if (!isSpecialCase && index === 0) { fullUri += `::/data/data/com.termux/files/home/${name}`; } else { fullUri += `/${name}`; } } else if (isAcodeTerminalPublicSafUri) { - if (!isSpecialCase && i === 0) { + if (!isSpecialCase && index === 0) { const sanitizedBase = terminalBasePath.endsWith("/") ? `${terminalBasePath}${name}` : `${terminalBasePath}/${name}`; @@ -493,41 +448,79 @@ export default { fullUri += `/${name}`; } } - - if (isLastElement && isFile) { - // Create file if it's the last element and isFile is true - if (!(await fsOperation(fullUri).exists())) { - await fsOperation(currentUri).createFile(name); - } else { - return; - } - } else { - // Create directory - if (!(await fsOperation(fullUri).exists())) { - await fsOperation(currentUri).createDirectory(name); - } else { - return; + return fullUri; + }; + const getExpectedType = (isLastPart) => + isLastPart && isFile ? "file" : "folder"; + const ensureEntry = async (baseUri, targetUri, name, expectedType) => { + const entries = await fsOperation(baseUri).lsDir(); + const existingEntry = entries.find((entry) => entry.name === name); + + if (existingEntry) { + const actualType = + existingEntry.isDirectory || existingEntry.isFile === false + ? "folder" + : "file"; + if (actualType !== expectedType) { + throw new Error( + `${name} already exists as a ${actualType}, expected ${expectedType}.`, + ); } + + return { + url: existingEntry.url || targetUri, + created: false, + type: expectedType, + }; } - currentUri = fullUri; + + const createdUrl = + expectedType === "file" + ? await fsOperation(baseUri).createFile(name) + : await fsOperation(baseUri).createDirectory(name); + + return { + url: createdUrl || targetUri, + created: true, + type: expectedType, + }; + }; + let firstCreatedPath = null; + let firstCreatedType = null; + let firstTargetUri = uri; + + if (isExternalStorageUri) { + baseFolder = decodeURIComponent(currentUri.split("%3A")[1].split("/")[0]); } - let tileType; - if (isFile && parts.length === 1) { - tileType = "file"; - } else { - const urlParts = currentUri.split("/"); - const pathParts = pathString.split("/"); - const pathStartIndex = urlParts.findIndex( - (part) => part === pathParts[0], + if (parts[0]) { + firstTargetUri = getTargetUri(uri, parts[0], 0); + } + + for (let i = 0; i < parts.length; i++) { + const isLastElement = i === parts.length - 1; + const name = parts[i]; + const targetUri = getTargetUri(currentUri, name, i); + const expectedType = getExpectedType(isLastElement); + const entry = await ensureEntry( + currentUri, + targetUri, + name, + expectedType, ); - if (pathStartIndex !== -1) { - const pathEndIndex = pathStartIndex + pathParts.length; - urlParts.splice(pathStartIndex + 1, pathEndIndex - pathStartIndex - 1); + + if (entry.created && firstCreatedPath === null) { + firstCreatedPath = entry.url; + firstCreatedType = expectedType; } - currentUri = urlParts.join("/"); - tileType = "folder"; + + currentUri = entry.url; } - return { uri: currentUri, type: tileType }; + + return { + uri: firstCreatedPath || firstTargetUri, + type: + firstCreatedType || (isFile && parts.length === 1 ? "file" : "folder"), + }; }, formatDownloadCount(downloadCount) { const units = ["", "K", "M", "B", "T"]; From a6cc83f6fa499b32f26a49f4dcb11066ac57a132 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:46:18 +0530 Subject: [PATCH 2/3] address the review concerns --- src/lib/openFolder.js | 52 ++++++++++++++++++++++++------------------- src/utils/helpers.js | 7 ++---- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/lib/openFolder.js b/src/lib/openFolder.js index 437955c3a..dfc19498e 100644 --- a/src/lib/openFolder.js +++ b/src/lib/openFolder.js @@ -664,34 +664,40 @@ function execOperation(type, action, url, $target, name) { newName = helpers.fixFilename(newName); if (!newName) return; startLoading(); - const isNestedPath = newName.split("/").filter(Boolean).length > 1; - let newUrl; + try { + const isNestedPath = newName.split("/").filter(Boolean).length > 1; + let newUrl; - if (action === "new file") { - newUrl = await helpers.createFileStructure(url, newName); - } else { - newUrl = await helpers.createFileStructure(url, newName, false); - } - if (!newUrl) return; + if (action === "new file") { + newUrl = await helpers.createFileStructure(url, newName); + } else { + newUrl = await helpers.createFileStructure(url, newName, false); + } + if (!newUrl) return; - if (isNestedPath) { - openFolder.find(url)?.reload(); - await FileList.refresh(); - toast(strings.success); - return; - } + if (isNestedPath) { + openFolder.find(url)?.reload(); + await FileList.refresh(); + toast(strings.success); + return; + } - newName = Url.basename(newUrl.uri); - if ($target.unclasped) { - if (newUrl.type === "file") { - appendTile($target, createFileTile(newName, newUrl.uri)); - } else if (newUrl.type === "folder") { - appendList($target, createFolderTile(newName, newUrl.uri)); + newName = Url.basename(newUrl.uri); + if ($target.unclasped) { + if (newUrl.type === "file") { + appendTile($target, createFileTile(newName, newUrl.uri)); + } else if (newUrl.type === "folder") { + appendList($target, createFolderTile(newName, newUrl.uri)); + } } - } - FileList.append(url, newUrl.uri); - toast(strings.success); + FileList.append(url, newUrl.uri); + toast(strings.success); + } catch (error) { + helpers.error(error); + } finally { + stopLoading(); + } } async function paste() { diff --git a/src/utils/helpers.js b/src/utils/helpers.js index bbf6bab88..a3d2636e9 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -457,10 +457,7 @@ export default { const existingEntry = entries.find((entry) => entry.name === name); if (existingEntry) { - const actualType = - existingEntry.isDirectory || existingEntry.isFile === false - ? "folder" - : "file"; + const actualType = existingEntry.isDirectory ? "folder" : "file"; if (actualType !== expectedType) { throw new Error( `${name} already exists as a ${actualType}, expected ${expectedType}.`, @@ -468,7 +465,7 @@ export default { } return { - url: existingEntry.url || targetUri, + url: existingEntry.url || existingEntry.uri || targetUri, created: false, type: expectedType, }; From 8f6b155d18a466ed2d08bd6313def5d6ee331468 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:15:29 +0530 Subject: [PATCH 3/3] fix --- src/lib/openFolder.js | 2 +- src/pages/fileBrowser/fileBrowser.js | 2 +- src/utils/helpers.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/openFolder.js b/src/lib/openFolder.js index dfc19498e..712685039 100644 --- a/src/lib/openFolder.js +++ b/src/lib/openFolder.js @@ -673,7 +673,7 @@ function execOperation(type, action, url, $target, name) { } else { newUrl = await helpers.createFileStructure(url, newName, false); } - if (!newUrl) return; + if (!newUrl.created) return; if (isNestedPath) { openFolder.find(url)?.reload(); diff --git a/src/pages/fileBrowser/fileBrowser.js b/src/pages/fileBrowser/fileBrowser.js index 266df102b..0303d7470 100644 --- a/src/pages/fileBrowser/fileBrowser.js +++ b/src/pages/fileBrowser/fileBrowser.js @@ -1270,7 +1270,7 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { if (arg === "file") { newUrl = await helpers.createFileStructure(url, entryName); } - if (!newUrl) return; + if (!newUrl.created) return; return newUrl.uri; } diff --git a/src/utils/helpers.js b/src/utils/helpers.js index a3d2636e9..14ed09ed0 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -515,6 +515,7 @@ export default { return { uri: firstCreatedPath || firstTargetUri, + created: Boolean(firstCreatedPath), type: firstCreatedType || (isFile && parts.length === 1 ? "file" : "folder"), };