diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f341519f05..91e6f84ab8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,7 +29,7 @@ jobs: - name: Build for production run: npm run build - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: ./build/ @@ -42,4 +42,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 diff --git a/extensions/Eaielectronic/Login-page.js b/extensions/Eaielectronic/Login-page.js new file mode 100644 index 0000000000..f73505687e --- /dev/null +++ b/extensions/Eaielectronic/Login-page.js @@ -0,0 +1,278 @@ +// Name: Login +// ID: usermanagement +// Description: Allows you to create login forms. +// By: Eaielectronic +// By: SERPENT1867 +// License: MPL-2.0 +(function (Scratch) { + "use strict"; + class UserManagement { + constructor() { + this.users = []; + this.username = ""; + this.password = ""; + this.email = ""; + this.turbowarp = ""; // Variable for the form state + } + + getInfo() { + return { + id: "usermanagement", + name: "User Management", + color1: "#ff3f00", + blocks: [ + { + opcode: "openForm", + blockType: Scratch.BlockType.COMMAND, + text: "open form for [ACTION]", + arguments: { + ACTION: { + type: Scratch.ArgumentType.STRING, + menu: "actionsMenu", + defaultValue: "login", + }, + }, + }, + { + opcode: "hideForm", + blockType: Scratch.BlockType.COMMAND, + text: "hide form", + }, + { + opcode: "getUsername", + blockType: Scratch.BlockType.REPORTER, + text: "username", + }, + { + opcode: "getPassword", + blockType: Scratch.BlockType.REPORTER, + text: "password", + }, + { + opcode: "getEmail", + blockType: Scratch.BlockType.REPORTER, + text: "email", + }, + { + opcode: "getFormState", + blockType: Scratch.BlockType.REPORTER, + text: "form state", + }, + { + opcode: "clear", + blockType: Scratch.BlockType.COMMAND, + text: "clear values", + }, + ], + menus: { + actionsMenu: { + items: ["login", "create account", "recover password"], + }, + }, + }; + } + + openForm(args) { + const { ACTION } = args; + this.turbowarp = ACTION; + this.hideForm(); + + // Créer le formulaire en tant qu'élément HTML + const form = document.createElement("form"); + form.style.position = "fixed"; + form.style.top = "50%"; + form.style.left = "50%"; + form.style.transform = "translate(-50%, -50%)"; + form.style.padding = "20px"; + form.style.backgroundColor = "#f9f9f9"; + form.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.1)"; + form.style.borderRadius = "8px"; + form.style.width = "300px"; + form.style.fontFamily = "Arial, sans-serif"; + form.style.zIndex = "1000"; // S'assure que le formulaire reste au-dessus + + const closeButton = document.createElement("span"); + closeButton.textContent = "✖"; + closeButton.style.position = "absolute"; + closeButton.style.top = "10px"; + closeButton.style.right = "10px"; + closeButton.style.cursor = "pointer"; + closeButton.style.color = "#000"; + closeButton.addEventListener("click", () => { + this.hideForm(); + }); + form.appendChild(closeButton); + + const title = document.createElement("h2"); + title.textContent = ACTION.charAt(0).toUpperCase() + ACTION.slice(1); + title.style.textAlign = "center"; + title.style.color = "#333"; + form.appendChild(title); + + if (ACTION === "login" || ACTION === "create account") { + const usernameInput = document.createElement("input"); + usernameInput.type = "text"; + usernameInput.name = "username"; + usernameInput.placeholder = "Username"; + usernameInput.style.width = "90%"; + usernameInput.style.padding = "10px"; + usernameInput.style.margin = "10px 0"; + usernameInput.style.border = "1px solid #ccc"; + usernameInput.style.borderRadius = "4px"; + form.appendChild(usernameInput); + + const passwordInput = document.createElement("input"); + passwordInput.type = "password"; + passwordInput.name = "password"; + passwordInput.placeholder = "Password"; + passwordInput.style.width = "90%"; + passwordInput.style.padding = "10px"; + passwordInput.style.margin = "10px 0"; + passwordInput.style.border = "1px solid #ccc"; + passwordInput.style.borderRadius = "4px"; + form.appendChild(passwordInput); + } + + if (ACTION === "create account" || ACTION === "recover password") { + const emailInput = document.createElement("input"); + emailInput.type = "email"; + emailInput.name = "email"; + emailInput.placeholder = "Email"; + emailInput.style.width = "95%"; + emailInput.style.padding = "10px"; + emailInput.style.margin = "10px 0"; + emailInput.style.border = "1px solid #ccc"; + emailInput.style.borderRadius = "4px"; + form.appendChild(emailInput); + } + + const submitButton = document.createElement("button"); + submitButton.type = "submit"; + submitButton.textContent = "Send"; + submitButton.style.width = "100%"; + submitButton.style.padding = "10px"; + submitButton.style.margin = "10px 0"; + submitButton.style.backgroundColor = "#4CAF50"; + submitButton.style.color = "#fff"; + submitButton.style.border = "none"; + submitButton.style.borderRadius = "4px"; + submitButton.style.cursor = "pointer"; + form.appendChild(submitButton); + + form.addEventListener("submit", (event) => { + event.preventDefault(); + const formData = new FormData(form); + this.username = formData.get("username"); + this.password = formData.get("password"); + this.email = formData.get("email"); + + if (ACTION === "login") { + this.login({ USERNAME: this.username, PASSWORD: this.password }); + } else if (ACTION === "create account") { + this.createAccount({ + USERNAME: this.username, + PASSWORD: this.password, + EMAIL: this.email, + }); + } else if (ACTION === "recover password") { + this.recoverPassword({ EMAIL: this.email }); + } + + this.hideForm(); + }); + + // Ajouter les liens pour naviguer entre les formulaires + if (ACTION === "login" || ACTION === "create account") { + const resetPasswordLink = document.createElement("a"); + resetPasswordLink.textContent = "Reset password"; + resetPasswordLink.style.display = "block"; + resetPasswordLink.style.textAlign = "center"; + resetPasswordLink.style.marginTop = "10px"; + resetPasswordLink.style.color = "#007BFF"; + resetPasswordLink.style.cursor = "pointer"; + resetPasswordLink.addEventListener("click", () => { + this.hideForm(false); + this.openForm({ ACTION: "recover password" }); + }); + form.appendChild(resetPasswordLink); + } + + if (ACTION === "login" || ACTION === "recover password") { + const createAccountLink = document.createElement("a"); + createAccountLink.textContent = "Create account"; + createAccountLink.style.display = "block"; + createAccountLink.style.textAlign = "center"; + createAccountLink.style.marginTop = "10px"; + createAccountLink.style.color = "#007BFF"; + createAccountLink.style.cursor = "pointer"; + createAccountLink.addEventListener("click", () => { + this.hideForm(false); + this.openForm({ ACTION: "create account" }); + }); + form.appendChild(createAccountLink); + } + + document.body.appendChild(form); + } + + hideForm() { + const form = document.querySelector("form"); + if (form) { + document.body.removeChild(form); + this.turbowarp = ""; // Reset the form state + } + } + + createAccount(args) { + const { USERNAME, PASSWORD, EMAIL } = args; + const user = { username: USERNAME, password: PASSWORD, email: EMAIL }; + this.users.push(user); + console.log(`Account created for ${USERNAME}`); + } + + login(args) { + const { USERNAME, PASSWORD } = args; + const user = this.users.find( + (u) => u.username === USERNAME && u.password === PASSWORD + ); + console.log( + user + ? `Login successful for ${USERNAME}` + : "Incorrect username or password." + ); + } + + recoverPassword(args) { + const { EMAIL } = args; + const user = this.users.find((u) => u.email === EMAIL); + console.log( + user ? `Your password is: ${user.password}` : "Email not found." + ); + } + + getUsername() { + return this.username; + } + + getPassword() { + return this.password; + } + + getEmail() { + return this.email; + } + + getFormState() { + return this.turbowarp; + } + + clear() { + this.username = ""; + this.password = ""; + this.email = ""; + console.log("Values have been cleared."); + } + } + + Scratch.extensions.register(new UserManagement()); +})(Scratch); diff --git a/extensions/Lily/Assets.js b/extensions/Lily/Assets.js index ef96fc1563..282a910fc8 100644 --- a/extensions/Lily/Assets.js +++ b/extensions/Lily/Assets.js @@ -362,8 +362,14 @@ const assetType = this._typeIsBitmap(blob.type) ? runtime.storage.AssetType.ImageBitmap : runtime.storage.AssetType.ImageVector; + + // Bitmap data format is not actually enforced, but setting it to something that isn't in scratch-parser's + // known format list will throw an error when someone tries to load the project. + // (https://github.com/scratchfoundation/scratch-parser/blob/665f05d739a202d565a4af70a201909393d456b2/lib/sb3_definitions.json#L51) const dataType = - blob.type === "image/svg+xml" ? "svg" : blob.type.split("/")[1]; + blob.type === "image/svg+xml" + ? runtime.storage.DataFormat.SVG + : runtime.storage.DataFormat.PNG; const arrayBuffer = await new Promise((resolve, reject) => { const fr = new FileReader(); diff --git a/images/Eaieletronic/Login-page.svg b/images/Eaieletronic/Login-page.svg new file mode 100644 index 0000000000..1200b23acb --- /dev/null +++ b/images/Eaieletronic/Login-page.svg @@ -0,0 +1 @@ + \ No newline at end of file