diff --git a/biome.json b/biome.json index 76fc8f5..c2b9dc7 100644 --- a/biome.json +++ b/biome.json @@ -44,12 +44,6 @@ } }, "files": { - "ignore": [ - "node_modules/**", - "build/**", - "out/**", - ".next/**", - "coverage/**" - ] + "ignore": ["node_modules/**", "build/**", "out/**", ".next/**", "coverage/**"] } -} \ No newline at end of file +} diff --git a/package.json b/package.json index b8a7c36..46dd7a3 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "tailwind-scrollbar": "^3.0.4", "tailwindcss-animate": "^1.0.6", "tauri-controls": "^0.1.0", + "yup": "^1.4.0", "zod": "^3.21.4", "zustand": "^4.5.2" }, @@ -80,7 +81,7 @@ "@biomejs/biome": "^1.6.2", "@ianvs/prettier-plugin-sort-imports": "^4.1.0", "@tauri-apps/cli": "2.0.0-beta.11", - "@types/node": "^20.4.6", + "@types/node": "20.4.6", "@types/react": "^18.2.18", "@types/react-dom": "^18.2.7", "autoprefixer": "^10.4.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70e972e..5c13443 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -182,6 +182,9 @@ dependencies: tauri-controls: specifier: ^0.1.0 version: 0.1.0(@tauri-apps/plugin-os@2.0.0-alpha.0)(@tauri-apps/plugin-window@2.0.0-alpha.0)(clsx@2.0.0)(react-dom@18.2.0)(react@18.2.0)(tailwind-merge@1.14.0) + yup: + specifier: ^1.4.0 + version: 1.4.0 zod: specifier: ^3.21.4 version: 3.21.4 @@ -200,7 +203,7 @@ devDependencies: specifier: 2.0.0-beta.11 version: 2.0.0-beta.11 '@types/node': - specifier: ^20.4.6 + specifier: 20.4.6 version: 20.4.6 '@types/react': specifier: ^18.2.18 @@ -4180,6 +4183,10 @@ packages: react-is: 16.13.1 dev: false + /property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + dev: false + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -4789,6 +4796,10 @@ packages: dependencies: any-promise: 1.3.0 + /tiny-case@1.0.3: + resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + dev: false + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -4799,6 +4810,10 @@ packages: dependencies: is-number: 7.0.0 + /toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + dev: false + /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -4816,6 +4831,11 @@ packages: - supports-color dev: true + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: false + /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} @@ -5032,6 +5052,15 @@ packages: yargs-parser: 21.1.1 dev: true + /yup@1.4.0: + resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + dev: false + /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} dev: false diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 57fc38b..7439091 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -10,10 +10,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "2.0.0-beta.10", features = [] } +tauri-build = { version = "2.0.0-beta", features = [] } [dependencies] -tauri = { version = "2.0.0-beta.13", features = [] } +tauri = { version = "2.0.0-beta", features = [] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tauri-plugin-app = "2.0.0-alpha.2" diff --git a/src-tauri/capabilities/migrated.json b/src-tauri/capabilities/migrated.json new file mode 100644 index 0000000..e2006e7 --- /dev/null +++ b/src-tauri/capabilities/migrated.json @@ -0,0 +1,15 @@ +{ + "identifier": "migrated", + "description": "permissions that were migrated from v1", + "local": true, + "windows": ["main"], + "permissions": [ + "path:default", + "event:default", + "window:default", + "app:default", + "resources:default", + "menu:default", + "tray:default" + ] +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d713d8e..567dcb2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -2,68 +2,53 @@ "build": { "beforeDevCommand": "pnpm dev", "beforeBuildCommand": "pnpm build", - "devPath": "http://localhost:1420", - "distDir": "../dist", - "withGlobalTauri": false + "frontendDist": "../dist", + "devUrl": "http://localhost:1420" }, - "package": { - "productName": "botillery" - }, - "tauri": { - "bundle": { - "active": true, - "category": "DeveloperTool", - "copyright": "", + "productName": "botillery", + "identifier": "cl.botilatia.app", + "bundle": { + "active": true, + "category": "DeveloperTool", + "copyright": "", + "targets": "all", + "externalBin": [], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "", + "nsis": { + "installerIcon": "./icons/icon.ico" + } + }, + "longDescription": "", + "macOS": { + "entitlements": null, + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, + "resources": [], + "shortDescription": "", + "linux": { "deb": { "depends": [] - }, - "externalBin": [], - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "identifier": "cl.botilatia.app", - "longDescription": "", - "macOS": { - "entitlements": null, - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null - }, - "resources": [], - "shortDescription": "", - "targets": "all", - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "", - "nsis": { - "installerIcon": "./icons/icon.ico" - } } - }, + } + }, + "plugins": {}, + "app": { + "withGlobalTauri": false, "security": { - "csp": null, - "dangerousRemoteDomainIpcAccess": [ - { - "scheme": "https", - "domain": "tauri.localhost", - "windows": [ - "main" - ], - "plugins": [ - "app", - "shell", - "os", - "event", - "window" - ] - } - ] + "csp": null }, "windows": [ { diff --git a/src/components/greeting.tsx b/src/components/greeting.tsx index 85af6f9..756797d 100644 --- a/src/components/greeting.tsx +++ b/src/components/greeting.tsx @@ -1,6 +1,6 @@ "use client"; -import { invoke } from "@tauri-apps/api/tauri"; +import { invoke } from "@tauri-apps/api/core"; import { useEffect, useState } from "react"; export function Greeting() { diff --git a/src/lib/Objects/Project.ts b/src/lib/Objects/Project.ts index ad0d108..ce12d15 100644 --- a/src/lib/Objects/Project.ts +++ b/src/lib/Objects/Project.ts @@ -28,7 +28,7 @@ export interface Art { export enum CreationType { NEW = "new", IMPORT = "import", - MIGRATION = "migration", + MIGRATION = "migration" } export type IconArtSize = "64x64" | "128x128" | "256x256" | "512x512"; @@ -57,7 +57,7 @@ export enum PackType { WORLD_SKIN = "world_skin", TEXTURE_SKIN = "texture_skin", MASHUP = "mashup", - NONE = "none", + NONE = "none" } export type SkinResolution = "64x64" | "128x128" | "256x256" | "512x512" | "1024x1024" | null; diff --git a/src/lib/Objects/Session.ts b/src/lib/Objects/Session.ts index 6e93658..5340e30 100644 --- a/src/lib/Objects/Session.ts +++ b/src/lib/Objects/Session.ts @@ -27,21 +27,21 @@ export const DEFAULT_SKIN_PACK: SkinPack = { header: { name: "pack.name", uuid: "", - version: [1, 0, 0], + version: [1, 0, 0] }, modules: [ { type: "skin_pack", uuid: "", - version: [1, 0, 0], - }, - ], + version: [1, 0, 0] + } + ] }, skinList: [], skinsJSON: { skins: [], serialize_name: "", - localization_name: "", + localization_name: "" }, skinResolution: null, texts: [ @@ -49,9 +49,9 @@ export const DEFAULT_SKIN_PACK: SkinPack = { packTextLanguage: "en_US.lang", packName: "", packDescription: "", - translations: [], - }, - ], + translations: [] + } + ] }; export const DEFAULT_WORLD_PACK: WorldPack = { @@ -63,8 +63,8 @@ export const DEFAULT_WORLD_PACK: WorldPack = { packTextLanguage: "en_US.lang", packName: "", packDescription: "", - translations: [], - }, + translations: [] + } ], manifest: { format_version: 2, @@ -74,20 +74,20 @@ export const DEFAULT_WORLD_PACK: WorldPack = { version: [1, 0, 0], uuid: "", lock_template_options: true, - base_game_version: [], + base_game_version: [] }, modules: [ { type: "world_template", uuid: "", - version: [1, 0, 0], - }, + version: [1, 0, 0] + } ], metadata: { - authors: ["Packager by Waypoint"], - }, - }, - }, + authors: ["Packager by Waypoint"] + } + } + } }; export const DEFAULT_TEXTURE_PACK: PackSpecificProps = { @@ -98,8 +98,8 @@ export const DEFAULT_TEXTURE_PACK: PackSpecificProps = { packTextLanguage: "en_US.lang", packName: "", packDescription: "", - translations: [], - }, + translations: [] + } ], manifest: { format_version: 2, @@ -108,17 +108,17 @@ export const DEFAULT_TEXTURE_PACK: PackSpecificProps = { name: "pack.name", uuid: "", version: [1, 0, 0], - min_engine_version: [], + min_engine_version: [] }, modules: [ { type: "resources", uuid: "", - version: [1, 0, 0], - }, + version: [1, 0, 0] + } ], - dependencies: [], - }, + dependencies: [] + } }; Object.freeze(DEFAULT_SKIN_PACK); diff --git a/src/lib/Objects/Skin.ts b/src/lib/Objects/Skin.ts index 97dceb4..787e20f 100644 --- a/src/lib/Objects/Skin.ts +++ b/src/lib/Objects/Skin.ts @@ -76,8 +76,8 @@ export function cleanUnusedPixels(image: HTMLImageElement, isSlim: boolean, is64 ? ALEX_SKIN_MASK_X64 : ALEX_SKIN_MASK_X128 : is64 - ? STEVE_SKIN_MASK_X64 - : STEVE_SKIN_MASK_X128; + ? STEVE_SKIN_MASK_X64 + : STEVE_SKIN_MASK_X128; const context = canvas.getContext("2d"); if (!context) throw new Error("Unable to get canvas context"); @@ -117,6 +117,6 @@ export const STEVE_SKIN: Skin = { file_path: SteveSkin.src, url_blob_path: SteveSkin.src, price: "paid", - model: "default", + model: "default" }; Object.freeze(STEVE_SKIN); diff --git a/src/schemas/authSchema.ts b/src/schemas/authSchema.ts new file mode 100644 index 0000000..56e1c29 --- /dev/null +++ b/src/schemas/authSchema.ts @@ -0,0 +1,89 @@ +import { type InferType, object, string } from "yup"; + +export const SignInFormSchema = object({ + email: string().required("auth.errors.email.required").email("auth.errors.email.invalid").trim(), + password: string() + .required("auth.errors.password.required") + .min(6, "auth.errors.password.minLength") + .max(50, "auth.errors.password.maxLength") + .trim(), +}); + +export type SignInFromType = InferType; + +export const SignUpFormSchema = object({ + email: string().required("auth.errors.email.required").email("auth.errors.email.invalid").trim(), + password: string() + .required("auth.errors.password.required") + .min(6, "auth.errors.password.minLength") + .max(50, "auth.errors.password.maxLength") + .trim(), + confirmPassword: string() + .required("auth.errors.password.required") + .min(6, "auth.errors.password.minLength") + .max(50, "auth.errors.password.maxLength") + .trim(), + firstName: string() + .required("auth.errors.firstName.required") + .min(2, "auth.errors.firstName.minLength") + .max(50, "auth.errors.firstName.maxLength") + .trim(), + lastName: string() + .optional() + .min(2, "auth.errors.lastName.minLength") + .max(50, "auth.errors.lastName.maxLength") + .trim(), + company: string() + .optional() + .min(2, "auth.errors.company.minLength") + .max(50, "auth.errors.company.maxLength") + .trim(), + lang: string().required("auth.errors.lang.required").oneOf(["en", "es"], "auth.errors.lang.invalid"), +}); + +export type SignUpFromType = InferType; + +export const UpdateBasicUserInfoFormSchema = object({ + firstName: string() + .required("auth.errors.firstName.required") + .min(2, "auth.errors.firstName.minLength") + .max(50, "auth.errors.firstName.maxLength") + .trim(), + lastName: string() + .optional() + .min(2, "auth.errors.lastName.minLength") + .max(50, "auth.errors.lastName.maxLength") + .trim(), + lang: string().required("auth.errors.lang.required").oneOf(["en", "es"], "auth.errors.lang.invalid"), +}); + +export type UpdateBasicUserInfoFormType = InferType; + +export const UpdateCriticalUserInfoFormSchema = object({ + company: string() + .optional() + .min(2, "auth.errors.company.minLength") + .max(50, "auth.errors.company.maxLength") + .trim(), + password: string() + .required("auth.errors.password.required") + .min(6, "auth.errors.password.minLength") + .max(50, "auth.errors.password.maxLength") + .trim(), + confirmPassword: string() + .required("auth.errors.password.required") + .min(6, "auth.errors.password.minLength") + .max(50, "auth.errors.password.maxLength") + .trim(), +}); + +export type UpdateCriticalUserInfoFormType = InferType; + +export const VerifyUserEmailSchema = object({ + token: string() + .min(1, "auth.errors.email.verification.minCharacters") + .max(6, "auth.errors.email.verification.maxCharacters") + .trim(), +}); + +export type VerifyUserEmailType = InferType; diff --git a/src/store/api/ApiSlice.ts b/src/store/api/ApiSlice.ts index ba67456..a6c7e72 100644 --- a/src/store/api/ApiSlice.ts +++ b/src/store/api/ApiSlice.ts @@ -13,7 +13,7 @@ const API_URL = process.env.NEXT_PUBLIC_API_URL; export const api = axios.create({ baseURL: API_URL, - timeout: 8000, + timeout: 8000 }); api.interceptors.response.use( @@ -25,11 +25,11 @@ api.interceptors.response.use( method: error.config.method, url: error.config.url, params: error.config.params, - data: error.config.data, + data: error.config.data }); } return Promise.reject(error.message); - }, + } ); export interface ApiSlice { @@ -60,5 +60,5 @@ export const createApiSlice: StateCreator< return response.data; } throw new Error("Error with request Status:", response.status); - }, + } }); diff --git a/src/store/api/AuthApiSlice.ts b/src/store/api/AuthApiSlice.ts index 8c2b122..8c3cf1e 100644 --- a/src/store/api/AuthApiSlice.ts +++ b/src/store/api/AuthApiSlice.ts @@ -49,8 +49,8 @@ export const createAuthApiSlice: StateCreator< return await api .post("/auth/signOut", null, { headers: { - Authorization: `Bearer ${backendTokens.accessToken}`, - }, + Authorization: `Bearer ${backendTokens.accessToken}` + } }) .then(handleAxiosResponse); }, @@ -62,8 +62,8 @@ export const createAuthApiSlice: StateCreator< return await api .post("/auth/refresh-token", null, { headers: { - Authorization: `Bearer ${backendTokens.refreshToken}`, - }, + Authorization: `Bearer ${backendTokens.refreshToken}` + } }) .then(response => { if (response?.status === 201) { @@ -81,8 +81,8 @@ export const createAuthApiSlice: StateCreator< return await api .post("/auth/verify-email", { token, - email, + email }) .then(handleAxiosResponse); - }, + } }); diff --git a/src/store/api/UserApiSlice.ts b/src/store/api/UserApiSlice.ts index 4a0670f..46cc812 100644 --- a/src/store/api/UserApiSlice.ts +++ b/src/store/api/UserApiSlice.ts @@ -12,7 +12,7 @@ import type { AuthApiSlice } from "./AuthApiSlice"; export interface UserApiSlice { checkUsername: (username: string) => Promise; updateCriticalUserInfoToApi: ( - user: UpdateCriticalUserInfoFormType, + user: UpdateCriticalUserInfoFormType ) => Promise; updateBasicUserInfoToApi: (user: UpdateBasicUserInfoFormType) => Promise; } @@ -37,35 +37,35 @@ export const createUserApiSlice: StateCreator< return api .get(`/user/check-username/${username}`, { headers: { - Authorization: `Bearer ${backendTokens.accessToken}`, - }, + Authorization: `Bearer ${backendTokens.accessToken}` + } }) .then(handleAxiosResponse); }, updateCriticalUserInfoToApi: async ( - user: UpdateCriticalUserInfoFormType, + user: UpdateCriticalUserInfoFormType ): Promise => { const { api, handleAxiosResponse, backendTokens } = get(); if (!backendTokens || !user) return undefined; return api .post("/user/update", user, { headers: { - Authorization: `Bearer ${backendTokens.accessToken}`, - }, + Authorization: `Bearer ${backendTokens.accessToken}` + } }) .then(handleAxiosResponse); }, updateBasicUserInfoToApi: async ( - user: UpdateBasicUserInfoFormType, + user: UpdateBasicUserInfoFormType ): Promise => { const { api, handleAxiosResponse, backendTokens } = get(); if (!backendTokens || !user) return undefined; return api .post("/user/update", user, { headers: { - Authorization: `Bearer ${backendTokens.accessToken}`, - }, + Authorization: `Bearer ${backendTokens.accessToken}` + } }) .then(handleAxiosResponse); - }, + } }); diff --git a/src/store/app/SessionSlice.ts b/src/store/app/SessionSlice.ts index e8cbe63..5998158 100644 --- a/src/store/app/SessionSlice.ts +++ b/src/store/app/SessionSlice.ts @@ -7,10 +7,7 @@ import { STEVE_SKIN } from "../../lib/Objects/Skin"; import type { ApiSlice } from "../api/ApiSlice"; import type { AuthApiSlice } from "../api/AuthApiSlice"; import type { UserApiSlice } from "../api/UserApiSlice"; -import type { ProjectArtSlice } from "./ProjectArtSlice"; -import type { ProjectSlice } from "./ProjectSlice"; import type { SettingsSlice } from "./SettingsSlice"; -import type { SkinSlice } from "./SkinSlice"; import type { UserSlice } from "./UserSlice"; export type LogType = "INFO" | "ERROR" | "WARN" | "DEBUG"; @@ -48,15 +45,7 @@ export type SessionSlice = { }; export const createSessionSlice: StateCreator< - SessionSlice & - UserSlice & - ProjectArtSlice & - SettingsSlice & - SkinSlice & - ProjectSlice & - ApiSlice & - UserApiSlice & - AuthApiSlice, + SessionSlice & UserSlice & SettingsSlice & ApiSlice & UserApiSlice & AuthApiSlice, [], [], SessionSlice @@ -75,17 +64,17 @@ export const createSessionSlice: StateCreator< const { sessionUUID: uuid, saveAllData, loadedSession } = get(); if (!!false && !loadedSession) { - const { invoke } = await import("@tauri-apps/api/tauri"); + const { invoke } = await import("@tauri-apps/api"); const { appDataDir, join } = await import("@tauri-apps/api/path"); - const { exists, readTextFile } = await import("@tauri-apps/api/fs"); + const { exists, readTextFile } = await import("@tauri-apps/plugin-fs"); let sessionUUID = uuid; if (sessionUUID === "") { sessionUUID = await invoke("get_session_uuid", {}).then(async response => - typeof response === "string" ? response : "", + typeof response === "string" ? response : "" ); set({ sessionUUID, - loadedSession: true, + loadedSession: true }); } const appDataDirPath = await appDataDir(); @@ -97,32 +86,31 @@ export const createSessionSlice: StateCreator< } const sessionJSON: Session = JSON.parse(sessionFile); set({ - backendTokens: sessionJSON, - loadedSession: true, + session: sessionJSON, + loadedSession: true }); } else { await saveAllData(); set({ - loadedSession: true, + loadedSession: true }); } } }, saveAllData: async () => { - const { saveSession, saveProject } = get(); + const { saveSession } = get(); await saveSession(); - await saveProject(); }, saveSession: async () => { - const { loadedSession, sessionUUID: uuid, backendTokens: session, createSession, project } = get(); + const { loadedSession, sessionUUID: uuid, backendTokens: session, createSession } = get(); if (!!false && loadedSession) { const { appDataDir, join } = await import("@tauri-apps/api/path"); - const { invoke } = await import("@tauri-apps/api/tauri"); - const { exists, BaseDirectory, createDir, writeTextFile } = await import("@tauri-apps/api/fs"); + const { invoke } = await import("@tauri-apps/api"); + const { exists, BaseDirectory, mkdir, writeTextFile } = await import("@tauri-apps/plugin-fs"); let sessionUUID = uuid; if (sessionUUID === "") { sessionUUID = await invoke("get_session_uuid", {}).then(async response => - typeof response === "string" ? response : "", + typeof response === "string" ? response : "" ); set({ sessionUUID }); } @@ -130,30 +118,29 @@ export const createSessionSlice: StateCreator< const sessionJsonPath = await join(appDataDirPath, "Sessions", sessionUUID, "session.json"); await exists(sessionJsonPath).then(async exists => { if (!exists) { - await createDir(sessionJsonPath, { - dir: BaseDirectory.AppData, - recursive: true, + await mkdir(sessionJsonPath, { + baseDir: BaseDirectory.AppData, + recursive: true }); } }); if (session === undefined) { - set({ backendTokens: createSession() }); + set({ session: createSession() }); } - const sessionJSON = JSON.stringify({ ...session, project }, null, 4); + const sessionJSON = JSON.stringify({ ...session }, null, 4); await writeTextFile(sessionJsonPath, sessionJSON); } }, createSession: () => { - const { sessionUUID: uuid, currentProject } = get(); + const { sessionUUID: uuid } = get(); return { uuid, - changed: false, - current_project: currentProject, + changed: false } as Session; }, setOutputLogs: (outputLogs: OutputLog[]) => { set({ - outputLogs, + outputLogs }); }, setFoundNewVersion: (foundNewVersion: boolean) => { @@ -169,37 +156,15 @@ export const createSessionSlice: StateCreator< redirect(to); }, resetSession: (redirectToHome?: boolean) => { - const { redirectTo, project, defaultPartnerArt, defaultPartnerArtEnabled } = get(); - if (project !== null && project.skinPack !== null) { - project.skinPack.skinList.forEach(skin => URL.revokeObjectURL(skin.url_blob_path)); - } + const { redirectTo } = get(); + set(state => ({ ...state, - project: null, - currentProject: undefined, - packType: PackType.NONE, - steps: [0], - currentStep: 0, - sidebarSteps: { - title: ["packType"], - stepsPath: ["/project"], - color: "#FDFEFE", - }, checkNewVersion: false, downloadNewVersion: false, foundNewVersion: false, newVersion: undefined, - skin: STEVE_SKIN, - options: { - model: "slim", - pixelRatio: "match-device", - background: "#17181C", - }, - outputLogs: [], - viewer: null, - editingSkin: null, - defaultPartnerArt, - defaultPartnerArtEnabled, + outputLogs: [] })); if (redirectToHome) { redirectTo("/"); @@ -212,9 +177,9 @@ export const createSessionSlice: StateCreator< ...state.outputLogs, { type: "ERROR", - message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" "), - }, - ], + message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" ") + } + ] })); error(message, objects); }, @@ -225,9 +190,9 @@ export const createSessionSlice: StateCreator< ...state.outputLogs, { type, - message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" "), - }, - ], + message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" ") + } + ] })); }, addInfo: (message: string, ...objects: any[]) => { @@ -237,9 +202,9 @@ export const createSessionSlice: StateCreator< ...state.outputLogs, { type: "INFO", - message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" "), - }, - ], + message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" ") + } + ] })); info(message, objects); }, @@ -250,9 +215,9 @@ export const createSessionSlice: StateCreator< ...state.outputLogs, { type: "WARN", - message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" "), - }, - ], + message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" ") + } + ] })); warn(message, objects); }, @@ -263,9 +228,9 @@ export const createSessionSlice: StateCreator< ...state.outputLogs, { type: "DEBUG", - message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" "), - }, - ], + message: message + objects.map(object => JSON.stringify(object, null, 4)).join(" ") + } + ] })); debug(message, objects); }, @@ -277,7 +242,7 @@ export const createSessionSlice: StateCreator< const responseTime = getTime(start, performance.now()); addLogByType( type, - `${name} [END] ` + `${responseTime.minutes}m : ${responseTime.seconds}s : ${responseTime.milliseconds}ms`, + `${name} [END] ` + `${responseTime.minutes}m : ${responseTime.seconds}s : ${responseTime.milliseconds}ms` ); return result; }, @@ -301,5 +266,5 @@ export const createSessionSlice: StateCreator< break; } } - }, + } }); diff --git a/src/store/app/SkinSlice.tsx b/src/store/app/SkinSlice.tsx deleted file mode 100644 index 30dcb0f..0000000 --- a/src/store/app/SkinSlice.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import type { SkinViewerOptions } from "skinview3d"; -import type { StateCreator } from "zustand"; -import { STEVE_SKIN, type Skin, type SkinToExport } from "../../lib/Objects/Skin"; -import { formatLocalizationSkinName, formatSkinFilePath } from "../../lib/PackageUtils/PackageUtils"; -import type { ApiSlice } from "../api/ApiSlice"; -import type { AuthApiSlice } from "../api/AuthApiSlice"; -import type { UserApiSlice } from "../api/UserApiSlice"; -import type { ProjectArtSlice } from "./ProjectArtSlice"; -import type { ProjectSlice } from "./ProjectSlice"; -import type { SessionSlice } from "./SessionSlice"; -import type { SettingsSlice } from "./SettingsSlice"; -import type { UserSlice } from "./UserSlice"; - -export interface SkinSlice { - skin: Skin; - options: SkinViewerOptions; - editingSkin: Skin | null; - updateSkin: (newSkin: Skin) => void; - setEditingSkin: (newSkin: Skin | null) => void; - updateSkinFile: (currentSkin: Skin, deleteSkin?: boolean) => Promise; -} - -export const createSkinSlice: StateCreator< - SessionSlice & - UserSlice & - ProjectArtSlice & - SettingsSlice & - SkinSlice & - ProjectSlice & - ApiSlice & - UserApiSlice & - AuthApiSlice, - [], - [], - SkinSlice -> = (set, get) => ({ - skin: STEVE_SKIN, - options: { - model: "slim", - pixelRatio: "match-device", - background: "#17181C", - }, - editingSkin: null, - updateSkin: (newSkin: Skin) => { - const { project, addError } = get(); - if (project === null || project.skinPack === null) return; - - set({ - skin: newSkin, - }); - try { - project.skinPack.skinList.forEach((localSkin, i) => { - if (localSkin.url_blob_path === newSkin.url_blob_path && project.skinPack) { - project.skinPack.skinList[i] = newSkin; - } - }); - - project.skinPack?.skinList.forEach((localSkin, i) => { - project.skinPack?.texts.forEach(text => { - text.translations.forEach((traduction, k) => { - if (i === k) { - traduction.key = formatLocalizationSkinName(localSkin.name); - traduction.value = localSkin.name.trim(); - } - }); - }); - }); - } catch (err) { - addError("Found New Error while changing the Skin: ", newSkin, " ERROR:", err); - } - - set({ - project, - }); - }, - setEditingSkin: (newSkin: Skin | null) => { - set({ editingSkin: newSkin }); - }, - updateSkinFile: async (currentSkin: Skin, deleteSkin?: boolean) => { - const { project, skin, updateSkin, saveAllData, getProjectNameFormatted } = get(); - if (!!false && project !== null) { - const { appDataDir, join } = await import("@tauri-apps/api/path"); - const { BaseDirectory, removeFile, writeTextFile } = await import("@tauri-apps/api/fs"); - if (deleteSkin) { - if (skin !== null && project?.skinPack !== null && project?.skinPack.skinList.length !== 0) { - const newSkins = project?.skinPack.skinList.filter(skin => skin !== currentSkin); - const skinPackNameFormatted = getProjectNameFormatted() - ?.split(/\s+/) - .map((word, index) => { - if (typeof word[0] !== "string") return ""; - if (index === 0) { - return word.replace(/[^a-zA-Z0-9_\-.]/g, ""); - } - return ( - word[0].toUpperCase() + - word - .slice(1) - .toLowerCase() - .replace(/[^a-zA-Z0-9_\-.]/g, "") - ); - }) - .join(""); - const appDataDirPath = await appDataDir(); - await removeFile(currentSkin.file_path) - .catch(() => {}) - .finally(async () => { - const skinsToSave: SkinToExport[] = []; - newSkins.forEach(skin => { - skinsToSave.push({ - localization_name: formatLocalizationSkinName(skin.name), - geometry: `geometry.humanoid.${skin.model === "default" ? "custom" : "customSlim"}`, - texture: formatSkinFilePath(skin.name, skin.model), - type: skin.price, - }); - }); - URL.revokeObjectURL(currentSkin.url_blob_path); - const { invoke } = await import("@tauri-apps/api/tauri"); - const projectUUID = await invoke("get_project_uuid", {}).then(async response => { - if (typeof response === "string") { - return response; - } - return ""; - }); - const defaultAppSkinPath = await join( - appDataDirPath, - "Projects", - projectUUID, - "out", - "Content", - "skin_pack", - ); - const skinsJson = { - skins: skinsToSave, - serialize_name: skinPackNameFormatted, - localization_name: skinPackNameFormatted, - }; - const skinsAsJSON = JSON.stringify(skinsJson, null, 4); - const finalSkinMetadata = await join(defaultAppSkinPath, "skins.json"); - - project.skinPack?.texts.forEach(text => { - text.translations.filter((traduction, i) => { - if (traduction.key === currentSkin.name.replace(/[^a-zA-Z0-9_\-.]/g, "")) { - text.translations.splice(i, 1); - } - return true; - }); - }); - - await writeTextFile(finalSkinMetadata, skinsAsJSON, { - dir: BaseDirectory.AppData, - }) - .catch(_err => { - /* showNotification({ - title: err, - message: "", - color: "red", - icon: , - id: "skin-save-error", - }) */ - }) - .finally(async () => { - if (!project.skinPack) return; - project.skinPack.skinList = newSkins; - if (newSkins.length === 0) { - project.skinPack.skinResolution = null; - } - - updateSkin( - currentSkin !== skin ? skin : newSkins.length === 0 ? STEVE_SKIN : newSkins[0], - ); - - saveAllData(); - }); - }); - } else { - updateSkin({ - ...skin, - model: "default", - name: "", - price: "paid", - }); - if (project.skinPack) { - project.skinPack.skinResolution = null; - } - } - } - } else { - // TODO: Create a Backend system to support this for the web version. - /* showNotification({ - title: t("pack.errors.featureNotAvailable"), - message: "", - color: "red", - icon: , - }); */ - } - }, -}); diff --git a/src/store/app/UserSlice.ts b/src/store/app/UserSlice.ts index 28be01f..7863208 100644 --- a/src/store/app/UserSlice.ts +++ b/src/store/app/UserSlice.ts @@ -4,7 +4,7 @@ import type { SignInFromType, SignUpFromType, UpdateBasicUserInfoFormType, - UpdateCriticalUserInfoFormType, + UpdateCriticalUserInfoFormType } from "@/schemas/authSchema"; import type { Session } from "next-auth"; import { signIn as signInNextAuth, signOut as signOutNextAuth } from "next-auth/react"; @@ -12,11 +12,8 @@ import type { StateCreator } from "zustand"; import type { ApiSlice } from "../api/ApiSlice"; import type { AuthApiSlice } from "../api/AuthApiSlice"; import type { UserApiSlice } from "../api/UserApiSlice"; -import type { ProjectArtSlice } from "./ProjectArtSlice"; -import type { ProjectSlice } from "./ProjectSlice"; import type { SessionSlice } from "./SessionSlice"; import type { SettingsSlice } from "./SettingsSlice"; -import type { SkinSlice } from "./SkinSlice"; export interface UserSlice { backendTokens: BackendTokens | null; @@ -41,15 +38,7 @@ export interface UserSlice { } export const createUserSlice: StateCreator< - SessionSlice & - UserSlice & - ProjectArtSlice & - SettingsSlice & - SkinSlice & - ProjectSlice & - ApiSlice & - UserApiSlice & - AuthApiSlice, + SessionSlice & UserSlice & SettingsSlice & ApiSlice & UserApiSlice & AuthApiSlice, [], [], UserSlice @@ -67,15 +56,15 @@ export const createUserSlice: StateCreator< "credentials", { redirect: false, - ...signInForm, + ...signInForm }, - signInForm, + signInForm ); if (!nextAuthResponse) { toast({ title: "Error", - description: "An error occurred while signing in. Please try again.", + description: "An error occurred while signing in. Please try again." }); return false; } @@ -86,7 +75,7 @@ export const createUserSlice: StateCreator< } toast({ title: nextAuthResponse.error, - variant: "destructive", + variant: "destructive" }); return false; } @@ -102,7 +91,7 @@ export const createUserSlice: StateCreator< if (!userResponse) { toast({ title: "Error", - description: "An error occurred while signing up. Please try again.", + description: "An error occurred while signing up. Please try again." }); return false; } @@ -110,7 +99,7 @@ export const createUserSlice: StateCreator< userResponse.errors.forEach(error => { toast({ title: error.message, - variant: "destructive", + variant: "destructive" }); }); return false; @@ -123,7 +112,7 @@ export const createUserSlice: StateCreator< if (!userResponse) { toast({ title: "Error", - description: "An error occurred while verifying your email. Please try again.", + description: "An error occurred while verifying your email. Please try again." }); return false; } @@ -131,7 +120,7 @@ export const createUserSlice: StateCreator< userResponse.errors.forEach(error => { toast({ title: error.message, - variant: "destructive", + variant: "destructive" }); }); return false; @@ -140,13 +129,13 @@ export const createUserSlice: StateCreator< const nextAuthResponse = await signInNextAuth("credentials", { redirect: false, email, - password, + password }); if (!nextAuthResponse) { toast({ title: "Error", - description: "An error occurred while signing in. Please try again.", + description: "An error occurred while signing in. Please try again." }); return false; } @@ -154,7 +143,7 @@ export const createUserSlice: StateCreator< if ("error" in nextAuthResponse && nextAuthResponse.error) { toast({ title: nextAuthResponse.error, - variant: "destructive", + variant: "destructive" }); return false; } @@ -167,7 +156,7 @@ export const createUserSlice: StateCreator< signOut: async () => { const { signOutUser } = get(); set({ - signingOut: true, + signingOut: true }); await signOutUser() .then(() => { @@ -176,7 +165,7 @@ export const createUserSlice: StateCreator< requestForceRefreshHook: false, notifications: [], signedIn: false, - backendTokens: null, + backendTokens: null }); }) .finally(async () => { @@ -201,13 +190,13 @@ export const createUserSlice: StateCreator< const nextAuthResponse = await signInNextAuth("credentials", { redirect: false, email: user.email, - password: updateUserForm.password, + password: updateUserForm.password }); if (!nextAuthResponse) { toast({ title: "Error", - description: "An error occurred while signing in. Please try again.", + description: "An error occurred while signing in. Please try again." }); return false; } @@ -215,7 +204,7 @@ export const createUserSlice: StateCreator< if ("error" in nextAuthResponse && nextAuthResponse.error) { toast({ title: nextAuthResponse.error, - variant: "destructive", + variant: "destructive" }); return false; } @@ -231,26 +220,26 @@ export const createUserSlice: StateCreator< loggingIn: false, requestForceRefreshHook: false, notifications: [], - backendTokens: null, + backendTokens: null }); }, clearBasicData: () => {}, addNotification: notification => { const { notifications } = get(); set({ - notifications: [...notifications, notification], + notifications: [...notifications, notification] }); }, clearNotifications: () => { set({ - notifications: [], + notifications: [] }); }, setSession: session => { set({ backendTokens: session.backendTokens, user: session.user, - expiresIn: session.expires, + expiresIn: session.expires }); - }, + } });