diff --git a/src/lang/ar-ye.json b/src/lang/ar-ye.json index 6592c7c46..4bfc2dfd5 100644 --- a/src/lang/ar-ye.json +++ b/src/lang/ar-ye.json @@ -382,5 +382,9 @@ "most_downloaded": "الأكثر تنزيلًا", "newly_added": "أضاف حديثا", "top_rated": "أعلى تصنيف", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/be-by.json b/src/lang/be-by.json index 24f960eb9..200590f1f 100644 --- a/src/lang/be-by.json +++ b/src/lang/be-by.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/bn-bd.json b/src/lang/bn-bd.json index dd6fe6620..bc99db949 100644 --- a/src/lang/bn-bd.json +++ b/src/lang/bn-bd.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/cs-cz.json b/src/lang/cs-cz.json index a6a95c49d..0ff0d691f 100644 --- a/src/lang/cs-cz.json +++ b/src/lang/cs-cz.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/de-de.json b/src/lang/de-de.json index 9e5aa1ad3..9472dc5d0 100644 --- a/src/lang/de-de.json +++ b/src/lang/de-de.json @@ -382,5 +382,9 @@ "most_downloaded": "Meist Heruntergeladene", "newly_added": "Neu Hinzugefügte", "top_rated": "Am besten Bewertete", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/en-us.json b/src/lang/en-us.json index af1232d86..e0898c26b 100644 --- a/src/lang/en-us.json +++ b/src/lang/en-us.json @@ -383,5 +383,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/es-sv.json b/src/lang/es-sv.json index 1616b6fa6..5ed698655 100644 --- a/src/lang/es-sv.json +++ b/src/lang/es-sv.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/fr-fr.json b/src/lang/fr-fr.json index fae119d90..1a7ac1b61 100644 --- a/src/lang/fr-fr.json +++ b/src/lang/fr-fr.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/hi-in.json b/src/lang/hi-in.json index 94f9859b8..8888f8b47 100644 --- a/src/lang/hi-in.json +++ b/src/lang/hi-in.json @@ -383,5 +383,9 @@ "most_downloaded": "सर्वाधिक डाउनलोड", "newly_added": "नया नया़ा", "top_rated": "टॉप रेटेड", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/hu-hu.json b/src/lang/hu-hu.json index 95e8d46ea..72c5b34c4 100644 --- a/src/lang/hu-hu.json +++ b/src/lang/hu-hu.json @@ -382,5 +382,9 @@ "most_downloaded": "Legtöbbször letöltött", "newly_added": "Újonnan hozzáadott", "top_rated": "Legjobbra értékelt", - "rename not supported": "A Termux könyvtár átnevezése nem támogatott " + "rename not supported": "A Termux könyvtár átnevezése nem támogatott ", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/id-id.json b/src/lang/id-id.json index 2175eba8b..6e1164580 100644 --- a/src/lang/id-id.json +++ b/src/lang/id-id.json @@ -384,5 +384,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/ir-fa.json b/src/lang/ir-fa.json index 7bb634fe3..8bbe96f4e 100644 --- a/src/lang/ir-fa.json +++ b/src/lang/ir-fa.json @@ -383,5 +383,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/it-it.json b/src/lang/it-it.json index eeca3601d..eeb835f99 100644 --- a/src/lang/it-it.json +++ b/src/lang/it-it.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/ja-jp.json b/src/lang/ja-jp.json index 9128e5b85..78aee4f86 100644 --- a/src/lang/ja-jp.json +++ b/src/lang/ja-jp.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/ko-kr.json b/src/lang/ko-kr.json index 374ed1960..572dced56 100644 --- a/src/lang/ko-kr.json +++ b/src/lang/ko-kr.json @@ -382,5 +382,9 @@ "most_downloaded": "대부분의 다운로드", "newly_added": "새로 추가되었습니다", "top_rated": "최고 평점", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/ml-in.json b/src/lang/ml-in.json index 6ae40fb5e..4ca44a90d 100644 --- a/src/lang/ml-in.json +++ b/src/lang/ml-in.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/mm-unicode.json b/src/lang/mm-unicode.json index f39dc5be9..1513bac28 100644 --- a/src/lang/mm-unicode.json +++ b/src/lang/mm-unicode.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/mm-zawgyi.json b/src/lang/mm-zawgyi.json index e316fc3ea..ba8f5596c 100644 --- a/src/lang/mm-zawgyi.json +++ b/src/lang/mm-zawgyi.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/pl-pl.json b/src/lang/pl-pl.json index 7d1ca398f..d8cf713cd 100644 --- a/src/lang/pl-pl.json +++ b/src/lang/pl-pl.json @@ -382,5 +382,9 @@ "most_downloaded": "Najczęściej pobierane", "newly_added": "Ostatnio dodane", "top_rated": "Najwyżej oceniane", - "rename not supported": "Zmiana nazwy katalogu w termux nie jest obsługiwana" + "rename not supported": "Zmiana nazwy katalogu w termux nie jest obsługiwana", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/pt-br.json b/src/lang/pt-br.json index ff2190b89..482013fdd 100644 --- a/src/lang/pt-br.json +++ b/src/lang/pt-br.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/pu-in.json b/src/lang/pu-in.json index a15ebb64f..d34ee89ef 100644 --- a/src/lang/pu-in.json +++ b/src/lang/pu-in.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/ru-ru.json b/src/lang/ru-ru.json index 5be55c2eb..56598c902 100644 --- a/src/lang/ru-ru.json +++ b/src/lang/ru-ru.json @@ -383,5 +383,9 @@ "most_downloaded": "Популярные", "newly_added": "Новые", "top_rated": "С высокой оценкой", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/tl-ph.json b/src/lang/tl-ph.json index 1f40f2fa7..3294a1505 100644 --- a/src/lang/tl-ph.json +++ b/src/lang/tl-ph.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/tr-tr.json b/src/lang/tr-tr.json index 84000654a..fd3f65693 100644 --- a/src/lang/tr-tr.json +++ b/src/lang/tr-tr.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/uk-ua.json b/src/lang/uk-ua.json index 47546498e..3065488e3 100644 --- a/src/lang/uk-ua.json +++ b/src/lang/uk-ua.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/uz-uz.json b/src/lang/uz-uz.json index cb3036fc0..fabc2c8c4 100644 --- a/src/lang/uz-uz.json +++ b/src/lang/uz-uz.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/vi-vn.json b/src/lang/vi-vn.json index 8348aba4d..2a133fc41 100644 --- a/src/lang/vi-vn.json +++ b/src/lang/vi-vn.json @@ -383,5 +383,9 @@ "most_downloaded": "Tải xuống nhiều nhất", "newly_added": "Mới được thêm vào", "top_rated": "Đánh giá cao nhất", - "rename not supported": "Đổi tên trên thư mục Termux không được hỗ trợ" + "rename not supported": "Đổi tên trên thư mục Termux không được hỗ trợ", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/zh-cn.json b/src/lang/zh-cn.json index 1b65a39a8..11c2caeb8 100644 --- a/src/lang/zh-cn.json +++ b/src/lang/zh-cn.json @@ -382,5 +382,9 @@ "most_downloaded": "最多下载", "newly_added": "最近上架", "top_rated": "最多好评", - "rename not supported": "不支持修改 Termux 内的文件夹名" + "rename not supported": "不支持修改 Termux 内的文件夹名", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/zh-hant.json b/src/lang/zh-hant.json index 987c38240..a9bd98c00 100644 --- a/src/lang/zh-hant.json +++ b/src/lang/zh-hant.json @@ -382,5 +382,9 @@ "most_downloaded": "最多下載", "newly_added": "最近上架", "top_rated": "最多好評", - "rename not supported": "不支持修改 Termux 內的文件夾名" + "rename not supported": "不支持修改 Termux 內的文件夾名", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/lang/zh-tw.json b/src/lang/zh-tw.json index 0d84e72ce..86ebe7b39 100644 --- a/src/lang/zh-tw.json +++ b/src/lang/zh-tw.json @@ -382,5 +382,9 @@ "most_downloaded": "Most Downloaded", "newly_added": "Newly Added", "top_rated": "Top Rated", - "rename not supported": "Rename on termux dir isn't supported" + "rename not supported": "Rename on termux dir isn't supported", + "compress": "Compress", + "copy uri": "Copy Uri", + "delete entries": "Are you sure you want to delete {count} items?", + "deleting items": "Deleting {count} items..." } diff --git a/src/pages/fileBrowser/fileBrowser.js b/src/pages/fileBrowser/fileBrowser.js index d9b98ce3a..5d5ffd312 100644 --- a/src/pages/fileBrowser/fileBrowser.js +++ b/src/pages/fileBrowser/fileBrowser.js @@ -1,5 +1,6 @@ import "./fileBrowser.scss"; +import Checkbox from "components/checkbox"; import Contextmenu from "components/contextmenu"; import Page from "components/page"; import searchBar from "components/searchbar"; @@ -10,6 +11,7 @@ import prompt from "dialogs/prompt"; import select from "dialogs/select"; import fsOperation from "fileSystem"; import externalFs from "fileSystem/externalFs"; +import JSZip from "jszip"; import actionStack from "lib/actionStack"; import checkFiles from "lib/checkFiles"; import constants from "lib/constants"; @@ -61,6 +63,9 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { const allStorages = []; let storageList = JSON.parse(localStorage.storageList || "[]"); + let isSelectionMode = false; + let selectedItems = new Set(); + if (!info) { if (mode !== "both") { info = IS_FOLDER_MODE ? strings["open folder"] : strings["open file"]; @@ -74,9 +79,22 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { const $menuToggler = ( ); + const $selectionMenuToggler = ( + + ); const $addMenuToggler = ( ); + const $selectionModeToggler = ( + + ); + const $search = ; const $lead = ; const $page = Page(strings["file browser"].capitalize(), { @@ -106,6 +124,15 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { }, ...menuOption, }); + const $selectionMenu = Contextmenu({ + innerHTML: () => { + return ` +
  • ${strings.compress.capitalize(0)}
  • +
  • ${strings.delete.capitalize(0)}
  • + `; + }, + ...((menuOption.toggler = $selectionMenuToggler) && menuOption), + }); const $addMenu = Contextmenu({ innerHTML: () => { if (currentDir.url === "/") { @@ -118,6 +145,8 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { }, ...((menuOption.toggler = $addMenuToggler) && menuOption), }); + + $selectionMenuToggler.style.display = "none"; const progress = {}; let cachedDir = {}; let currentDir = { @@ -137,7 +166,13 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { $content.addEventListener("click", handleClick); $content.addEventListener("contextmenu", handleContextMenu, true); $page.body = $content; - $page.header.append($search, $addMenuToggler, $menuToggler); + $page.header.append( + $search, + $selectionModeToggler, + $addMenuToggler, + $menuToggler, + $selectionMenuToggler, + ); if (IS_FOLDER_MODE) { $openFolder = tag("button", { @@ -172,6 +207,11 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { action: close, }); + $selectionModeToggler.onclick = function () { + isSelectionMode = !isSelectionMode; + toggleSelectionMode(isSelectionMode); + }; + $fbMenu.onclick = function (e) { $fbMenu.hide(); const action = e.target.getAttribute("action"); @@ -245,6 +285,165 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { } }; + $selectionMenu.onclick = async (e) => { + $selectionMenu.hide(); + const $target = e.target; + const action = $target.getAttribute("action"); + if (!action) return; + + switch (action) { + case "compress": + if (currentDir.url === "/") { + break; + } + + const zip = new JSZip(); + let loadingLoader = loader.create( + strings["loading"], + "Compressing files", + { + timeout: 3000, + }, + ); + try { + for (const url of selectedItems) { + const fs = fsOperation(url); + const stats = await fs.stat(); + const isDir = stats.isDirectory; + + if (isDir) { + const addDirToZip = async (dirUrl, zipFolder) => { + const entries = await fsOperation(dirUrl).lsDir(); + for (const entry of entries) { + const percent = ( + ((entries.length - entries.indexOf(entry)) / + entries.length) * + 100 + ).toFixed(0); + loadingLoader.setMessage( + `Compressing ${entry.name.length > 20 ? entry.name.substring(0, 20) + "..." : entry.name} (${percent}%)`, + ); + if (entry.isDirectory) { + const newZipFolder = zipFolder.folder(entry.name); + await addDirToZip(entry.url, newZipFolder); + } else { + const content = await fsOperation(entry.url).readFile(); + zipFolder.file(entry.name, content, { binary: true }); + } + } + }; + await addDirToZip(url, zip.folder(Url.basename(url))); + } else { + const content = await fs.readFile(); + zip.file(Url.basename(url), content); + } + } + + const zipContent = await zip.generateAsync({ + type: "arraybuffer", + }); + const zipName = "archive_" + Date.now() + ".zip"; + const zipPath = Url.join(currentDir.url, zipName); + const shortPath = + currentDir.url.length > 40 + ? currentDir.url.substring(0, 37) + "..." + : currentDir.url; + loadingLoader.setMessage(`Saving ${zipName} to ${shortPath}`); + await fsOperation(currentDir.url).createFile(zipName, zipContent); + loadingLoader.destroy(); + toast(strings.success); + isSelectionMode = !isSelectionMode; + toggleSelectionMode(isSelectionMode); + reload(); + } catch (err) { + loadingLoader.destroy(); + toast(strings.error); + console.error(err); + } + break; + + case "delete": { + if (currentDir.url === "/") { + break; + } + + // Show confirmation dialog + const confirmMessage = + selectedItems.size === 1 + ? strings["delete entry"].replace( + "{name}", + Array.from(selectedItems)[0].split("/").pop(), + ) + : strings["delete entries"].replace( + "{count}", + selectedItems.size, + ); + + const confirmation = await confirm(strings.warning, confirmMessage); + if (!confirmation) break; + + const loadingDialog = loader.create( + strings.loading, + strings["deleting items"].replace("{count}", selectedItems.size), + { timeout: 3000 }, + ); + + try { + for (const url of selectedItems) { + if ((await fsOperation(url).stat()).isDirectory) { + if (url.startsWith("content://com.termux.documents/tree/")) { + const fs = fsOperation(url); + const entries = await fs.lsDir(); + if (entries.length === 0) { + await fs.delete(); + } else { + const deleteRecursively = async (currentUrl) => { + const currentFs = fsOperation(currentUrl); + const currentEntries = await currentFs.lsDir(); + for (const entry of currentEntries) { + if (entry.isDirectory) { + await deleteRecursively(entry.url); + } else { + await fsOperation(entry.url).delete(); + } + } + await currentFs.delete(); + }; + await deleteRecursively(url); + } + } else { + await fsOperation(url).delete(); + } + helpers.updateUriOfAllActiveFiles(url); + recents.removeFolder(url); + } else { + const fs = fsOperation(url); + await fs.delete(); + const openedFile = editorManager.getFile(url, "uri"); + if (openedFile) openedFile.uri = null; + } + recents.removeFile(url); + openFolder.removeItem(url); + delete cachedDir[url]; + } + toast(strings.success); + reload(); + isSelectionMode = false; + toggleSelectionMode(false); + } catch (err) { + loadingDialog.destroy(); + helpers.error(err); + } finally { + loadingDialog.destroy(); + } + break; + } + + default: + break; + } + }; + $search.onclick = function () { const $list = $content.get("#list"); if ($list) searchBar($list, (hide) => (hideSearchBar = hide)); @@ -275,6 +474,90 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { $page.hide(); } + function updateSelectionCount($count) { + if ($count) { + $count.textContent = `${selectedItems.size} items selected`; + } + } + + function toggleSelectionMode(active) { + const $list = $content.get("#list"); + if (active) { + $list.classList.add("selection-mode"); + const $header = tag("div", { + className: "selection-header", + }); + + const selectAllCheckbox = Checkbox("", false); + const $count = tag("span", { + className: "text selection-count", + textContent: "0 items selected", + }); + + // Handle select all functionality + selectAllCheckbox.onclick = () => { + const checked = selectAllCheckbox.checked; + const items = $list.querySelectorAll(".tile:not(.selection-header)"); + items.forEach((item) => { + const checkbox = item.querySelector(".input-checkbox"); + if (checkbox) { + checkbox.checked = checked; + const url = item.querySelector("data-url").textContent; + if (checked) { + selectedItems.add(url); + } else { + selectedItems.delete(url); + } + } + }); + updateSelectionCount($count); + }; + + $header.append(selectAllCheckbox, $count); + $list.insertBefore($header, $list.firstChild); + + // Add checkboxes to list items + $list + .querySelectorAll(".tile:not(.selection-header)") + .forEach((item) => { + const checkbox = Checkbox("", false); + checkbox.onclick = () => { + const url = item.querySelector("data-url").textContent; + if (checkbox.checked) { + selectedItems.add(url); + } else { + selectedItems.delete(url); + } + updateSelectionCount($count); + }; + item.prepend(checkbox); + }); + + $addMenuToggler.style.display = "none"; + $menuToggler.style.display = "none"; + $selectionMenuToggler.style.display = ""; + + // Disable floating button in selection mode + if ($openFolder) { + $openFolder.disabled = true; + } + } else { + $list.classList.remove("selection-mode"); + $list.querySelector(".selection-header")?.remove(); + $list.querySelectorAll(".input-checkbox").forEach((cb) => cb.remove()); + selectedItems.clear(); + + $addMenuToggler.style.display = ""; + $menuToggler.style.display = ""; + $selectionMenuToggler.style.display = "none"; + + // Re-enable floating button when exiting selection mode + if ($openFolder) { + $openFolder.disabled = false; + } + } + } + /** * Called when any file folder is clicked * @param {MouseEvent} e @@ -285,6 +568,25 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { * @type {HTMLElement} */ const $el = e.target; + + if (isSelectionMode) { + const checkbox = $el.closest(".tile")?.querySelector(".input-checkbox"); + if (checkbox && !$el.closest(".selection-header")) { + checkbox.checked = !checkbox.checked; + const url = $el + .closest(".tile") + .querySelector("data-url").textContent; + if (checkbox.checked) { + selectedItems.add(url); + } else { + selectedItems.delete(url); + } + const $count = $content.querySelector(".selection-count"); + updateSelectionCount($count); + } + return; + } + let action = $el.getAttribute("action") || $el.dataset.action; if (!action) return; @@ -404,6 +706,10 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { options.push(["info", strings.info, "info"]); } + if (currentDir.url !== "/" && url) { + options.push(["copyuri", strings["copy uri"], "copy"]); + } + const option = await select(strings["select"], options); switch (option) { case "delete": { @@ -446,6 +752,11 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) { case "info": acode.exec("file-info", url); break; + + case "copyuri": + navigator.clipboard.writeText(url); + alert(strings.success, strings["copied to clipboard"]); + break; } } diff --git a/src/pages/fileBrowser/fileBrowser.scss b/src/pages/fileBrowser/fileBrowser.scss index 1ce5d975b..146902c8e 100644 --- a/src/pages/fileBrowser/fileBrowser.scss +++ b/src/pages/fileBrowser/fileBrowser.scss @@ -86,4 +86,20 @@ } } } -} \ No newline at end of file + .selection-header { + display: flex; + align-items: center; + padding: 5px; + background: var(--primary-color); + color: var(--primary-text-color); + position: sticky; + top: 0; + z-index: 1; + gap: 10px; + + .selection-count { + font-size: 0.9em; + } + + } +}