From f27dd4cc2cb5ca391ee1b9d9315b9b71fa155782 Mon Sep 17 00:00:00 2001 From: lamia Date: Thu, 19 Dec 2024 22:15:12 +0100 Subject: [PATCH] feat: Add Ability to Cancel Ongoing Deployments in Dev Center (#879) --- src/dev-center/js/dev-center.js | 252 +++++--------------------------- 1 file changed, 40 insertions(+), 212 deletions(-) diff --git a/src/dev-center/js/dev-center.js b/src/dev-center/js/dev-center.js index c6eab46c79..d9957a7e19 100644 --- a/src/dev-center/js/dev-center.js +++ b/src/dev-center/js/dev-center.js @@ -288,6 +288,8 @@ async function create_app(title, source_path = null, items = null) { }) .then(async (app) => { + $('.new-app-modal').get(0).close(); + window.location.reload(); let app_dir; // ---------------------------------------------------- // Create app directory in AppData @@ -313,6 +315,8 @@ async function create_app(title, source_path = null, items = null) { maximizeOnStart: false, background: false, }).then(async (app) => { + $('.new-app-modal').get(0).close(); + window.location.reload(); // refresh app list puter.apps.list().then(async (resp) => { apps = resp; @@ -555,8 +559,8 @@ function generate_edit_app_section(app) { -

A list of file type specifiers. For example if you include .txt your apps could be opened when a user clicks on a TXT file.

- +

A comma-separated list of file type specifiers. For example if you include .txt, your apps could be opened when a user clicks on a TXT file.

+

Window

@@ -762,63 +766,6 @@ async function edit_app_section(cur_app_name) { toggleResetButton(); // Ensure Reset button is initially disabled $('#edit-app').show(); - const filetype_association_input = document.querySelector('textarea[id=edit-app-filetype-associations]'); - let tagify = new Tagify(filetype_association_input, { - pattern: /\.(?:[a-z0-9]+)|(?:[a-z]+\/(?:[a-z0-9.-]+|\*))/, - delimiters: ", ", - enforceWhitelist: false, - dropdown : { - // show the dropdown immediately on focus (0 character typed) - enabled: 0, - }, - whitelist: [ - // MIME type patterns - "text/*", "image/*", "audio/*", "video/*", "application/*", - - // Documents - ".doc", ".docx", ".pdf", ".txt", ".odt", ".rtf", ".tex", ".md", ".pages", ".epub", ".mobi", ".azw", ".azw3", ".djvu", ".xps", ".oxps", ".fb2", ".textile", ".markdown", ".asciidoc", ".rst", ".wpd", ".wps", ".abw", ".zabw", - - // Spreadsheets - ".xls", ".xlsx", ".csv", ".ods", ".numbers", ".tsv", ".gnumeric", ".xlt", ".xltx", ".xlsm", ".xltm", ".xlam", ".xlsb", - - // Presentations - ".ppt", ".pptx", ".key", ".odp", ".pps", ".ppsx", ".pptm", ".potx", ".potm", ".ppam", - - // Images - ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".svg", ".webp", ".ico", ".psd", ".ai", ".eps", ".raw", ".cr2", ".nef", ".orf", ".sr2", ".heic", ".heif", ".avif", ".jxr", ".hdp", ".wdp", ".jng", ".xcf", ".pgm", ".pbm", ".ppm", ".pnm", - - // Video - ".mp4", ".avi", ".mov", ".wmv", ".mkv", ".flv", ".webm", ".m4v", ".mpeg", ".mpg", ".3gp", ".3g2", ".ogv", ".vob", ".drc", ".gifv", ".mng", ".qt", ".yuv", ".rm", ".rmvb", ".asf", ".amv", ".m2v", ".svi", - - // Audio - ".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a", ".wma", ".aiff", ".alac", ".ape", ".au", ".mid", ".midi", ".mka", ".pcm", ".ra", ".ram", ".snd", ".wv", ".opus", - - // Code/Development - ".js", ".ts", ".html", ".css", ".json", ".xml", ".php", ".py", ".java", ".cpp", ".c", ".cs", ".h", ".hpp", ".hxx", ".rs", ".go", ".rb", ".pl", ".swift", ".kt", ".kts", ".scala", ".coffee", ".sass", ".scss", ".less", ".jsx", ".tsx", ".vue", ".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat", ".cmd", ".sql", ".r", ".dart", ".f", ".f90", ".for", ".lua", ".m", ".mm", ".clj", ".erl", ".ex", ".exs", ".elm", ".hs", ".lhs", ".lisp", ".ml", ".mli", ".nim", ".pl", ".rkt", ".v", ".vhd", - - // Archives - ".zip", ".rar", ".7z", ".tar", ".gz", ".bz2", ".xz", ".z", ".lz", ".lzma", ".tlz", ".txz", ".tgz", ".tbz2", ".bz", ".br", ".lzo", ".ar", ".cpio", ".shar", ".lrz", ".lz4", ".lz2", ".rz", ".sfark", ".sz", ".zoo", - - // Database - ".db", ".sql", ".sqlite", ".sqlite3", ".dbf", ".mdb", ".accdb", ".db3", ".s3db", ".dbx", - - // Fonts - ".ttf", ".otf", ".woff", ".woff2", ".eot", ".pfa", ".pfb", ".sfd", - - // CAD and 3D - ".dwg", ".dxf", ".stl", ".obj", ".fbx", ".dae", ".3ds", ".blend", ".max", ".ma", ".mb", ".c4d", ".skp", ".usd", ".usda", ".usdc", ".abc", - - // Scientific/Technical - ".mat", ".fig", ".nb", ".cdf", ".fits", ".fts", ".fit", ".gmsh", ".msh", ".fem", ".neu", ".hdf", ".h5", ".nx", ".unv", - - // System - ".exe", ".dll", ".so", ".dylib", ".app", ".dmg", ".iso", ".img", ".bin", ".msi", ".apk", ".ipa", ".deb", ".rpm", - - // Directory - ".directory" - ], - }) - // -------------------------------------------------------- // Dragster // -------------------------------------------------------- @@ -859,7 +806,7 @@ async function edit_app_section(cur_app_name) { dropped_items = items[0].path; $('.drop-area').removeClass('drop-area-hover'); $('.drop-area').addClass('drop-area-ready-to-deploy'); - drop_area_content = `

index.html

Ready to deploy 🚀

Cancel

`; + drop_area_content = `

index.html

Ready to deploy 🚀

`; $('.drop-area').html(drop_area_content); // enable deploy button @@ -893,7 +840,7 @@ async function edit_app_section(cur_app_name) { dropped_items = items; $('.drop-area').removeClass('drop-area-hover'); $('.drop-area').addClass('drop-area-ready-to-deploy'); - drop_area_content = `

${items.length} items

Ready to deploy 🚀

Cancel

`; + drop_area_content = `

${items.length} items

Ready to deploy 🚀

`; $('.drop-area').html(drop_area_content); // enable deploy button @@ -934,7 +881,7 @@ async function edit_app_section(cur_app_name) { $('.drop-area').removeClass('drop-area-hover'); $('.drop-area').addClass('drop-area-ready-to-deploy'); - drop_area_content = `

${rootItems}

Ready to deploy 🚀

Cancel

`; + drop_area_content = `

${rootItems}

Ready to deploy 🚀

`; $('.drop-area').html(drop_area_content); // enable deploy button @@ -1008,7 +955,7 @@ async function edit_app_section(cur_app_name) { rootItems = html_encode(rootItems); $('.drop-area').removeClass('drop-area-hover'); $('.drop-area').addClass('drop-area-ready-to-deploy'); - drop_area_content = `

${rootItems}

Ready to deploy 🚀

Cancel

`; + drop_area_content = `

${rootItems}

Ready to deploy 🚀

`; $('.drop-area').html(drop_area_content); // enable deploy button @@ -1159,32 +1106,6 @@ $(document).on('click', '.edit-app-save-btn', async function (e) { } } - // parse filetype_associations - - filetype_associations = JSON.parse(filetype_associations); - filetype_associations = filetype_associations.map((type) => { - const fileType = type.value; - - - if ( - !fileType || - fileType === "." || - fileType === "/" - ) { - error = `File Association Type must be valid.`; - return null; // Return null for invalid cases - } - const lower = fileType.toLocaleLowerCase(); - - if (fileType.includes("/")) { - return lower; - } else if (fileType.includes(".")) { - return "." + lower.split(".")[1]; - } else { - return "." + lower; - } - }).filter(Boolean); - // error? if (error) { $('#edit-app-error').show(); @@ -1196,8 +1117,8 @@ $(document).on('click', '.edit-app-save-btn', async function (e) { // show working spinner puter.ui.showSpinner(); - - + // parse filetype_associations + filetype_associations = filetype_associations.split(',').map(element => element.trim()); // disable submit button $('.edit-app-save-btn').prop('disabled', true); @@ -1721,130 +1642,14 @@ function sort_apps() { } } -/** - * Checks if the items being deployed contain a .git directory - * @param {Array|string} items - Items to check (can be path string or array of items) - * @returns {Promise} - True if .git directory is found - */ -async function hasGitDirectory(items) { - // Case 1: Single Puter path - if (typeof items === 'string' && (items.startsWith('/') || items.startsWith('~'))) { - const stat = await puter.fs.stat(items); - if (stat.is_dir) { - const files = await puter.fs.readdir(items); - return files.some(file => file.name === '.git' && file.is_dir); - } - return false; - } - - // Case 2: Array of Puter items - if (Array.isArray(items) && items[0]?.uid) { - return items.some(item => item.name === '.git' && item.is_dir); - } - - // Case 3: Local items (DataTransferItems) - if (Array.isArray(items)) { - for (let item of items) { - if (item.fullPath?.includes('/.git/') || - item.path?.includes('/.git/') || - item.filepath?.includes('/.git/')) { - return true; - } - } - } - - return false; -} - -/** - * Shows a warning dialog about .git directory deployment - * @returns {Promise} - True if the user wants to proceed with deployment - */ -async function showGitWarningDialog() { - try { - // Check if the user has chosen to skip the warning - const skipWarning = await puter.kv.get('skip-git-warning'); - - // Log retrieved value for debugging - console.log('Retrieved skip-git-warning:', skipWarning); - - // If the user opted to skip the warning, proceed without showing it - if (skipWarning === true) { - return true; - } - } catch (error) { - console.error('Error accessing KV store:', error); - // If KV store access fails, fall back to showing the dialog - } - - // Create the modal dialog - const modal = document.createElement('div'); - modal.innerHTML = ` -
-

Warning: Git Repository Detected

-

A .git directory was found in your deployment files. Deploying .git directories may:

-
    -
  • Expose sensitive information like commit history and configuration
  • -
  • Significantly increase deployment size
  • -
-
- - -
-
- - -
-
-
- `; - document.body.appendChild(modal); - - return new Promise((resolve) => { - // Handle "Continue Deployment" - document.getElementById('continue-deployment').addEventListener('click', async () => { - try { - const skipChecked = document.getElementById('skip-git-warning')?.checked; - if (skipChecked) { - console.log("Saving 'skip-git-warning' preference as true"); - await puter.kv.set('skip-git-warning', true); - } - } catch (error) { - console.error('Error saving user preference to KV store:', error); - } finally { - document.body.removeChild(modal); - resolve(true); // Continue deployment - } - }); - - // Handle "Cancel Deployment" - document.getElementById('cancel-deployment').addEventListener('click', () => { - document.body.removeChild(modal); - resolve(false); // Cancel deployment - }); - }); -} - window.deploy = async function (app, items) { - // Check for .git directory before proceeding - try { - if (await hasGitDirectory(items)) { - const shouldProceed = await showGitWarningDialog(); - if (!shouldProceed) { - reset_drop_area(); - return; - } - } - } catch (err) { - console.error('Error checking for .git directory:', err); - } let appdata_dir, current_app_dir; // disable deploy button $('.deploy-btn').addClass('disabled'); // change drop area text - $('.drop-area').html(deploying_spinner + '
Deploying (0%)
'); + $('.drop-area').html(deploying_spinner + '
Deploying (0%)

Cancel

'); if (typeof items === 'string' && (items.startsWith('/') || items.startsWith('~'))) { $('.drop-area').removeClass('drop-area-hover'); @@ -2215,7 +2020,7 @@ $(document).on('click', '.insta-deploy-existing-app-deploy-btn', function (e) { $('.drop-area').removeClass('drop-area-hover'); $('.drop-area').addClass('drop-area-ready-to-deploy'); - let drop_area_content = `

Ready to deploy 🚀

Cancel

`; + let drop_area_content = `

Ready to deploy 🚀

Cancel

`; $('.drop-area').html(drop_area_content); // deploy @@ -2657,9 +2462,32 @@ function enable_window_settings(){ $('#edit-app-hide-titlebar').prop('disabled', false); } -$(document).on('click', '.reset-deploy', function (e) { - reset_drop_area(); -}) +$(document).on('click', '.reset-deploy', async function (e) { + // Display a confirmation dialog to ask the user + const alert_resp = await puter.ui.alert( + 'Are you sure you want to cancel the deployment?', + [ + { + label: 'Yes, cancel deployment', + value: 'cancel', + type: 'danger', // This can style the button as red/danger + }, + { + label: 'No, keep it', + value: 'keep' + } + ] + ); + + if (alert_resp === 'cancel') { + // If the user clicks "Yes, cancel deployment", reset the drop area + reset_drop_area(); + } else { + // If the user clicks "No, keep it", do nothing or log it + console.log('Deployment is not canceled.'); + } +}); + $(document).on('click', '.sidebar-toggle', function (e) { $('.sidebar').toggleClass('open');