10 Commits

14 changed files with 365 additions and 263 deletions

View File

@@ -70,7 +70,6 @@ function getGlobalConfig(osuPath) {
path: "", path: "",
get: async (key) => { get: async (key) => {
if (!configFileInfo.path) { if (!configFileInfo.path) {
console.log("config file not loaded");
return ""; return "";
} }
const fileStream = await fs.promises.readFile( const fileStream = await fs.promises.readFile(
@@ -181,11 +180,6 @@ async function getFilesThatNeedUpdate(osuPath, releaseStreamFiles) {
if ( if (
fileHashOnDisk.trim().toLowerCase() != fileHash.trim().toLowerCase() fileHashOnDisk.trim().toLowerCase() != fileHash.trim().toLowerCase()
) { ) {
console.log({
fileOnDisk,
fileHashOnDisk,
fileHash,
});
updateFiles.push(updatePatch); updateFiles.push(updatePatch);
} }
} else updateFiles.push(updatePatch); } else updateFiles.push(updatePatch);
@@ -224,6 +218,11 @@ function downloadUpdateFiles(osuPath, updateFiles) {
}); });
}); });
try { try {
if (fs.existsSync(path.join(osuPath, fileName))) {
await fs.promises.rm(path.join(osuPath, fileName), {
force: true,
});
}
await fs.promises.writeFile( await fs.promises.writeFile(
path.join(osuPath, fileName), path.join(osuPath, fileName),
axiosDownloadWithProgress.data, axiosDownloadWithProgress.data,
@@ -298,6 +297,11 @@ function downloadPatcherUpdates(osuPath, patcherUpdates) {
}); });
try { try {
if (fs.existsSync(path.join(osuPath, "EZPPLauncher", fileName))) {
await fs.promises.rm(path.join(osuPath, "EZPPLauncher", fileName), {
force: true,
});
}
await fs.promises.writeFile( await fs.promises.writeFile(
path.join(osuPath, "EZPPLauncher", fileName), path.join(osuPath, "EZPPLauncher", fileName),
axiosDownloadWithProgress.data, axiosDownloadWithProgress.data,
@@ -362,6 +366,12 @@ function downloadUIFiles(osuPath, uiFiles) {
}, },
}); });
try { try {
if (fs.existsSync(path.join(osuPath, "EZPPLauncher", fileName))) {
await fs.promises.rm(path.join(osuPath, "EZPPLauncher", fileName), {
force: true,
});
}
await fs.promises.writeFile( await fs.promises.writeFile(
path.join(osuPath, "EZPPLauncher", fileName), path.join(osuPath, "EZPPLauncher", fileName),
axiosDownloadWithProgress.data, axiosDownloadWithProgress.data,
@@ -413,17 +423,43 @@ async function findOsuInstallation() {
value = value.substring(1, value.length - 3); value = value.substring(1, value.length - 3);
return path.dirname(value.trim()); return path.dirname(value.trim());
} }
/* const osuStruct = await regedit.listValuesSync(osuLocationFromDefaultIcon);
for (const line of osuStruct.split("\n")) {
if (line.includes("REG_SZ")) {
let value = line.trim().split(" ")[2];
value = value.substring(1, value.length - 3);
return path.dirname(value.trim());
}
} */
return undefined; return undefined;
} }
async function updateOsuConfigHashes(osuPath) {
const osuCfg = path.join(osuPath, "osu!.cfg");
const fileStream = await fs.promises.readFile(osuCfg, "utf-8");
const lines = fileStream.split(/\r?\n/);
const newLines = [];
for (const line of lines) {
if (line.includes(" = ")) {
const argsPair = line.split(" = ", 2);
const key = argsPair[0];
const value = argsPair[1];
if (key.startsWith("h_")) {
const fileName = key.substring(2, key.length);
const filePath = path.join(osuPath, fileName);
if (!fs.existsSync(filePath)) continue;
const binaryFileContents = await fs.promises.readFile(filePath);
const existingFileMD5 = crypto.createHash("md5").update(
binaryFileContents,
).digest("hex");
if (value == existingFileMD5) newLines.push(line);
else newLines.push(`${key} = ${existingFileMD5}`);
} else if (line.startsWith("u_UpdaterAutoStart")) {
newLines.push(`${key} = 0`);
} else {
newLines.push(line);
}
} else {
newLines.push(line);
}
}
await fs.promises.writeFile(osuCfg, newLines.join("\n"), "utf-8");
}
module.exports = { module.exports = {
isValidOsuFolder, isValidOsuFolder,
getUserConfig, getUserConfig,
@@ -438,4 +474,5 @@ module.exports = {
getUIFiles, getUIFiles,
replaceUIFile, replaceUIFile,
findOsuInstallation, findOsuInstallation,
updateOsuConfigHashes,
}; };

View File

@@ -3,6 +3,7 @@ const { appName, appVersion } = require("./appInfo.js");
const clientId = "1032772293220384808"; const clientId = "1032772293220384808";
let richPresence; let richPresence;
let intervalId;
let currentStatus = { let currentStatus = {
details: " ", details: " ",
@@ -31,7 +32,8 @@ module.exports = {
richPresence = new DiscordRPC.AutoClient({ transport: "ipc" }); richPresence = new DiscordRPC.AutoClient({ transport: "ipc" });
richPresence.endlessLogin({ clientId }); richPresence.endlessLogin({ clientId });
richPresence.once("ready", () => { richPresence.once("ready", () => {
setInterval(() => { richPresence.setActivity(currentStatus);
intervalId = setInterval(() => {
richPresence.setActivity(currentStatus); richPresence.setActivity(currentStatus);
}, 2500); }, 2500);
}); });
@@ -39,6 +41,7 @@ module.exports = {
}, },
disconnect: async () => { disconnect: async () => {
if (richPresence) { if (richPresence) {
clearInterval(intervalId);
await richPresence.clearActivity(); await richPresence.clearActivity();
await richPresence.destroy(); await richPresence.destroy();
richPresence = null; richPresence = null;
@@ -53,6 +56,8 @@ module.exports = {
currentStatus.smallImageText = osuVersion ? `osu! ${osuVersion}` : " "; currentStatus.smallImageText = osuVersion ? `osu! ${osuVersion}` : " ";
}, },
update: () => { update: () => {
richPresence.setActivity(currentStatus); if (richPresence) {
} richPresence.setActivity(currentStatus);
}; }
},
};

25
electron/updateCheck.js Normal file
View File

@@ -0,0 +1,25 @@
const semver = require("semver");
const { appVersion } = require("./appInfo");
const repoApiUrl =
"https://git.ez-pp.farm/api/v1/repos/EZPPFarm/EZPPLauncher/releases?limit=1";
const releasesUrl =
"https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest";
module.exports = {
updateAvailable: async () => {
try {
const latestRelease = await fetch(repoApiUrl);
const json = await latestRelease.json();
if (json.length <= 0) return false;
return {
update: semver.lt(appVersion, json[0].tag_name),
release: json[0],
};
} catch (err) {
return { update: false };
}
},
releasesUrl,
};

99
main.js
View File

@@ -1,5 +1,7 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow, Menu, ipcMain, dialog } = require("electron"); const { app, BrowserWindow, Menu, ipcMain, dialog, shell } = require(
"electron",
);
const path = require("path"); const path = require("path");
const serve = require("electron-serve"); const serve = require("electron-serve");
const loadURL = serve({ directory: "public" }); const loadURL = serve({ directory: "public" });
@@ -21,6 +23,7 @@ const {
downloadUIFiles, downloadUIFiles,
replaceUIFile, replaceUIFile,
findOsuInstallation, findOsuInstallation,
updateOsuConfigHashes,
} = require("./electron/osuUtil"); } = require("./electron/osuUtil");
const { formatBytes } = require("./electron/formattingUtil"); const { formatBytes } = require("./electron/formattingUtil");
const windowName = require("get-window-by-name"); const windowName = require("get-window-by-name");
@@ -29,6 +32,8 @@ const { runFileDetached } = require("./electron/executeUtil");
const richPresence = require("./electron/richPresence"); const richPresence = require("./electron/richPresence");
const cryptUtil = require("./electron/cryptoUtil"); const cryptUtil = require("./electron/cryptoUtil");
const { getHwId } = require("./electron/hwidUtil"); const { getHwId } = require("./electron/hwidUtil");
const { appName, appVersion } = require("./electron/appInfo");
const { updateAvailable, releasesUrl } = require("./electron/updateCheck");
// Keep a global reference of the window object, if you don't, the window will // Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected. // be closed automatically when the JavaScript object is garbage collected.
@@ -50,7 +55,6 @@ function startOsuStatus() {
osuCheckInterval = setInterval(async () => { osuCheckInterval = setInterval(async () => {
const osuWindowTitle = windowName.getWindowText("osu!.exe"); const osuWindowTitle = windowName.getWindowText("osu!.exe");
if (osuWindowTitle.length < 0) { if (osuWindowTitle.length < 0) {
console.log("No osu! window found");
return; return;
} }
const firstInstance = osuWindowTitle[0]; const firstInstance = osuWindowTitle[0];
@@ -73,18 +77,22 @@ function startOsuStatus() {
const windowTitle = firstInstance.processTitle; const windowTitle = firstInstance.processTitle;
lastOsuStatus = windowTitle; lastOsuStatus = windowTitle;
const currentStatusRequest = await fetch("https://api.ez-pp.farm/get_player_status?name=" + currentUser.username); const currentStatusRequest = await fetch(
"https://api.ez-pp.farm/get_player_status?name=" + currentUser.username,
);
const currentStatus = await currentStatusRequest.json(); const currentStatus = await currentStatusRequest.json();
if (!("player_status" in currentStatus)) return; if (!("player_status" in currentStatus)) return;
if (!("status" in currentStatus.player_status)) return; if (!("status" in currentStatus.player_status)) return;
let details = "Idle..."; let details = "Idle...";
let infoText = currentStatus.player_status.status.info_text.length > 0 ? currentStatus.player_status.status.info_text : " "; let infoText = currentStatus.player_status.status.info_text.length > 0
? currentStatus.player_status.status.info_text
: " ";
switch (currentStatus.player_status.status.action) { switch (currentStatus.player_status.status.action) {
case 1: case 1:
details = "AFK..." details = "AFK...";
infoText = " "; infoText = " ";
break; break;
case 2: case 2:
@@ -94,7 +102,7 @@ function startOsuStatus() {
details = "Editing..."; details = "Editing...";
break; break;
case 4: case 4:
details = "Modding..." details = "Modding...";
break; break;
case 5: case 5:
details = "Multiplayer: Selecting a Beatmap..."; details = "Multiplayer: Selecting a Beatmap...";
@@ -124,7 +132,7 @@ function startOsuStatus() {
richPresence.updateStatus({ richPresence.updateStatus({
details, details,
state: infoText state: infoText,
}); });
richPresence.update(); richPresence.update();
@@ -260,6 +268,23 @@ function registerIPCPipes() {
return config.all(); return config.all();
}); });
ipcMain.handle("ezpplauncher:setting-update", async (e, args) => {
for (const key of Object.keys(args)) {
const value = args[key];
if (key == "presence") {
if (!value) richPresence.disconnect();
else richPresence.connect();
}
if (typeof value == "boolean") {
config.set(key, value ? "true" : "false");
} else {
config.set(key, value);
}
}
});
ipcMain.handle("ezpplauncher:detect-folder", async (e) => { ipcMain.handle("ezpplauncher:detect-folder", async (e) => {
const detected = await findOsuInstallation(); const detected = await findOsuInstallation();
if (detected && await isValidOsuFolder(detected)) { if (detected && await isValidOsuFolder(detected)) {
@@ -295,8 +320,14 @@ function registerIPCPipes() {
return config.all(); return config.all();
}); });
ipcMain.handle("ezpplauncher:launch", async (e, args) => { ipcMain.handle("ezpplauncher:exitAndUpdate", async (e) => {
patch = args.patch; await shell.openExternal(releasesUrl);
app.exit();
});
ipcMain.handle("ezpplauncher:launch", async (e) => {
const configPatch = config.get("patch");
patch = configPatch != undefined ? configPatch == "true" : true;
mainWindow.webContents.send("ezpplauncher:launchstatus", { mainWindow.webContents.send("ezpplauncher:launchstatus", {
status: "Checking osu! directory...", status: "Checking osu! directory...",
}); });
@@ -336,7 +367,7 @@ function registerIPCPipes() {
mainWindow.webContents.send("ezpplauncher:alert", { mainWindow.webContents.send("ezpplauncher:alert", {
type: "error", type: "error",
message: message:
`Failed to download/replace ${filename}!\nMaybe try to rerun the Launcher as Admin.`, `Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
}); });
}); });
uiDownloader.eventEmitter.on("data", (data) => { uiDownloader.eventEmitter.on("data", (data) => {
@@ -344,8 +375,9 @@ function registerIPCPipes() {
progress: Math.ceil(data.progress), progress: Math.ceil(data.progress),
}); });
mainWindow.webContents.send("ezpplauncher:launchstatus", { mainWindow.webContents.send("ezpplauncher:launchstatus", {
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total) status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${
})...`, formatBytes(data.total)
})...`,
}); });
}); });
await uiDownloader.startDownload(); await uiDownloader.startDownload();
@@ -366,7 +398,7 @@ function registerIPCPipes() {
mainWindow.webContents.send("ezpplauncher:alert", { mainWindow.webContents.send("ezpplauncher:alert", {
type: "error", type: "error",
message: message:
`Failed to download/replace ${filename}!\nMaybe try to rerun the Launcher as Admin.`, `Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
}); });
}); });
updateDownloader.eventEmitter.on("data", (data) => { updateDownloader.eventEmitter.on("data", (data) => {
@@ -374,8 +406,9 @@ function registerIPCPipes() {
progress: Math.ceil(data.progress), progress: Math.ceil(data.progress),
}); });
mainWindow.webContents.send("ezpplauncher:launchstatus", { mainWindow.webContents.send("ezpplauncher:launchstatus", {
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total) status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${
})...`, formatBytes(data.total)
})...`,
}); });
}); });
await updateDownloader.startDownload(); await updateDownloader.startDownload();
@@ -412,7 +445,7 @@ function registerIPCPipes() {
mainWindow.webContents.send("ezpplauncher:alert", { mainWindow.webContents.send("ezpplauncher:alert", {
type: "error", type: "error",
message: message:
`Failed to download/replace ${filename}!\nMaybe try to rerun the Launcher as Admin.`, `Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
}); });
}); });
patcherDownloader.eventEmitter.on("data", (data) => { patcherDownloader.eventEmitter.on("data", (data) => {
@@ -420,8 +453,9 @@ function registerIPCPipes() {
progress: Math.ceil(data.progress), progress: Math.ceil(data.progress),
}); });
mainWindow.webContents.send("ezpplauncher:launchstatus", { mainWindow.webContents.send("ezpplauncher:launchstatus", {
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total) status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${
})...`, formatBytes(data.total)
})...`,
}); });
}); });
await patcherDownloader.startDownload(); await patcherDownloader.startDownload();
@@ -446,11 +480,17 @@ function registerIPCPipes() {
mainWindow.webContents.send("ezpplauncher:launchstatus", { mainWindow.webContents.send("ezpplauncher:launchstatus", {
status: "Preparing launch...", status: "Preparing launch...",
}); });
await updateOsuConfigHashes(osuPath);
await replaceUIFile(osuPath, false);
const userConfig = getUserConfig(osuPath); const userConfig = getUserConfig(osuPath);
richPresence.updateVersion(await userConfig.get("LastVersion")); richPresence.updateVersion(await userConfig.get("LastVersion"));
richPresence.update(); richPresence.update();
await userConfig.set("ShowInterfaceDuringRelax", "1");
if (currentUser) { if (currentUser) {
await userConfig.set("CredentialEndpoint", "ez-pp.farm");
await userConfig.set("SavePassword", "1");
await userConfig.set("SaveUsername", "1");
await userConfig.set("Username", currentUser.username); await userConfig.set("Username", currentUser.username);
await userConfig.set("Password", currentUser.password); await userConfig.set("Password", currentUser.password);
} }
@@ -466,7 +506,7 @@ function registerIPCPipes() {
richPresence.updateVersion(); richPresence.updateVersion();
richPresence.updateStatus({ richPresence.updateStatus({
state: "Idle in Launcher...", state: "Idle in Launcher...",
details: undefined details: undefined,
}); });
richPresence.update(); richPresence.update();
mainWindow.webContents.send("ezpplauncher:launchstatus", { mainWindow.webContents.send("ezpplauncher:launchstatus", {
@@ -476,14 +516,13 @@ function registerIPCPipes() {
setTimeout(async () => { setTimeout(async () => {
await replaceUIFile(osuPath, true); await replaceUIFile(osuPath, true);
mainWindow.webContents.send("ezpplauncher:launchabort"); mainWindow.webContents.send("ezpplauncher:launchabort");
osuLoaded = false;
}, 5000); }, 5000);
}; };
await replaceUIFile(osuPath, false);
runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook); runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook);
mainWindow.hide(); mainWindow.hide();
startOsuStatus(); startOsuStatus();
/* mainWindow.webContents.send("ezpplauncher:launchprogress", { /* mainWindow.webContents.send("ezpplauncher:launchprogress", {
progress: 0, progress: 0,
}); });
@@ -504,6 +543,7 @@ function createWindow() {
resizable: false, resizable: false,
frame: false, frame: false,
titleBarStyle: "hidden", titleBarStyle: "hidden",
title: `${appName} ${appVersion}`,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
preload: path.join(__dirname, "preload.js"), preload: path.join(__dirname, "preload.js"),
@@ -530,7 +570,15 @@ function createWindow() {
} }
registerIPCPipes(); registerIPCPipes();
richPresence.connect();
const presenceEnabled = config.get("presence");
if (presenceEnabled == undefined) {
richPresence.connect();
} else {
if (presenceEnabled == "true") {
richPresence.connect();
}
}
// Uncomment the following line of code when app is ready to be packaged. // Uncomment the following line of code when app is ready to be packaged.
// loadURL(mainWindow); // loadURL(mainWindow);
@@ -550,8 +598,13 @@ function createWindow() {
// Emitted when the window is ready to be shown // Emitted when the window is ready to be shown
// This helps in showing the window gracefully. // This helps in showing the window gracefully.
mainWindow.once("ready-to-show", () => { mainWindow.once("ready-to-show", async () => {
const updateInfo = await updateAvailable();
if (updateInfo.update) {
mainWindow.webContents.send("ezpplauncher:update", updateInfo.release);
}
mainWindow.show(); mainWindow.show();
mainWindow.focus();
}); });
} }

150
package-lock.json generated
View File

@@ -20,7 +20,9 @@
"electron-serve": "^1.1.0", "electron-serve": "^1.1.0",
"get-window-by-name": "^2.0.0", "get-window-by-name": "^2.0.0",
"regedit-rs": "^1.0.2", "regedit-rs": "^1.0.2",
"semver": "^7.5.4",
"svelte-french-toast": "^1.2.0", "svelte-french-toast": "^1.2.0",
"sweetalert2": "^11.10.3",
"systeminformation": "^5.21.22" "systeminformation": "^5.21.22"
}, },
"devDependencies": { "devDependencies": {
@@ -168,6 +170,14 @@
"global-agent": "^3.0.0" "global-agent": "^3.0.0"
} }
}, },
"node_modules/@electron/get/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@electron/notarize": { "node_modules/@electron/notarize": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz",
@@ -362,21 +372,6 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"node_modules/@electron/rebuild/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@electron/rebuild/node_modules/universalify": { "node_modules/@electron/rebuild/node_modules/universalify": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -1147,21 +1142,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/@npmcli/fs/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@npmcli/move-file": { "node_modules/@npmcli/move-file": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
@@ -2044,21 +2024,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/app-builder-lib/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/app-builder-lib/node_modules/universalify": { "node_modules/app-builder-lib/node_modules/universalify": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -4488,21 +4453,6 @@
"node": ">=10.0" "node": ">=10.0"
} }
}, },
"node_modules/global-agent/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"optional": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/globalthis": { "node_modules/globalthis": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
@@ -5728,20 +5678,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/node-abi/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-addon-api": { "node_modules/node-addon-api": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
@@ -5756,21 +5692,6 @@
"semver": "^7.3.5" "semver": "^7.3.5"
} }
}, },
"node_modules/node-api-version/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -5850,21 +5771,6 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/node-gyp/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.14", "version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -7712,11 +7618,17 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "6.3.1", "version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
} }
}, },
"node_modules/semver-compare": { "node_modules/semver-compare": {
@@ -7852,21 +7764,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/sirv": { "node_modules/sirv": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
@@ -8588,6 +8485,15 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true "dev": true
}, },
"node_modules/sweetalert2": {
"version": "11.10.3",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.10.3.tgz",
"integrity": "sha512-mZYtQR7v+khyEruq0SsVUa6XIdI9Aue8s2XAIpAwdlLN1T0w7mxKEjyubiBZ3/bLbHC/wGS4wNABvXWubCizvA==",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/limonte"
}
},
"node_modules/systeminformation": { "node_modules/systeminformation": {
"version": "5.21.22", "version": "5.21.22",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.22.tgz", "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.22.tgz",

View File

@@ -17,7 +17,6 @@
"electron/**/*" "electron/**/*"
], ],
"win": { "win": {
"requestedExecutionLevel": "requireAdministrator",
"target": [ "target": [
"portable" "portable"
] ]
@@ -48,7 +47,9 @@
"electron-serve": "^1.1.0", "electron-serve": "^1.1.0",
"get-window-by-name": "^2.0.0", "get-window-by-name": "^2.0.0",
"regedit-rs": "^1.0.2", "regedit-rs": "^1.0.2",
"semver": "^7.5.4",
"svelte-french-toast": "^1.2.0", "svelte-french-toast": "^1.2.0",
"sweetalert2": "^11.10.3",
"systeminformation": "^5.21.22" "systeminformation": "^5.21.22"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,5 +1,6 @@
const { Titlebar, TitlebarColor } = require("custom-electron-titlebar"); const { Titlebar, TitlebarColor } = require("custom-electron-titlebar");
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
const { appName, appVersion } = require("./electron/appInfo");
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
const titlebar = new Titlebar({ const titlebar = new Titlebar({
@@ -9,6 +10,7 @@ window.addEventListener("DOMContentLoaded", () => {
enableMnemonics: false, enableMnemonics: false,
maximizable: false, maximizable: false,
}); });
titlebar.updateTitle(`${appName} ${appVersion}`);
}); });
window.addEventListener("login-attempt", async (e) => { window.addEventListener("login-attempt", async (e) => {
@@ -57,6 +59,11 @@ window.addEventListener("settings-get", async () => {
); );
}); });
window.addEventListener("setting-update", async (e) => {
const detail = e.detail;
await ipcRenderer.invoke("ezpplauncher:setting-update", detail);
});
window.addEventListener("folder-auto", async (e) => { window.addEventListener("folder-auto", async (e) => {
const result = await ipcRenderer.invoke("ezpplauncher:detect-folder"); const result = await ipcRenderer.invoke("ezpplauncher:detect-folder");
window.dispatchEvent( window.dispatchEvent(
@@ -75,6 +82,10 @@ window.addEventListener("settings-set", async (e) => {
await ipcRenderer.invoke("ezpplauncher:settings-set", e.detail); await ipcRenderer.invoke("ezpplauncher:settings-set", e.detail);
}); });
window.addEventListener("updateExit", async () => {
await ipcRenderer.invoke("ezpplauncher:exitAndUpdate");
});
ipcRenderer.addListener("ezpplauncher:launchabort", (e, args) => { ipcRenderer.addListener("ezpplauncher:launchabort", (e, args) => {
window.dispatchEvent( window.dispatchEvent(
new CustomEvent("launch-abort"), new CustomEvent("launch-abort"),
@@ -98,3 +109,9 @@ ipcRenderer.addListener("ezpplauncher:launchprogress", (e, args) => {
new CustomEvent("launchProgressUpdate", { detail: args }), new CustomEvent("launchProgressUpdate", { detail: args }),
); );
}); });
ipcRenderer.addListener("ezpplauncher:update", (e, args) => {
window.dispatchEvent(
new CustomEvent("update", { detail: args }),
);
});

View File

@@ -5,13 +5,15 @@
DropdownItem, DropdownItem,
DropdownHeader, DropdownHeader,
DropdownDivider, DropdownDivider,
Button Button,
Indicator,
} from "flowbite-svelte"; } from "flowbite-svelte";
import { import {
ArrowLeftSolid, ArrowLeftSolid,
ArrowRightFromBracketSolid, ArrowRightFromBracketSolid,
ArrowRightToBracketSolid, ArrowRightToBracketSolid,
UserSettingsSolid HeartSolid,
UserSettingsSolid,
} from "flowbite-svelte-icons"; } from "flowbite-svelte-icons";
import ezppLogo from "../public/favicon.png"; import ezppLogo from "../public/favicon.png";
import { import {
@@ -19,7 +21,7 @@
currentUser, currentUser,
launching, launching,
launchPercentage, launchPercentage,
launchStatus launchStatus,
} from "./storage/localStore"; } from "./storage/localStore";
import { Page } from "./consts/pages"; import { Page } from "./consts/pages";
import Login from "./pages/Login.svelte"; import Login from "./pages/Login.svelte";
@@ -27,10 +29,13 @@
import toast, { Toaster } from "svelte-french-toast"; import toast, { Toaster } from "svelte-french-toast";
import type { User } from "./types/user"; import type { User } from "./types/user";
import Settings from "./pages/Settings.svelte"; import Settings from "./pages/Settings.svelte";
import Swal from "sweetalert2";
let user: User | undefined = undefined; let user: User | undefined = undefined;
let loggedIn = false; let loggedIn = false;
let updateInfo: Record<string, unknown>;
currentUser.subscribe((newUser) => { currentUser.subscribe((newUser) => {
loggedIn = newUser != undefined; loggedIn = newUser != undefined;
user = newUser; user = newUser;
@@ -44,18 +49,29 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 2000 duration: 2000,
}); });
}; };
window.addEventListener("update", async (e) => {
const update = (e as CustomEvent).detail;
await Swal.fire({
html: `EZPPLauncher ${update.tag_name} is now available!<br>Click the Button bellow to download the latest release!`,
title: "It's your lucky day!",
allowOutsideClick: false,
allowEscapeKey: false,
allowEnterKey: false,
confirmButtonText: "Thanks!",
});
window.dispatchEvent(new CustomEvent("updateExit"));
});
window.addEventListener("launchStatusUpdate", (e) => { window.addEventListener("launchStatusUpdate", (e) => {
console.log((e as CustomEvent).detail);
const status = (e as CustomEvent).detail.status; const status = (e as CustomEvent).detail.status;
launchStatus.set(status); launchStatus.set(status);
}); });
window.addEventListener("launchProgressUpdate", (e) => { window.addEventListener("launchProgressUpdate", (e) => {
console.log((e as CustomEvent).detail);
const progress = (e as CustomEvent).detail.progress; const progress = (e as CustomEvent).detail.progress;
launchPercentage.set(progress); launchPercentage.set(progress);
}); });
@@ -75,7 +91,7 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 2000 duration: 2000,
}); });
break; break;
} }
@@ -84,7 +100,7 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 4000 duration: 4000,
}); });
break; break;
} }
@@ -94,7 +110,7 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 1500 duration: 1500,
}); });
} }
} }
@@ -103,88 +119,104 @@
<Toaster></Toaster> <Toaster></Toaster>
<div class="p-2 flex flex-row justify-between items-center"> {#if !updateInfo}
<div class="flex flex-row items-center animate-fadeIn opacity-0"> <div class="p-2 flex flex-row justify-between items-center">
{#if $currentPage == Page.Settings} <div class="flex flex-row items-center animate-fadeIn opacity-0">
<Button {#if $currentPage == Page.Settings}
class="dark:active:!bg-gray-900 !ring-0 w-10 h-10 mr-1 rounded-lg animate-sideIn opacity-0 active:scale-95 transition-transform duration-75" <Button
color="light" class="dark:active:!bg-gray-900 !ring-0 w-10 h-10 mr-1 rounded-lg animate-sideIn opacity-0 active:scale-95 transition-transform duration-75"
on:click={() => { color="light"
currentPage.set(Page.Launch);
}}
>
<ArrowLeftSolid class="outline-none border-none" size="sm" />
</Button>
{/if}
<img src={ezppLogo} alt="EZPPFarm Logo" class="w-12 h-12 mr-2" />
<span class="text-gray-700 dark:text-gray-100 text-xl font-extralight">
EZPPLauncher
</span>
</div>
{#if $currentPage == Page.Launch}
<div
class="flex flex-row gap-2 w-fill cursor-pointer md:order-2 animate-fadeIn opacity-0"
>
<Avatar
class="rounded-lg border dark:border-gray-700 hover:ring-4 hover:ring-gray-200 dark:hover:ring-gray-800"
src={loggedIn
? "https://a.ez-pp.farm/" + user?.id
: "https://a.ez-pp.farm/0"}
id="avatar-menu"
/>
</div>
<Dropdown placement="bottom-start" triggeredBy="#avatar-menu">
<DropdownHeader>
<span class="block text-sm">{loggedIn ? user?.name : "Guest"}</span>
<span
class="block truncate text-sm font-medium text-gray-500 dark:text-gray-200"
>
{loggedIn ? user?.email : "Please log in!"}
</span>
</DropdownHeader>
<DropdownItem
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
on:click={() => {
if (!$launching) currentPage.set(Page.Settings);
}}
>
<UserSettingsSolid class="select-none outline-none border-none" />
Settings
</DropdownItem>
<DropdownDivider />
{#if loggedIn}
<DropdownItem
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
on:click={() => { on:click={() => {
if (!$launching) logout(); currentPage.set(Page.Launch);
}} }}
> >
<ArrowRightFromBracketSolid <ArrowLeftSolid class="outline-none border-none" size="sm" />
class="select-none outline-none border-none" </Button>
/>
Sign out
</DropdownItem>
{:else}
<DropdownItem
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
on:click={() => {
if (!$launching) currentPage.set(Page.Login);
}}
>
<ArrowRightToBracketSolid
class="select-none outline-none border-none"
/>
Login
</DropdownItem>
{/if} {/if}
</Dropdown> <img src={ezppLogo} alt="EZPPFarm Logo" class="w-12 h-12 mr-2" />
{/if} <span class="text-gray-700 dark:text-gray-100 text-xl font-extralight">
</div> EZPPLauncher
</span>
</div>
{#if $currentPage == Page.Launch}
<div
class="flex flex-row gap-2 w-fill cursor-pointer md:order-2 animate-lsideIn opacity-0"
>
<Avatar
class="rounded-lg border dark:border-gray-700 hover:ring-4 hover:ring-gray-200 dark:hover:ring-gray-800"
src={loggedIn
? "https://a.ez-pp.farm/" + user?.id
: "https://a.ez-pp.farm/0"}
id="avatar-menu"
/>
<!-- TODO: if user has donator, display heart indicator-->
{#if $currentUser && $currentUser.donor}
<Indicator
class="pointer-events-none"
color="red"
border
size="xl"
placement="top-right"
>
<span class="text-red-300 text-xs font-bold">
<HeartSolid class="select-none pointer-events-none" size="xs" />
</span>
</Indicator>
{/if}
</div>
<Dropdown placement="bottom-start" triggeredBy="#avatar-menu">
<DropdownHeader>
<span class="block text-sm">{loggedIn ? user?.name : "Guest"}</span>
<span
class="block truncate text-sm font-medium text-gray-500 dark:text-gray-200"
>
{loggedIn ? user?.email : "Please log in!"}
</span>
</DropdownHeader>
<DropdownItem
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
on:click={() => {
if (!$launching) currentPage.set(Page.Settings);
}}
>
<UserSettingsSolid class="select-none outline-none border-none" />
Settings
</DropdownItem>
<DropdownDivider />
{#if loggedIn}
<DropdownItem
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
on:click={() => {
if (!$launching) logout();
}}
>
<ArrowRightFromBracketSolid
class="select-none outline-none border-none"
/>
Sign out
</DropdownItem>
{:else}
<DropdownItem
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
on:click={() => {
if (!$launching) currentPage.set(Page.Login);
}}
>
<ArrowRightToBracketSolid
class="select-none outline-none border-none"
/>
Login
</DropdownItem>
{/if}
</Dropdown>
{/if}
</div>
{#if $currentPage == Page.Login} {#if $currentPage == Page.Login}
<Login /> <Login />
{:else if $currentPage == Page.Settings} {:else if $currentPage == Page.Settings}
<Settings /> <Settings />
{:else} {:else}
<Launch /> <Launch />
{/if}
{/if} {/if}

View File

@@ -5,6 +5,8 @@
* { * {
font-family: "Prompt"; font-family: "Prompt";
-webkit-user-select: none !important;
user-select: none !important;
} }
html .cet-titlebar { html .cet-titlebar {
@@ -56,12 +58,26 @@ html .cet-titlebar .cet-control-icon svg {
background-color: #202020 !important; background-color: #202020 !important;
color: #ececec !important; color: #ececec !important;
} }
.swal2-container {
background: #202020 !important;
}
.swal2-container .swal2-popup {
background: #323232 !important;
color: #fff !important;
}
} }
.animatedProgress div { .animatedProgress div {
transition: width 0.35s cubic-bezier(0.65, -0.02, 0.31, 1.01); transition: width 0.35s cubic-bezier(0.65, -0.02, 0.31, 1.01);
} }
.noselect {
-webkit-user-select: none !important;
user-select: none !important;
}
@keyframes progress-loading { @keyframes progress-loading {
50% { 50% {
background-position-x: -115%; background-position-x: -115%;

View File

@@ -196,7 +196,7 @@
<Checkbox bind:checked={saveCredentials} disabled={loading} <Checkbox bind:checked={saveCredentials} disabled={loading}
>Save credentials</Checkbox >Save credentials</Checkbox
> >
<div class="flex flex-col justify-center items-center gap-5 mt-1"> <div class="flex flex-col justify-center items-center gap-2 mt-1">
<Button <Button
class="dark:active:!bg-gray-900 active:scale-95 transition-transform duration-75" class="dark:active:!bg-gray-900 active:scale-95 transition-transform duration-75"
color="light" color="light"

View File

@@ -1,12 +1,5 @@
<script lang="ts"> <script lang="ts">
import { import { Button, ButtonGroup, Input, Toggle } from "flowbite-svelte";
Button,
ButtonGroup,
Input,
Label,
Toggle,
Tooltip
} from "flowbite-svelte";
import { FileSearchSolid, FolderSolid } from "flowbite-svelte-icons"; import { FileSearchSolid, FolderSolid } from "flowbite-svelte-icons";
import { patch, presence } from "./../storage/localStore"; import { patch, presence } from "./../storage/localStore";
@@ -15,6 +8,12 @@
window.addEventListener("settings-result", (e) => { window.addEventListener("settings-result", (e) => {
const settings: Record<string, string>[] = (e as CustomEvent).detail; const settings: Record<string, string>[] = (e as CustomEvent).detail;
const osuPath = settings.find((setting) => setting.key == "osuPath"); const osuPath = settings.find((setting) => setting.key == "osuPath");
const settingPatch = settings.find((setting) => setting.key == "patch");
const settingPresence = settings.find(
(setting) => setting.key == "presence"
);
patch.set(settingPatch ? settingPatch.val == "true" : true);
presence.set(settingPresence ? settingPresence.val == "true" : true);
folderPath = osuPath ? osuPath.val : ""; folderPath = osuPath ? osuPath.val : "";
}); });
window.dispatchEvent(new CustomEvent("settings-get")); window.dispatchEvent(new CustomEvent("settings-get"));
@@ -29,12 +28,16 @@
const togglePatching = () => { const togglePatching = () => {
patch.set(!$patch); patch.set(!$patch);
//TODO: save in config window.dispatchEvent(
new CustomEvent("setting-update", { detail: { patch: $patch } })
);
}; };
const togglePresence = () => { const togglePresence = () => {
presence.set(!$presence); presence.set(!$presence);
//TODO: save in config window.dispatchEvent(
new CustomEvent("setting-update", { detail: { presence: $presence } })
);
}; };
</script> </script>

View File

@@ -3,6 +3,7 @@ import { Page } from "../consts/pages";
import type { User } from "../types/user"; import type { User } from "../types/user";
export const startup = writable(false); export const startup = writable(false);
export const updateAvailable = writable(false);
export const launching = writable(false); export const launching = writable(false);
export const launchStatus = writable("Waiting..."); export const launchStatus = writable("Waiting...");
export const launchPercentage = writable(-1); export const launchPercentage = writable(-1);

View File

@@ -1,5 +1,6 @@
export type User = { export type User = {
id: number; id: number;
donor: boolean;
name: string; name: string;
email: string; email: string;
}; };

View File

@@ -12,6 +12,10 @@ const config = {
"0%": { opacity: "0", transform: "translateX(-5px)" }, "0%": { opacity: "0", transform: "translateX(-5px)" },
"100%": { opacity: "1" }, "100%": { opacity: "1" },
}, },
lslideIn: {
"0%": { opacity: "0", transform: "translateX(5px)" },
"100%": { opacity: "1" },
},
fadeIn: { fadeIn: {
"0%": { opacity: "0", transform: "translateY(5px)" }, "0%": { opacity: "0", transform: "translateY(5px)" },
"100%": { opacity: "1" }, "100%": { opacity: "1" },
@@ -23,6 +27,7 @@ const config = {
}, },
animation: { animation: {
sideIn: "slideIn 1s ease forwards", sideIn: "slideIn 1s ease forwards",
lsideIn: "lslideIn 1s ease forwards",
fadeIn: "fadeIn 1s ease forwards", fadeIn: "fadeIn 1s ease forwards",
fadeOut: "fadeOut 1s ease forwards", fadeOut: "fadeOut 1s ease forwards",
}, },