From 7a3f0ef9e27f9fb29d08948508660fca87a2720a Mon Sep 17 00:00:00 2001 From: Hydra Date: Sat, 20 Apr 2024 15:54:08 -0300 Subject: [PATCH 01/70] feat: add a modal to select game installation folder (pt and en translation) --- forge.config.ts | 6 +- src/locales/en/translation.json | 8 +- src/locales/pt/translation.json | 8 +- .../events/library/add-game-to-library.ts | 2 +- .../events/torrenting/start-game-download.ts | 6 +- src/preload.ts | 13 +- src/renderer/assets/lottie/settings.json | 1055 ++++++++++++++++- src/renderer/declaration.d.ts | 3 +- src/renderer/hooks/use-download.ts | 5 +- .../pages/game-details/game-details.tsx | 8 +- .../pages/game-details/repacks-modal.tsx | 20 +- .../game-details/select-folder-modal.css.tsx | 19 + .../game-details/select-folder-modal.tsx | 103 ++ 13 files changed, 1232 insertions(+), 24 deletions(-) create mode 100644 src/renderer/pages/game-details/select-folder-modal.css.tsx create mode 100644 src/renderer/pages/game-details/select-folder-modal.tsx diff --git a/forge.config.ts b/forge.config.ts index 217187644..94cfac577 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -20,7 +20,7 @@ const linuxPkgConfig = { icon: "images/icon.png", genericName: "Games Launcher", name: "hydra-launcher", - productName: "Hydra" + productName: "Hydra", }; const config: ForgeConfig = { @@ -50,10 +50,10 @@ const config: ForgeConfig = { }), new MakerZIP({}, ["darwin", "linux"]), new MakerRpm({ - options: linuxPkgConfig + options: linuxPkgConfig, }), new MakerDeb({ - options: linuxPkgConfig + options: linuxPkgConfig, }), ], publishers: [ diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index f59f1e876..a9fcb19df 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -77,7 +77,13 @@ "play": "Play", "deleting": "Deleting installer…", "close": "Close", - "playing_now": "Playing now" + "playing_now": "Playing now", + "change": "Change", + "select_folder_description": "Select the folder where the game will be installed", + "downloads_path": "Downloads path", + "select_folder_hint": "To change the default folder, access the", + "hydra_settings": "Hydra settings", + "download_now": "Download now" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 145163b49..c2d24840d 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -73,7 +73,13 @@ "not_played_yet": "Você ainda não jogou {{title}}", "close": "Fechar", "deleting": "Excluindo instalador…", - "playing_now": "Jogando agora" + "playing_now": "Jogando agora", + "change": "Mudar", + "select_folder_description": "Selecione a pasta em que o jogo será baixado", + "downloads_path": "Diretório do download", + "select_folder_hint": "Para trocar a pasta padrão, acesse as ", + "hydra_settings": "Configurações do Hydra", + "download_now": "Baixe agora" }, "activation": { "title": "Ativação", diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts index 8680b29a6..12e4c8bf8 100644 --- a/src/main/events/library/add-game-to-library.ts +++ b/src/main/events/library/add-game-to-library.ts @@ -10,7 +10,7 @@ const addGameToLibrary = async ( _event: Electron.IpcMainInvokeEvent, objectID: string, title: string, - gameShop: GameShop, + gameShop: GameShop ) => { const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID)); diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index 570fd2ecf..f661a956e 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -5,7 +5,6 @@ import { GameStatus } from "@main/constants"; import { registerEvent } from "../register-event"; import type { GameShop } from "@types"; -import { getDownloadsPath } from "../helpers/get-downloads-path"; import { getImageBase64 } from "@main/helpers"; import { In } from "typeorm"; @@ -14,7 +13,8 @@ const startGameDownload = async ( repackId: number, objectID: string, title: string, - gameShop: GameShop + gameShop: GameShop, + downloadPath: string ) => { const [game, repack] = await Promise.all([ gameRepository.findOne({ @@ -37,7 +37,7 @@ const startGameDownload = async ( writePipe.write({ action: "pause" }); - const downloadsPath = game?.downloadPath ?? (await getDownloadsPath()); + const downloadsPath = game?.downloadPath ?? downloadPath; await gameRepository.update( { diff --git a/src/preload.ts b/src/preload.ts index 93acde243..7ee37d0b3 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -15,8 +15,17 @@ contextBridge.exposeInMainWorld("electron", { repackId: number, objectID: string, title: string, - shop: GameShop - ) => ipcRenderer.invoke("startGameDownload", repackId, objectID, title, shop), + shop: GameShop, + downloadPath: string + ) => + ipcRenderer.invoke( + "startGameDownload", + repackId, + objectID, + title, + shop, + downloadPath + ), cancelGameDownload: (gameId: number) => ipcRenderer.invoke("cancelGameDownload", gameId), pauseGameDownload: (gameId: number) => diff --git a/src/renderer/assets/lottie/settings.json b/src/renderer/assets/lottie/settings.json index 4d37e53a0..92fa645ec 100644 --- a/src/renderer/assets/lottie/settings.json +++ b/src/renderer/assets/lottie/settings.json @@ -1 +1,1054 @@ -{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.5.6","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":120,"w":900,"h":900,"nm":"Pre-comp 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"estrela Outlines 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.933],"y":[0]},"t":0,"s":[0]},{"t":60,"s":[180]}],"ix":10},"p":{"a":0,"k":[450,450,0],"ix":2},"a":{"a":0,"k":[308.5,333,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.011,34.469],[34.47,-0.012],[-0.011,-34.469],[-34.47,0.011]],"o":[[-0.012,-34.47],[-34.469,0.011],[0.012,34.47],[34.469,-0.012]],"v":[[62.333,0.005],[-0.101,-62.387],[-62.493,0.047],[-0.059,62.439]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-68.917],[68.916,0],[0,68.916],[-68.916,0]],"o":[[0,68.916],[-68.916,0],[0,-68.917],[68.916,0]],"v":[[124.725,0.005],[-0.059,124.789],[-124.843,0.005],[-0.059,-124.779]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[15.804,-1.372],[1.206,-4.575],[0,0],[18.676,-9.234],[6.656,-4.451],[23.252,6.364],[0,0],[0.832,-1.206],[6.697,-14.392],[-3.327,-3.328],[0,0],[1.331,-20.755],[-0.499,-8.062],[17.095,-16.887],[0,0],[-0.582,-1.289],[-9.151,-12.977],[-4.575,1.248],[0,0],[-17.345,-11.563],[-7.196,-3.578],[-6.114,-23.293],[0,0],[-1.497,-0.124],[-15.804,1.373],[-1.207,4.575],[0,0],[-18.676,9.234],[-6.655,4.451],[-23.251,-6.364],[0,0],[-0.832,1.206],[-6.739,14.392],[3.327,3.328],[0,0],[-1.331,20.755],[0.499,8.061],[-17.095,16.887],[0,0],[0.582,1.289],[9.144,12.981],[4.576,-1.248],[0,0],[17.345,11.563],[7.202,3.563],[6.114,23.293],[0,0],[1.498,0.125]],"o":[[-1.497,0.125],[0,0],[-6.114,23.334],[-7.196,3.578],[-17.345,11.563],[0,0],[-4.534,-1.248],[-9.151,12.978],[-0.582,1.289],[0,0],[17.095,16.887],[-0.5,8.062],[1.331,20.755],[0,0],[-3.369,3.328],[6.738,14.392],[0.832,1.248],[0,0],[23.293,-6.364],[6.697,4.451],[18.593,9.234],[0,0],[1.206,4.533],[15.804,1.373],[1.498,-0.124],[0,0],[6.114,-23.335],[7.196,-3.578],[17.345,-11.563],[0,0],[4.534,1.206],[9.151,-13.019],[0.582,-1.289],[0,0],[-17.095,-16.887],[0.499,-8.062],[-1.331,-20.756],[0,0],[3.369,-3.328],[-6.715,-14.39],[-0.832,-1.248],[0,0],[-23.293,6.364],[-6.676,-4.47],[-18.592,-9.234],[0,0],[-1.207,-4.575],[-15.804,-1.372]],"v":[[-23.81,-269.32],[-29.508,-263.247],[-41.529,-217.285],[-83.04,-167.829],[-103.838,-155.766],[-167.436,-144.536],[-213.314,-157.139],[-221.425,-155.267],[-245.259,-114.089],[-242.805,-106.185],[-208.905,-72.66],[-186.86,-12.099],[-186.86,12.109],[-208.905,72.671],[-242.805,106.195],[-245.259,114.099],[-221.425,155.236],[-213.314,157.15],[-167.477,144.547],[-103.838,155.777],[-82.999,167.839],[-41.529,217.295],[-29.508,263.257],[-23.81,269.329],[23.691,269.329],[29.39,263.257],[41.411,217.295],[82.922,167.839],[103.719,155.777],[167.317,144.547],[213.196,157.15],[221.307,155.277],[245.141,114.099],[242.687,106.195],[208.787,72.671],[186.742,12.109],[186.742,-12.098],[208.787,-72.66],[242.687,-106.185],[245.141,-114.089],[221.307,-155.225],[213.196,-157.139],[167.359,-144.536],[103.719,-155.766],[82.88,-167.828],[41.411,-217.285],[29.39,-263.247],[23.691,-269.32]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[-9.816,0],[-9.697,-0.846],[-7.071,-26.828],[0,0],[-5.532,-2.745],[-8.902,-5.948],[-2.745,0.79],[0,0],[-17.802,-25.331],[-8.236,-17.72],[19.758,-19.508],[0,0],[-0.416,-6.114],[0.666,-10.69],[-2.079,-1.997],[0,0],[13.102,-28.076],[11.232,-15.979],[26.787,7.363],[0,0],[5.117,-3.411],[9.602,-4.741],[0.707,-2.745],[0,0],[30.904,-2.663],[19.401,1.705],[7.03,26.87],[0,0],[5.532,2.745],[8.891,5.969],[2.746,-0.79],[0,0],[17.802,25.332],[8.277,17.703],[-19.716,19.508],[0,0],[0.416,6.114],[-0.666,10.693],[2.08,1.997],[0,0],[-13.102,28.076],[-11.242,15.972],[-26.787,-7.363],[0,0],[-5.116,3.411],[-9.608,4.742],[-0.707,2.745],[0,0],[-30.905,2.704]],"o":[[9.733,0.014],[30.904,2.662],[0,0],[0.749,2.745],[9.609,4.742],[5.116,3.411],[0,0],[26.787,-7.321],[11.231,16.014],[13.102,28.076],[0,0],[-2.038,1.997],[0.666,10.69],[-0.416,6.114],[0,0],[19.758,19.508],[-8.252,17.703],[-17.802,25.373],[0,0],[-2.787,-0.79],[-8.902,5.952],[-5.532,2.745],[0,0],[-6.987,26.87],[-19.402,1.705],[-30.905,-2.663],[0,0],[-0.748,-2.745],[-9.611,-4.723],[-5.116,-3.411],[0,0],[-26.786,7.321],[-11.208,-16.008],[-13.102,-28.076],[0,0],[2.08,-1.997],[-0.666,-10.693],[0.416,-6.114],[0,0],[-19.716,-19.508],[8.241,-17.709],[17.802,-25.373],[0,0],[2.787,0.79],[8.901,-5.948],[5.532,-2.745],[0,0],[6.988,-26.87],[9.608,-0.832]],"v":[[-0.059,-332.751],[29.099,-331.462],[89.785,-279.095],[101.764,-233.049],[110.582,-223.733],[138.368,-207.676],[150.804,-204.723],[196.683,-217.327],[272.385,-191.123],[301.668,-140.459],[286.527,-61.805],[252.669,-28.28],[249.009,-16.051],[249.009,16.061],[252.669,28.289],[286.527,61.815],[301.668,140.47],[272.385,191.09],[196.683,217.295],[150.846,204.734],[138.367,207.687],[110.582,223.743],[101.806,233.059],[89.743,279.063],[29.099,331.473],[-29.217,331.473],[-89.862,279.063],[-101.883,233.059],[-110.701,223.743],[-138.486,207.687],[-150.923,204.734],[-196.802,217.336],[-272.504,191.132],[-301.786,140.47],[-286.687,61.815],[-252.788,28.289],[-249.127,16.061],[-249.127,-16.051],[-252.788,-28.28],[-286.687,-61.805],[-301.786,-140.459],[-272.504,-191.08],[-196.801,-217.285],[-150.964,-204.723],[-138.486,-207.676],[-110.701,-223.733],[-101.924,-233.049],[-89.862,-279.053],[-29.217,-331.504]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898460657456,0.889815326765,0.896223060758,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.077,333.249],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":8,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"estrela Outlines 2 Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[450,450,0],"ix":2},"a":{"a":0,"k":[450,450,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[80,80,100]},{"t":60,"s":[100,100,100]}],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[89.47,0],[0,-89.47],[-89.47,0],[0,89.47]],"o":[[-89.47,0],[0,89.47],[89.47,0],[0,-89.47]],"v":[[446,286],[284,448],[446,610],[608,448]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"w":900,"h":900,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"estrela Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.84],"y":[-1.753]},"o":{"x":[0],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.361],"y":[1]},"o":{"x":[0],"y":[0]},"t":28,"s":[15]},{"t":60,"s":[180]}],"ix":10},"p":{"a":0,"k":[450,450,0],"ix":2},"a":{"a":0,"k":[308.5,333,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.011,34.469],[34.47,-0.012],[-0.011,-34.469],[-34.47,0.011]],"o":[[-0.012,-34.47],[-34.469,0.011],[0.012,34.47],[34.469,-0.012]],"v":[[62.333,0.005],[-0.101,-62.387],[-62.493,0.047],[-0.059,62.439]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-68.917],[68.916,0],[0,68.916],[-68.916,0]],"o":[[0,68.916],[-68.916,0],[0,-68.917],[68.916,0]],"v":[[124.725,0.005],[-0.059,124.789],[-124.843,0.005],[-0.059,-124.779]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[15.804,-1.372],[1.206,-4.575],[0,0],[18.676,-9.234],[6.656,-4.451],[23.252,6.364],[0,0],[0.832,-1.206],[6.697,-14.392],[-3.327,-3.328],[0,0],[1.331,-20.755],[-0.499,-8.062],[17.095,-16.887],[0,0],[-0.582,-1.289],[-9.151,-12.977],[-4.575,1.248],[0,0],[-17.345,-11.563],[-7.196,-3.578],[-6.114,-23.293],[0,0],[-1.497,-0.124],[-15.804,1.373],[-1.207,4.575],[0,0],[-18.676,9.234],[-6.655,4.451],[-23.251,-6.364],[0,0],[-0.832,1.206],[-6.739,14.392],[3.327,3.328],[0,0],[-1.331,20.755],[0.499,8.061],[-17.095,16.887],[0,0],[0.582,1.289],[9.144,12.981],[4.576,-1.248],[0,0],[17.345,11.563],[7.202,3.563],[6.114,23.293],[0,0],[1.498,0.125]],"o":[[-1.497,0.125],[0,0],[-6.114,23.334],[-7.196,3.578],[-17.345,11.563],[0,0],[-4.534,-1.248],[-9.151,12.978],[-0.582,1.289],[0,0],[17.095,16.887],[-0.5,8.062],[1.331,20.755],[0,0],[-3.369,3.328],[6.738,14.392],[0.832,1.248],[0,0],[23.293,-6.364],[6.697,4.451],[18.593,9.234],[0,0],[1.206,4.533],[15.804,1.373],[1.498,-0.124],[0,0],[6.114,-23.335],[7.196,-3.578],[17.345,-11.563],[0,0],[4.534,1.206],[9.151,-13.019],[0.582,-1.289],[0,0],[-17.095,-16.887],[0.499,-8.062],[-1.331,-20.756],[0,0],[3.369,-3.328],[-6.715,-14.39],[-0.832,-1.248],[0,0],[-23.293,6.364],[-6.676,-4.47],[-18.592,-9.234],[0,0],[-1.207,-4.575],[-15.804,-1.372]],"v":[[-23.81,-269.32],[-29.508,-263.247],[-41.529,-217.285],[-83.04,-167.829],[-103.838,-155.766],[-167.436,-144.536],[-213.314,-157.139],[-221.425,-155.267],[-245.259,-114.089],[-242.805,-106.185],[-208.905,-72.66],[-186.86,-12.099],[-186.86,12.109],[-208.905,72.671],[-242.805,106.195],[-245.259,114.099],[-221.425,155.236],[-213.314,157.15],[-167.477,144.547],[-103.838,155.777],[-82.999,167.839],[-41.529,217.295],[-29.508,263.257],[-23.81,269.329],[23.691,269.329],[29.39,263.257],[41.411,217.295],[82.922,167.839],[103.719,155.777],[167.317,144.547],[213.196,157.15],[221.307,155.277],[245.141,114.099],[242.687,106.195],[208.787,72.671],[186.742,12.109],[186.742,-12.098],[208.787,-72.66],[242.687,-106.185],[245.141,-114.089],[221.307,-155.225],[213.196,-157.139],[167.359,-144.536],[103.719,-155.766],[82.88,-167.828],[41.411,-217.285],[29.39,-263.247],[23.691,-269.32]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[-9.816,0],[-9.697,-0.846],[-7.071,-26.828],[0,0],[-5.532,-2.745],[-8.902,-5.948],[-2.745,0.79],[0,0],[-17.802,-25.331],[-8.236,-17.72],[19.758,-19.508],[0,0],[-0.416,-6.114],[0.666,-10.69],[-2.079,-1.997],[0,0],[13.102,-28.076],[11.232,-15.979],[26.787,7.363],[0,0],[5.117,-3.411],[9.602,-4.741],[0.707,-2.745],[0,0],[30.904,-2.663],[19.401,1.705],[7.03,26.87],[0,0],[5.532,2.745],[8.891,5.969],[2.746,-0.79],[0,0],[17.802,25.332],[8.277,17.703],[-19.716,19.508],[0,0],[0.416,6.114],[-0.666,10.693],[2.08,1.997],[0,0],[-13.102,28.076],[-11.242,15.972],[-26.787,-7.363],[0,0],[-5.116,3.411],[-9.608,4.742],[-0.707,2.745],[0,0],[-30.905,2.704]],"o":[[9.733,0.014],[30.904,2.662],[0,0],[0.749,2.745],[9.609,4.742],[5.116,3.411],[0,0],[26.787,-7.321],[11.231,16.014],[13.102,28.076],[0,0],[-2.038,1.997],[0.666,10.69],[-0.416,6.114],[0,0],[19.758,19.508],[-8.252,17.703],[-17.802,25.373],[0,0],[-2.787,-0.79],[-8.902,5.952],[-5.532,2.745],[0,0],[-6.987,26.87],[-19.402,1.705],[-30.905,-2.663],[0,0],[-0.748,-2.745],[-9.611,-4.723],[-5.116,-3.411],[0,0],[-26.786,7.321],[-11.208,-16.008],[-13.102,-28.076],[0,0],[2.08,-1.997],[-0.666,-10.693],[0.416,-6.114],[0,0],[-19.716,-19.508],[8.241,-17.709],[17.802,-25.373],[0,0],[2.787,0.79],[8.901,-5.948],[5.532,-2.745],[0,0],[6.988,-26.87],[9.608,-0.832]],"v":[[-0.059,-332.751],[29.099,-331.462],[89.785,-279.095],[101.764,-233.049],[110.582,-223.733],[138.368,-207.676],[150.804,-204.723],[196.683,-217.327],[272.385,-191.123],[301.668,-140.459],[286.527,-61.805],[252.669,-28.28],[249.009,-16.051],[249.009,16.061],[252.669,28.289],[286.527,61.815],[301.668,140.47],[272.385,191.09],[196.683,217.295],[150.846,204.734],[138.367,207.687],[110.582,223.743],[101.806,233.059],[89.743,279.063],[29.099,331.473],[-29.217,331.473],[-89.862,279.063],[-101.883,233.059],[-110.701,223.743],[-138.486,207.687],[-150.923,204.734],[-196.802,217.336],[-272.504,191.132],[-301.786,140.47],[-286.687,61.815],[-252.788,28.289],[-249.127,16.061],[-249.127,-16.051],[-252.788,-28.28],[-286.687,-61.805],[-301.786,-140.459],[-272.504,-191.08],[-196.801,-217.285],[-150.964,-204.723],[-138.486,-207.676],[-110.701,-223.733],[-101.924,-233.049],[-89.862,-279.053],[-29.217,-331.504]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898460657456,0.889815326765,0.896223060758,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.077,333.249],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":8,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{ + "v": "4.8.0", + "meta": { "g": "LottieFiles AE 3.5.6", "a": "", "k": "", "d": "", "tc": "" }, + "fr": 60, + "ip": 0, + "op": 120, + "w": 900, + "h": 900, + "nm": "Pre-comp 1", + "ddd": 0, + "assets": [ + { + "id": "comp_0", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "estrela Outlines 2", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.933], "y": [0] }, + "t": 0, + "s": [0] + }, + { "t": 60, "s": [180] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [450, 450, 0], "ix": 2 }, + "a": { "a": 0, "k": [308.5, 333, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0.011, 34.469], + [34.47, -0.012], + [-0.011, -34.469], + [-34.47, 0.011] + ], + "o": [ + [-0.012, -34.47], + [-34.469, 0.011], + [0.012, 34.47], + [34.469, -0.012] + ], + "v": [ + [62.333, 0.005], + [-0.101, -62.387], + [-62.493, 0.047], + [-0.059, 62.439] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -68.917], + [68.916, 0], + [0, 68.916], + [-68.916, 0] + ], + "o": [ + [0, 68.916], + [-68.916, 0], + [0, -68.917], + [68.916, 0] + ], + "v": [ + [124.725, 0.005], + [-0.059, 124.789], + [-124.843, 0.005], + [-0.059, -124.779] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [15.804, -1.372], + [1.206, -4.575], + [0, 0], + [18.676, -9.234], + [6.656, -4.451], + [23.252, 6.364], + [0, 0], + [0.832, -1.206], + [6.697, -14.392], + [-3.327, -3.328], + [0, 0], + [1.331, -20.755], + [-0.499, -8.062], + [17.095, -16.887], + [0, 0], + [-0.582, -1.289], + [-9.151, -12.977], + [-4.575, 1.248], + [0, 0], + [-17.345, -11.563], + [-7.196, -3.578], + [-6.114, -23.293], + [0, 0], + [-1.497, -0.124], + [-15.804, 1.373], + [-1.207, 4.575], + [0, 0], + [-18.676, 9.234], + [-6.655, 4.451], + [-23.251, -6.364], + [0, 0], + [-0.832, 1.206], + [-6.739, 14.392], + [3.327, 3.328], + [0, 0], + [-1.331, 20.755], + [0.499, 8.061], + [-17.095, 16.887], + [0, 0], + [0.582, 1.289], + [9.144, 12.981], + [4.576, -1.248], + [0, 0], + [17.345, 11.563], + [7.202, 3.563], + [6.114, 23.293], + [0, 0], + [1.498, 0.125] + ], + "o": [ + [-1.497, 0.125], + [0, 0], + [-6.114, 23.334], + [-7.196, 3.578], + [-17.345, 11.563], + [0, 0], + [-4.534, -1.248], + [-9.151, 12.978], + [-0.582, 1.289], + [0, 0], + [17.095, 16.887], + [-0.5, 8.062], + [1.331, 20.755], + [0, 0], + [-3.369, 3.328], + [6.738, 14.392], + [0.832, 1.248], + [0, 0], + [23.293, -6.364], + [6.697, 4.451], + [18.593, 9.234], + [0, 0], + [1.206, 4.533], + [15.804, 1.373], + [1.498, -0.124], + [0, 0], + [6.114, -23.335], + [7.196, -3.578], + [17.345, -11.563], + [0, 0], + [4.534, 1.206], + [9.151, -13.019], + [0.582, -1.289], + [0, 0], + [-17.095, -16.887], + [0.499, -8.062], + [-1.331, -20.756], + [0, 0], + [3.369, -3.328], + [-6.715, -14.39], + [-0.832, -1.248], + [0, 0], + [-23.293, 6.364], + [-6.676, -4.47], + [-18.592, -9.234], + [0, 0], + [-1.207, -4.575], + [-15.804, -1.372] + ], + "v": [ + [-23.81, -269.32], + [-29.508, -263.247], + [-41.529, -217.285], + [-83.04, -167.829], + [-103.838, -155.766], + [-167.436, -144.536], + [-213.314, -157.139], + [-221.425, -155.267], + [-245.259, -114.089], + [-242.805, -106.185], + [-208.905, -72.66], + [-186.86, -12.099], + [-186.86, 12.109], + [-208.905, 72.671], + [-242.805, 106.195], + [-245.259, 114.099], + [-221.425, 155.236], + [-213.314, 157.15], + [-167.477, 144.547], + [-103.838, 155.777], + [-82.999, 167.839], + [-41.529, 217.295], + [-29.508, 263.257], + [-23.81, 269.329], + [23.691, 269.329], + [29.39, 263.257], + [41.411, 217.295], + [82.922, 167.839], + [103.719, 155.777], + [167.317, 144.547], + [213.196, 157.15], + [221.307, 155.277], + [245.141, 114.099], + [242.687, 106.195], + [208.787, 72.671], + [186.742, 12.109], + [186.742, -12.098], + [208.787, -72.66], + [242.687, -106.185], + [245.141, -114.089], + [221.307, -155.225], + [213.196, -157.139], + [167.359, -144.536], + [103.719, -155.766], + [82.88, -167.828], + [41.411, -217.285], + [29.39, -263.247], + [23.691, -269.32] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [-9.816, 0], + [-9.697, -0.846], + [-7.071, -26.828], + [0, 0], + [-5.532, -2.745], + [-8.902, -5.948], + [-2.745, 0.79], + [0, 0], + [-17.802, -25.331], + [-8.236, -17.72], + [19.758, -19.508], + [0, 0], + [-0.416, -6.114], + [0.666, -10.69], + [-2.079, -1.997], + [0, 0], + [13.102, -28.076], + [11.232, -15.979], + [26.787, 7.363], + [0, 0], + [5.117, -3.411], + [9.602, -4.741], + [0.707, -2.745], + [0, 0], + [30.904, -2.663], + [19.401, 1.705], + [7.03, 26.87], + [0, 0], + [5.532, 2.745], + [8.891, 5.969], + [2.746, -0.79], + [0, 0], + [17.802, 25.332], + [8.277, 17.703], + [-19.716, 19.508], + [0, 0], + [0.416, 6.114], + [-0.666, 10.693], + [2.08, 1.997], + [0, 0], + [-13.102, 28.076], + [-11.242, 15.972], + [-26.787, -7.363], + [0, 0], + [-5.116, 3.411], + [-9.608, 4.742], + [-0.707, 2.745], + [0, 0], + [-30.905, 2.704] + ], + "o": [ + [9.733, 0.014], + [30.904, 2.662], + [0, 0], + [0.749, 2.745], + [9.609, 4.742], + [5.116, 3.411], + [0, 0], + [26.787, -7.321], + [11.231, 16.014], + [13.102, 28.076], + [0, 0], + [-2.038, 1.997], + [0.666, 10.69], + [-0.416, 6.114], + [0, 0], + [19.758, 19.508], + [-8.252, 17.703], + [-17.802, 25.373], + [0, 0], + [-2.787, -0.79], + [-8.902, 5.952], + [-5.532, 2.745], + [0, 0], + [-6.987, 26.87], + [-19.402, 1.705], + [-30.905, -2.663], + [0, 0], + [-0.748, -2.745], + [-9.611, -4.723], + [-5.116, -3.411], + [0, 0], + [-26.786, 7.321], + [-11.208, -16.008], + [-13.102, -28.076], + [0, 0], + [2.08, -1.997], + [-0.666, -10.693], + [0.416, -6.114], + [0, 0], + [-19.716, -19.508], + [8.241, -17.709], + [17.802, -25.373], + [0, 0], + [2.787, 0.79], + [8.901, -5.948], + [5.532, -2.745], + [0, 0], + [6.988, -26.87], + [9.608, -0.832] + ], + "v": [ + [-0.059, -332.751], + [29.099, -331.462], + [89.785, -279.095], + [101.764, -233.049], + [110.582, -223.733], + [138.368, -207.676], + [150.804, -204.723], + [196.683, -217.327], + [272.385, -191.123], + [301.668, -140.459], + [286.527, -61.805], + [252.669, -28.28], + [249.009, -16.051], + [249.009, 16.061], + [252.669, 28.289], + [286.527, 61.815], + [301.668, 140.47], + [272.385, 191.09], + [196.683, 217.295], + [150.846, 204.734], + [138.367, 207.687], + [110.582, 223.743], + [101.806, 233.059], + [89.743, 279.063], + [29.099, 331.473], + [-29.217, 331.473], + [-89.862, 279.063], + [-101.883, 233.059], + [-110.701, 223.743], + [-138.486, 207.687], + [-150.923, 204.734], + [-196.802, 217.336], + [-272.504, 191.132], + [-301.786, 140.47], + [-286.687, 61.815], + [-252.788, 28.289], + [-249.127, 16.061], + [-249.127, -16.051], + [-252.788, -28.28], + [-286.687, -61.805], + [-301.786, -140.459], + [-272.504, -191.08], + [-196.801, -217.285], + [-150.964, -204.723], + [-138.486, -207.676], + [-110.701, -223.733], + [-101.924, -233.049], + [-89.862, -279.053], + [-29.217, -331.504] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "mm", + "mm": 1, + "nm": "Merge Paths 1", + "mn": "ADBE Vector Filter - Merge", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [0.898460657456, 0.889815326765, 0.896223060758, 1], + "ix": 4 + }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [308.077, 333.249], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 8, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ] + } + ], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 0, + "nm": "estrela Outlines 2 Comp 1", + "refId": "comp_0", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [450, 450, 0], "ix": 2 }, + "a": { "a": 0, "k": [450, 450, 0], "ix": 1 }, + "s": { + "a": 1, + "k": [ + { + "i": { "x": [0.667, 0.667, 0.667], "y": [1, 1, 1] }, + "o": { "x": [1, 1, 0.333], "y": [0, 0, 0] }, + "t": 0, + "s": [100, 100, 100] + }, + { + "i": { "x": [0, 0, 0.667], "y": [1, 1, 1] }, + "o": { "x": [0.333, 0.333, 0.333], "y": [0, 0, 0] }, + "t": 30, + "s": [80, 80, 100] + }, + { "t": 60, "s": [100, 100, 100] } + ], + "ix": 6 + } + }, + "ao": 0, + "hasMask": true, + "masksProperties": [ + { + "inv": false, + "mode": "a", + "pt": { + "a": 0, + "k": { + "i": [ + [89.47, 0], + [0, -89.47], + [-89.47, 0], + [0, 89.47] + ], + "o": [ + [-89.47, 0], + [0, 89.47], + [89.47, 0], + [0, -89.47] + ], + "v": [ + [446, 286], + [284, 448], + [446, 610], + [608, 448] + ], + "c": true + }, + "ix": 1 + }, + "o": { "a": 0, "k": 100, "ix": 3 }, + "x": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Mask 1" + } + ], + "w": 900, + "h": 900, + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "estrela Outlines", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.84], "y": [-1.753] }, + "o": { "x": [0], "y": [0] }, + "t": 0, + "s": [0] + }, + { + "i": { "x": [0.361], "y": [1] }, + "o": { "x": [0], "y": [0] }, + "t": 28, + "s": [15] + }, + { "t": 60, "s": [180] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [450, 450, 0], "ix": 2 }, + "a": { "a": 0, "k": [308.5, 333, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0.011, 34.469], + [34.47, -0.012], + [-0.011, -34.469], + [-34.47, 0.011] + ], + "o": [ + [-0.012, -34.47], + [-34.469, 0.011], + [0.012, 34.47], + [34.469, -0.012] + ], + "v": [ + [62.333, 0.005], + [-0.101, -62.387], + [-62.493, 0.047], + [-0.059, 62.439] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -68.917], + [68.916, 0], + [0, 68.916], + [-68.916, 0] + ], + "o": [ + [0, 68.916], + [-68.916, 0], + [0, -68.917], + [68.916, 0] + ], + "v": [ + [124.725, 0.005], + [-0.059, 124.789], + [-124.843, 0.005], + [-0.059, -124.779] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [15.804, -1.372], + [1.206, -4.575], + [0, 0], + [18.676, -9.234], + [6.656, -4.451], + [23.252, 6.364], + [0, 0], + [0.832, -1.206], + [6.697, -14.392], + [-3.327, -3.328], + [0, 0], + [1.331, -20.755], + [-0.499, -8.062], + [17.095, -16.887], + [0, 0], + [-0.582, -1.289], + [-9.151, -12.977], + [-4.575, 1.248], + [0, 0], + [-17.345, -11.563], + [-7.196, -3.578], + [-6.114, -23.293], + [0, 0], + [-1.497, -0.124], + [-15.804, 1.373], + [-1.207, 4.575], + [0, 0], + [-18.676, 9.234], + [-6.655, 4.451], + [-23.251, -6.364], + [0, 0], + [-0.832, 1.206], + [-6.739, 14.392], + [3.327, 3.328], + [0, 0], + [-1.331, 20.755], + [0.499, 8.061], + [-17.095, 16.887], + [0, 0], + [0.582, 1.289], + [9.144, 12.981], + [4.576, -1.248], + [0, 0], + [17.345, 11.563], + [7.202, 3.563], + [6.114, 23.293], + [0, 0], + [1.498, 0.125] + ], + "o": [ + [-1.497, 0.125], + [0, 0], + [-6.114, 23.334], + [-7.196, 3.578], + [-17.345, 11.563], + [0, 0], + [-4.534, -1.248], + [-9.151, 12.978], + [-0.582, 1.289], + [0, 0], + [17.095, 16.887], + [-0.5, 8.062], + [1.331, 20.755], + [0, 0], + [-3.369, 3.328], + [6.738, 14.392], + [0.832, 1.248], + [0, 0], + [23.293, -6.364], + [6.697, 4.451], + [18.593, 9.234], + [0, 0], + [1.206, 4.533], + [15.804, 1.373], + [1.498, -0.124], + [0, 0], + [6.114, -23.335], + [7.196, -3.578], + [17.345, -11.563], + [0, 0], + [4.534, 1.206], + [9.151, -13.019], + [0.582, -1.289], + [0, 0], + [-17.095, -16.887], + [0.499, -8.062], + [-1.331, -20.756], + [0, 0], + [3.369, -3.328], + [-6.715, -14.39], + [-0.832, -1.248], + [0, 0], + [-23.293, 6.364], + [-6.676, -4.47], + [-18.592, -9.234], + [0, 0], + [-1.207, -4.575], + [-15.804, -1.372] + ], + "v": [ + [-23.81, -269.32], + [-29.508, -263.247], + [-41.529, -217.285], + [-83.04, -167.829], + [-103.838, -155.766], + [-167.436, -144.536], + [-213.314, -157.139], + [-221.425, -155.267], + [-245.259, -114.089], + [-242.805, -106.185], + [-208.905, -72.66], + [-186.86, -12.099], + [-186.86, 12.109], + [-208.905, 72.671], + [-242.805, 106.195], + [-245.259, 114.099], + [-221.425, 155.236], + [-213.314, 157.15], + [-167.477, 144.547], + [-103.838, 155.777], + [-82.999, 167.839], + [-41.529, 217.295], + [-29.508, 263.257], + [-23.81, 269.329], + [23.691, 269.329], + [29.39, 263.257], + [41.411, 217.295], + [82.922, 167.839], + [103.719, 155.777], + [167.317, 144.547], + [213.196, 157.15], + [221.307, 155.277], + [245.141, 114.099], + [242.687, 106.195], + [208.787, 72.671], + [186.742, 12.109], + [186.742, -12.098], + [208.787, -72.66], + [242.687, -106.185], + [245.141, -114.089], + [221.307, -155.225], + [213.196, -157.139], + [167.359, -144.536], + [103.719, -155.766], + [82.88, -167.828], + [41.411, -217.285], + [29.39, -263.247], + [23.691, -269.32] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [-9.816, 0], + [-9.697, -0.846], + [-7.071, -26.828], + [0, 0], + [-5.532, -2.745], + [-8.902, -5.948], + [-2.745, 0.79], + [0, 0], + [-17.802, -25.331], + [-8.236, -17.72], + [19.758, -19.508], + [0, 0], + [-0.416, -6.114], + [0.666, -10.69], + [-2.079, -1.997], + [0, 0], + [13.102, -28.076], + [11.232, -15.979], + [26.787, 7.363], + [0, 0], + [5.117, -3.411], + [9.602, -4.741], + [0.707, -2.745], + [0, 0], + [30.904, -2.663], + [19.401, 1.705], + [7.03, 26.87], + [0, 0], + [5.532, 2.745], + [8.891, 5.969], + [2.746, -0.79], + [0, 0], + [17.802, 25.332], + [8.277, 17.703], + [-19.716, 19.508], + [0, 0], + [0.416, 6.114], + [-0.666, 10.693], + [2.08, 1.997], + [0, 0], + [-13.102, 28.076], + [-11.242, 15.972], + [-26.787, -7.363], + [0, 0], + [-5.116, 3.411], + [-9.608, 4.742], + [-0.707, 2.745], + [0, 0], + [-30.905, 2.704] + ], + "o": [ + [9.733, 0.014], + [30.904, 2.662], + [0, 0], + [0.749, 2.745], + [9.609, 4.742], + [5.116, 3.411], + [0, 0], + [26.787, -7.321], + [11.231, 16.014], + [13.102, 28.076], + [0, 0], + [-2.038, 1.997], + [0.666, 10.69], + [-0.416, 6.114], + [0, 0], + [19.758, 19.508], + [-8.252, 17.703], + [-17.802, 25.373], + [0, 0], + [-2.787, -0.79], + [-8.902, 5.952], + [-5.532, 2.745], + [0, 0], + [-6.987, 26.87], + [-19.402, 1.705], + [-30.905, -2.663], + [0, 0], + [-0.748, -2.745], + [-9.611, -4.723], + [-5.116, -3.411], + [0, 0], + [-26.786, 7.321], + [-11.208, -16.008], + [-13.102, -28.076], + [0, 0], + [2.08, -1.997], + [-0.666, -10.693], + [0.416, -6.114], + [0, 0], + [-19.716, -19.508], + [8.241, -17.709], + [17.802, -25.373], + [0, 0], + [2.787, 0.79], + [8.901, -5.948], + [5.532, -2.745], + [0, 0], + [6.988, -26.87], + [9.608, -0.832] + ], + "v": [ + [-0.059, -332.751], + [29.099, -331.462], + [89.785, -279.095], + [101.764, -233.049], + [110.582, -223.733], + [138.368, -207.676], + [150.804, -204.723], + [196.683, -217.327], + [272.385, -191.123], + [301.668, -140.459], + [286.527, -61.805], + [252.669, -28.28], + [249.009, -16.051], + [249.009, 16.061], + [252.669, 28.289], + [286.527, 61.815], + [301.668, 140.47], + [272.385, 191.09], + [196.683, 217.295], + [150.846, 204.734], + [138.367, 207.687], + [110.582, 223.743], + [101.806, 233.059], + [89.743, 279.063], + [29.099, 331.473], + [-29.217, 331.473], + [-89.862, 279.063], + [-101.883, 233.059], + [-110.701, 223.743], + [-138.486, 207.687], + [-150.923, 204.734], + [-196.802, 217.336], + [-272.504, 191.132], + [-301.786, 140.47], + [-286.687, 61.815], + [-252.788, 28.289], + [-249.127, 16.061], + [-249.127, -16.051], + [-252.788, -28.28], + [-286.687, -61.805], + [-301.786, -140.459], + [-272.504, -191.08], + [-196.801, -217.285], + [-150.964, -204.723], + [-138.486, -207.676], + [-110.701, -223.733], + [-101.924, -233.049], + [-89.862, -279.053], + [-29.217, -331.504] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "mm", + "mm": 1, + "nm": "Merge Paths 1", + "mn": "ADBE Vector Filter - Merge", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [0.898460657456, 0.889815326765, 0.896223060758, 1], + "ix": 4 + }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [308.077, 333.249], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 8, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/src/renderer/declaration.d.ts b/src/renderer/declaration.d.ts index cceed299f..393717bad 100644 --- a/src/renderer/declaration.d.ts +++ b/src/renderer/declaration.d.ts @@ -22,7 +22,8 @@ declare global { repackId: number, objectID: string, title: string, - shop: GameShop + shop: GameShop, + downloadPath: string ) => Promise; cancelGameDownload: (gameId: number) => Promise; pauseGameDownload: (gameId: number) => Promise; diff --git a/src/renderer/hooks/use-download.ts b/src/renderer/hooks/use-download.ts index 522dd2c2d..27f90e8d7 100644 --- a/src/renderer/hooks/use-download.ts +++ b/src/renderer/hooks/use-download.ts @@ -28,10 +28,11 @@ export function useDownload() { repackId: number, objectID: string, title: string, - shop: GameShop + shop: GameShop, + downloadPath: string ) => window.electron - .startGameDownload(repackId, objectID, title, shop) + .startGameDownload(repackId, objectID, title, shop, downloadPath) .then((game) => { dispatch(clearDownload()); updateLibrary(); diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index 12d9d9406..88e91ba2e 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -138,12 +138,16 @@ export function GameDetails() { }; }, [game?.id, isGamePlaying, getGame]); - const handleStartDownload = async (repackId: number) => { + const handleStartDownload = async ( + repackId: number, + downloadPath: string + ) => { return startDownload( repackId, gameDetails.objectID, gameDetails.name, - shop as GameShop + shop as GameShop, + downloadPath ).then(() => { getGame(); setShowRepacksModal(false); diff --git a/src/renderer/pages/game-details/repacks-modal.tsx b/src/renderer/pages/game-details/repacks-modal.tsx index b3917b23d..bb47060ef 100644 --- a/src/renderer/pages/game-details/repacks-modal.tsx +++ b/src/renderer/pages/game-details/repacks-modal.tsx @@ -10,11 +10,12 @@ import type { DiskSpace } from "check-disk-space"; import { format } from "date-fns"; import { SPACING_UNIT } from "@renderer/theme.css"; import { formatBytes } from "@renderer/utils"; +import { SelectFolderModal } from "./select-folder-modal"; export interface RepacksModalProps { visible: boolean; gameDetails: ShopDetails; - startDownload: (repackId: number) => Promise; + startDownload: (repackId: number, downloadPath: string) => Promise; onClose: () => void; } @@ -24,9 +25,10 @@ export function RepacksModal({ startDownload, onClose, }: RepacksModalProps) { - const [downloadStarting, setDownloadStarting] = useState(false); + const [openSelectFolderModal, setOpenSelectFolderModal] = useState(false); const [diskFreeSpace, setDiskFreeSpace] = useState(null); const [filteredRepacks, setFilteredRepacks] = useState([]); + const [repack, setRepack] = useState(null); const { t } = useTranslation("game_details"); @@ -45,10 +47,8 @@ export function RepacksModal({ }, [visible]); const handleRepackClick = (repack: GameRepack) => { - setDownloadStarting(true); - startDownload(repack.id).finally(() => { - setDownloadStarting(false); - }); + setRepack(repack); + setOpenSelectFolderModal(true); }; const handleFilter: React.ChangeEventHandler = (event) => { @@ -70,6 +70,13 @@ export function RepacksModal({ })} onClose={onClose} > + setOpenSelectFolderModal(false)} + gameDetails={gameDetails} + startDownload={startDownload} + repack={repack} + />
@@ -80,7 +87,6 @@ export function RepacksModal({ key={repack.id} theme="dark" onClick={() => handleRepackClick(repack)} - disabled={downloadStarting} className={styles.repackButton} >

{repack.title}

diff --git a/src/renderer/pages/game-details/select-folder-modal.css.tsx b/src/renderer/pages/game-details/select-folder-modal.css.tsx new file mode 100644 index 000000000..7d32bccc6 --- /dev/null +++ b/src/renderer/pages/game-details/select-folder-modal.css.tsx @@ -0,0 +1,19 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "@renderer/theme.css"; + +export const container = style({ + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 2}px`, + width: "100%", +}); + +export const downloadsPathField = style({ + display: "flex", + gap: `${SPACING_UNIT * 2}px`, +}); + +export const hintText = style({ + fontSize: 12, + color: vars.color.bodyText, +}); diff --git a/src/renderer/pages/game-details/select-folder-modal.tsx b/src/renderer/pages/game-details/select-folder-modal.tsx new file mode 100644 index 000000000..4ba06ecea --- /dev/null +++ b/src/renderer/pages/game-details/select-folder-modal.tsx @@ -0,0 +1,103 @@ +import { Button, Modal, TextField } from "@renderer/components"; +import { GameRepack, ShopDetails } from "@types"; +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import * as styles from "./select-folder-modal.css"; +import { Link } from "react-router-dom"; + +export interface SelectFolderModalProps { + visible: boolean; + gameDetails: ShopDetails; + onClose: () => void; + startDownload: (repackId: number, downloadPath: string) => Promise; + repack: GameRepack; +} + +export function SelectFolderModal({ + visible, + gameDetails, + onClose, + startDownload, + repack, +}: SelectFolderModalProps) { + const { t } = useTranslation("game_details"); + + const [selectedPath, setSelectedPath] = useState({ + downloadsPath: "", + }); + const [downloadStarting, setDownloadStarting] = useState(false); + + useEffect(() => { + Promise.all([ + window.electron.getDefaultDownloadsPath(), + window.electron.getUserPreferences(), + ]).then(([path, userPreferences]) => { + setSelectedPath({ + downloadsPath: userPreferences?.downloadsPath || path, + }); + }); + }, []); + + const handleChooseDownloadsPath = async () => { + const { filePaths } = await window.electron.showOpenDialog({ + defaultPath: selectedPath.downloadsPath, + properties: ["openDirectory"], + }); + + if (filePaths && filePaths.length > 0) { + const path = filePaths[0]; + setSelectedPath({ downloadsPath: path }); + } + }; + + const handleStartClick = () => { + setDownloadStarting(true); + startDownload(repack.id, selectedPath.downloadsPath).finally(() => { + setDownloadStarting(false); + }); + }; + + return ( + +
+
+ + + +
+

+ {t("select_folder_hint")}{" "} + + {t("hydra_settings")} + +

+ +
+
+ ); +} From 12fb53908b1b70d51f237969c3fbd03fcfc31d51 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sat, 20 Apr 2024 21:11:01 -0300 Subject: [PATCH 02/70] fix: select Folder Modal doesn't close --- src/renderer/pages/game-details/game-details.tsx | 4 ++++ src/renderer/pages/game-details/repacks-modal.tsx | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index 9c9178bfa..aae3f2675 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -51,6 +51,7 @@ export function GameDetails() { const { t, i18n } = useTranslation("game_details"); const [showRepacksModal, setShowRepacksModal] = useState(false); + const [showSelectFolderModal, setShowSelectFolderModal] = useState(false); const randomGameObjectID = useRef(null); @@ -153,6 +154,7 @@ export function GameDetails() { ).then(() => { getGame(); setShowRepacksModal(false); + setShowSelectFolderModal(false); }); }; @@ -177,6 +179,8 @@ export function GameDetails() { visible={showRepacksModal} gameDetails={gameDetails} startDownload={handleStartDownload} + showSelectFolderModal={showSelectFolderModal} + setShowSelectFolderModal={setShowSelectFolderModal} onClose={() => setShowRepacksModal(false)} /> )} diff --git a/src/renderer/pages/game-details/repacks-modal.tsx b/src/renderer/pages/game-details/repacks-modal.tsx index 3282ee3b0..7908c486b 100644 --- a/src/renderer/pages/game-details/repacks-modal.tsx +++ b/src/renderer/pages/game-details/repacks-modal.tsx @@ -16,6 +16,8 @@ import { SelectFolderModal } from "./select-folder-modal"; export interface RepacksModalProps { visible: boolean; gameDetails: ShopDetails; + showSelectFolderModal: boolean; + setShowSelectFolderModal: (value: boolean) => void; startDownload: (repackId: number, downloadPath: string) => Promise; onClose: () => void; } @@ -23,10 +25,11 @@ export interface RepacksModalProps { export function RepacksModal({ visible, gameDetails, + showSelectFolderModal, + setShowSelectFolderModal, startDownload, onClose, }: RepacksModalProps) { - const [openSelectFolderModal, setOpenSelectFolderModal] = useState(false); const [diskFreeSpace, setDiskFreeSpace] = useState(null); const [filteredRepacks, setFilteredRepacks] = useState([]); const [repack, setRepack] = useState(null); @@ -53,7 +56,7 @@ export function RepacksModal({ const handleRepackClick = (repack: GameRepack) => { setRepack(repack); - setOpenSelectFolderModal(true); + setShowSelectFolderModal(true); }; const handleFilter: React.ChangeEventHandler = (event) => { @@ -76,8 +79,8 @@ export function RepacksModal({ onClose={onClose} > setOpenSelectFolderModal(false)} + visible={showSelectFolderModal} + onClose={() => setShowSelectFolderModal(false)} gameDetails={gameDetails} startDownload={startDownload} repack={repack} From e860b8aeaa78c727e424b948cc4e5ab3c001946a Mon Sep 17 00:00:00 2001 From: Hydra Date: Sat, 20 Apr 2024 21:32:10 -0300 Subject: [PATCH 03/70] fix: game dont install in another path after the first installation --- src/main/events/torrenting/start-game-download.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index f661a956e..d6c243b56 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -37,7 +37,7 @@ const startGameDownload = async ( writePipe.write({ action: "pause" }); - const downloadsPath = game?.downloadPath ?? downloadPath; + const downloadsPath = downloadPath; await gameRepository.update( { From fca84f7c90dc893b7683713b4b03e37aa981099c Mon Sep 17 00:00:00 2001 From: Hydra Date: Sat, 20 Apr 2024 23:02:30 -0300 Subject: [PATCH 04/70] refactor: changed selectedPath from an object to a string --- .../pages/game-details/select-folder-modal.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/renderer/pages/game-details/select-folder-modal.tsx b/src/renderer/pages/game-details/select-folder-modal.tsx index 4ba06ecea..111c22dc0 100644 --- a/src/renderer/pages/game-details/select-folder-modal.tsx +++ b/src/renderer/pages/game-details/select-folder-modal.tsx @@ -23,9 +23,7 @@ export function SelectFolderModal({ }: SelectFolderModalProps) { const { t } = useTranslation("game_details"); - const [selectedPath, setSelectedPath] = useState({ - downloadsPath: "", - }); + const [selectedPath, setSelectedPath] = useState(null); const [downloadStarting, setDownloadStarting] = useState(false); useEffect(() => { @@ -33,9 +31,7 @@ export function SelectFolderModal({ window.electron.getDefaultDownloadsPath(), window.electron.getUserPreferences(), ]).then(([path, userPreferences]) => { - setSelectedPath({ - downloadsPath: userPreferences?.downloadsPath || path, - }); + setSelectedPath(userPreferences?.downloadsPath || path); }); }, []); @@ -47,7 +43,7 @@ export function SelectFolderModal({ if (filePaths && filePaths.length > 0) { const path = filePaths[0]; - setSelectedPath({ downloadsPath: path }); + setSelectedPath(path); } }; From 93ff4b8f7a8908d901445cfdef4f1cf54b3c6951 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sat, 20 Apr 2024 23:03:27 -0300 Subject: [PATCH 05/70] refactor: remove downloadPath constant to get directly from props --- src/main/events/torrenting/start-game-download.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index d6c243b56..d5143221a 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -37,8 +37,6 @@ const startGameDownload = async ( writePipe.write({ action: "pause" }); - const downloadsPath = downloadPath; - await gameRepository.update( { status: In([ @@ -57,7 +55,7 @@ const startGameDownload = async ( }, { status: GameStatus.DownloadingMetadata, - downloadPath: downloadsPath, + downloadPath: downloadPath, repack: { id: repackId }, } ); @@ -66,7 +64,7 @@ const startGameDownload = async ( action: "start", game_id: game.id, magnet: repack.magnet, - save_path: downloadsPath, + save_path: downloadPath, }); game.status = GameStatus.DownloadingMetadata; @@ -75,7 +73,7 @@ const startGameDownload = async ( action: "start", game_id: game.id, magnet: repack.magnet, - save_path: downloadsPath, + save_path: downloadPath, }); return game; @@ -88,7 +86,7 @@ const startGameDownload = async ( objectID, shop: gameShop, status: GameStatus.DownloadingMetadata, - downloadPath: downloadsPath, + downloadPath: downloadPath, repack: { id: repackId }, }); @@ -96,7 +94,7 @@ const startGameDownload = async ( action: "start", game_id: createdGame.id, magnet: repack.magnet, - save_path: downloadsPath, + save_path: downloadPath, }); const { repack: _, ...rest } = createdGame; From 5db7b97090f13389d5661d20f6d3e189b950d077 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sat, 20 Apr 2024 23:09:05 -0300 Subject: [PATCH 06/70] fix: cannot read properties of undefined in selectFolderModal --- src/renderer/pages/game-details/select-folder-modal.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/renderer/pages/game-details/select-folder-modal.tsx b/src/renderer/pages/game-details/select-folder-modal.tsx index 111c22dc0..4edfa6b1e 100644 --- a/src/renderer/pages/game-details/select-folder-modal.tsx +++ b/src/renderer/pages/game-details/select-folder-modal.tsx @@ -23,7 +23,7 @@ export function SelectFolderModal({ }: SelectFolderModalProps) { const { t } = useTranslation("game_details"); - const [selectedPath, setSelectedPath] = useState(null); + const [selectedPath, setSelectedPath] = useState(""); const [downloadStarting, setDownloadStarting] = useState(false); useEffect(() => { @@ -37,7 +37,7 @@ export function SelectFolderModal({ const handleChooseDownloadsPath = async () => { const { filePaths } = await window.electron.showOpenDialog({ - defaultPath: selectedPath.downloadsPath, + defaultPath: selectedPath, properties: ["openDirectory"], }); @@ -49,7 +49,7 @@ export function SelectFolderModal({ const handleStartClick = () => { setDownloadStarting(true); - startDownload(repack.id, selectedPath.downloadsPath).finally(() => { + startDownload(repack.id, selectedPath).finally(() => { setDownloadStarting(false); }); }; @@ -65,7 +65,7 @@ export function SelectFolderModal({
From 433e00bb500fb7110eebe8b41f56cfac468063ed Mon Sep 17 00:00:00 2001 From: Hydra Date: Sun, 21 Apr 2024 00:59:52 -0300 Subject: [PATCH 07/70] feat: change selectFolderModal to a section inside repacksModal --- .../events/hardware/get-disk-free-space.ts | 5 +- src/preload.ts | 2 +- src/renderer/declaration.d.ts | 2 +- .../pages/game-details/game-details.tsx | 4 - .../pages/game-details/repacks-modal.css.ts | 20 ++++ .../pages/game-details/repacks-modal.tsx | 108 +++++++++++++----- .../game-details/select-folder-modal.css.tsx | 19 --- .../game-details/select-folder-modal.tsx | 99 ---------------- 8 files changed, 101 insertions(+), 158 deletions(-) delete mode 100644 src/renderer/pages/game-details/select-folder-modal.css.tsx delete mode 100644 src/renderer/pages/game-details/select-folder-modal.tsx diff --git a/src/main/events/hardware/get-disk-free-space.ts b/src/main/events/hardware/get-disk-free-space.ts index 28dcafa20..427988d65 100644 --- a/src/main/events/hardware/get-disk-free-space.ts +++ b/src/main/events/hardware/get-disk-free-space.ts @@ -1,10 +1,9 @@ import checkDiskSpace from "check-disk-space"; import { registerEvent } from "../register-event"; -import { getDownloadsPath } from "../helpers/get-downloads-path"; -const getDiskFreeSpace = async (_event: Electron.IpcMainInvokeEvent) => - checkDiskSpace(await getDownloadsPath()); +const getDiskFreeSpace = async (_event: Electron.IpcMainInvokeEvent, path: string) => + checkDiskSpace(path); registerEvent(getDiskFreeSpace, { name: "getDiskFreeSpace", diff --git a/src/preload.ts b/src/preload.ts index ec36b67a5..b01ab5247 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -99,7 +99,7 @@ contextBridge.exposeInMainWorld("electron", { }, /* Hardware */ - getDiskFreeSpace: () => ipcRenderer.invoke("getDiskFreeSpace"), + getDiskFreeSpace: (path: string) => ipcRenderer.invoke("getDiskFreeSpace", path), /* Misc */ getOrCacheImage: (url: string) => ipcRenderer.invoke("getOrCacheImage", url), diff --git a/src/renderer/declaration.d.ts b/src/renderer/declaration.d.ts index f8d1018c3..3184940ac 100644 --- a/src/renderer/declaration.d.ts +++ b/src/renderer/declaration.d.ts @@ -76,7 +76,7 @@ declare global { ) => Promise; /* Hardware */ - getDiskFreeSpace: () => Promise; + getDiskFreeSpace: (path: string) => Promise; /* Misc */ getOrCacheImage: (url: string) => Promise; diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index aae3f2675..9c9178bfa 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -51,7 +51,6 @@ export function GameDetails() { const { t, i18n } = useTranslation("game_details"); const [showRepacksModal, setShowRepacksModal] = useState(false); - const [showSelectFolderModal, setShowSelectFolderModal] = useState(false); const randomGameObjectID = useRef(null); @@ -154,7 +153,6 @@ export function GameDetails() { ).then(() => { getGame(); setShowRepacksModal(false); - setShowSelectFolderModal(false); }); }; @@ -179,8 +177,6 @@ export function GameDetails() { visible={showRepacksModal} gameDetails={gameDetails} startDownload={handleStartDownload} - showSelectFolderModal={showSelectFolderModal} - setShowSelectFolderModal={setShowSelectFolderModal} onClose={() => setShowRepacksModal(false)} /> )} diff --git a/src/renderer/pages/game-details/repacks-modal.css.ts b/src/renderer/pages/game-details/repacks-modal.css.ts index c2568594e..4133d1a74 100644 --- a/src/renderer/pages/game-details/repacks-modal.css.ts +++ b/src/renderer/pages/game-details/repacks-modal.css.ts @@ -16,3 +16,23 @@ export const repackButton = style({ color: vars.color.bodyText, padding: `${SPACING_UNIT * 2}px`, }); + +export const container = style({ + width: "100%", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 2}px`, + marginBottom: SPACING_UNIT * 2, + paddingBottom: SPACING_UNIT * 2, + borderBottom: `solid 1px ${vars.color.borderColor}`, +}); + +export const downloadsPathField = style({ + display: "flex", + gap: `${SPACING_UNIT * 2}px`, +}); + +export const hintText = style({ + fontSize: 12, + color: vars.color.bodyText, +}); \ No newline at end of file diff --git a/src/renderer/pages/game-details/repacks-modal.tsx b/src/renderer/pages/game-details/repacks-modal.tsx index 7908c486b..33db267b7 100644 --- a/src/renderer/pages/game-details/repacks-modal.tsx +++ b/src/renderer/pages/game-details/repacks-modal.tsx @@ -6,18 +6,16 @@ import type { GameRepack, ShopDetails } from "@types"; import * as styles from "./repacks-modal.css"; -import type { DiskSpace } from "check-disk-space"; -import { format } from "date-fns"; +import { useAppSelector } from "@renderer/hooks"; import { SPACING_UNIT } from "@renderer/theme.css"; import { formatBytes } from "@renderer/utils"; -import { useAppSelector } from "@renderer/hooks"; -import { SelectFolderModal } from "./select-folder-modal"; +import type { DiskSpace } from "check-disk-space"; +import { format } from "date-fns"; +import { Link } from "react-router-dom"; export interface RepacksModalProps { visible: boolean; gameDetails: ShopDetails; - showSelectFolderModal: boolean; - setShowSelectFolderModal: (value: boolean) => void; startDownload: (repackId: number, downloadPath: string) => Promise; onClose: () => void; } @@ -25,18 +23,13 @@ export interface RepacksModalProps { export function RepacksModal({ visible, gameDetails, - showSelectFolderModal, - setShowSelectFolderModal, startDownload, onClose, }: RepacksModalProps) { const [diskFreeSpace, setDiskFreeSpace] = useState(null); const [filteredRepacks, setFilteredRepacks] = useState([]); - const [repack, setRepack] = useState(null); - - const repackersFriendlyNames = useAppSelector( - (state) => state.repackersFriendlyNames.value - ); + const [selectedPath, setSelectedPath] = useState(""); + const [downloadStarting, setDownloadStarting] = useState(false); const { t } = useTranslation("game_details"); @@ -44,20 +37,22 @@ export function RepacksModal({ setFilteredRepacks(gameDetails.repacks); }, [gameDetails.repacks]); - const getDiskFreeSpace = () => { - window.electron.getDiskFreeSpace().then((result) => { - setDiskFreeSpace(result); - }); - }; + useEffect(() => { + visible && getDiskFreeSpace(selectedPath); + }, [selectedPath, visible]); useEffect(() => { - getDiskFreeSpace(); - }, [visible]); + Promise.all([ + window.electron.getDefaultDownloadsPath(), + window.electron.getUserPreferences(), + ]).then(([path, userPreferences]) => { + setSelectedPath(userPreferences?.downloadsPath || path); + }); + }, []); - const handleRepackClick = (repack: GameRepack) => { - setRepack(repack); - setShowSelectFolderModal(true); - }; + const repackersFriendlyNames = useAppSelector( + (state) => state.repackersFriendlyNames.value + ); const handleFilter: React.ChangeEventHandler = (event) => { setFilteredRepacks( @@ -69,6 +64,32 @@ export function RepacksModal({ ); }; + const handleChooseDownloadsPath = async () => { + const { filePaths } = await window.electron.showOpenDialog({ + defaultPath: selectedPath, + properties: ["openDirectory"], + }); + + if (filePaths && filePaths.length > 0) { + const path = filePaths[0]; + setSelectedPath(path); + } + }; + + const handleRepackClick = (repack: GameRepack) => { + setDownloadStarting(true); + startDownload(repack.id, selectedPath).finally(() => { + setDownloadStarting(false); + }); + }; + + const getDiskFreeSpace = (path: string) => { + window.electron.getDiskFreeSpace(path).then((result) => { + setDiskFreeSpace(result); + }); + }; + + return ( - setShowSelectFolderModal(false)} - gameDetails={gameDetails} - startDownload={startDownload} - repack={repack} - /> +
+
+ + + +
+

+ {t("select_folder_hint")}{" "} + + {t("hydra_settings")} + +

+
@@ -96,6 +141,7 @@ export function RepacksModal({ theme="dark" onClick={() => handleRepackClick(repack)} className={styles.repackButton} + disabled={downloadStarting} >

{repack.title}

diff --git a/src/renderer/pages/game-details/select-folder-modal.css.tsx b/src/renderer/pages/game-details/select-folder-modal.css.tsx deleted file mode 100644 index 7d32bccc6..000000000 --- a/src/renderer/pages/game-details/select-folder-modal.css.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { style } from "@vanilla-extract/css"; -import { SPACING_UNIT, vars } from "@renderer/theme.css"; - -export const container = style({ - display: "flex", - flexDirection: "column", - gap: `${SPACING_UNIT * 2}px`, - width: "100%", -}); - -export const downloadsPathField = style({ - display: "flex", - gap: `${SPACING_UNIT * 2}px`, -}); - -export const hintText = style({ - fontSize: 12, - color: vars.color.bodyText, -}); diff --git a/src/renderer/pages/game-details/select-folder-modal.tsx b/src/renderer/pages/game-details/select-folder-modal.tsx deleted file mode 100644 index 4edfa6b1e..000000000 --- a/src/renderer/pages/game-details/select-folder-modal.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { Button, Modal, TextField } from "@renderer/components"; -import { GameRepack, ShopDetails } from "@types"; -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import * as styles from "./select-folder-modal.css"; -import { Link } from "react-router-dom"; - -export interface SelectFolderModalProps { - visible: boolean; - gameDetails: ShopDetails; - onClose: () => void; - startDownload: (repackId: number, downloadPath: string) => Promise; - repack: GameRepack; -} - -export function SelectFolderModal({ - visible, - gameDetails, - onClose, - startDownload, - repack, -}: SelectFolderModalProps) { - const { t } = useTranslation("game_details"); - - const [selectedPath, setSelectedPath] = useState(""); - const [downloadStarting, setDownloadStarting] = useState(false); - - useEffect(() => { - Promise.all([ - window.electron.getDefaultDownloadsPath(), - window.electron.getUserPreferences(), - ]).then(([path, userPreferences]) => { - setSelectedPath(userPreferences?.downloadsPath || path); - }); - }, []); - - const handleChooseDownloadsPath = async () => { - const { filePaths } = await window.electron.showOpenDialog({ - defaultPath: selectedPath, - properties: ["openDirectory"], - }); - - if (filePaths && filePaths.length > 0) { - const path = filePaths[0]; - setSelectedPath(path); - } - }; - - const handleStartClick = () => { - setDownloadStarting(true); - startDownload(repack.id, selectedPath).finally(() => { - setDownloadStarting(false); - }); - }; - - return ( - -

-
- - - -
-

- {t("select_folder_hint")}{" "} - - {t("hydra_settings")} - -

- -
-
- ); -} From 44583c46262d9689a35163d9b03963d35c9e9c70 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sun, 21 Apr 2024 01:02:17 -0300 Subject: [PATCH 08/70] prettier: format all files --- src/main/events/hardware/get-disk-free-space.ts | 6 ++++-- src/preload.ts | 3 ++- src/renderer/pages/game-details/repacks-modal.css.ts | 2 +- src/renderer/pages/game-details/repacks-modal.tsx | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/events/hardware/get-disk-free-space.ts b/src/main/events/hardware/get-disk-free-space.ts index 427988d65..0c87ed8bc 100644 --- a/src/main/events/hardware/get-disk-free-space.ts +++ b/src/main/events/hardware/get-disk-free-space.ts @@ -2,8 +2,10 @@ import checkDiskSpace from "check-disk-space"; import { registerEvent } from "../register-event"; -const getDiskFreeSpace = async (_event: Electron.IpcMainInvokeEvent, path: string) => - checkDiskSpace(path); +const getDiskFreeSpace = async ( + _event: Electron.IpcMainInvokeEvent, + path: string +) => checkDiskSpace(path); registerEvent(getDiskFreeSpace, { name: "getDiskFreeSpace", diff --git a/src/preload.ts b/src/preload.ts index b01ab5247..66fff80fa 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -99,7 +99,8 @@ contextBridge.exposeInMainWorld("electron", { }, /* Hardware */ - getDiskFreeSpace: (path: string) => ipcRenderer.invoke("getDiskFreeSpace", path), + getDiskFreeSpace: (path: string) => + ipcRenderer.invoke("getDiskFreeSpace", path), /* Misc */ getOrCacheImage: (url: string) => ipcRenderer.invoke("getOrCacheImage", url), diff --git a/src/renderer/pages/game-details/repacks-modal.css.ts b/src/renderer/pages/game-details/repacks-modal.css.ts index 4133d1a74..6a1d37f36 100644 --- a/src/renderer/pages/game-details/repacks-modal.css.ts +++ b/src/renderer/pages/game-details/repacks-modal.css.ts @@ -35,4 +35,4 @@ export const downloadsPathField = style({ export const hintText = style({ fontSize: 12, color: vars.color.bodyText, -}); \ No newline at end of file +}); diff --git a/src/renderer/pages/game-details/repacks-modal.tsx b/src/renderer/pages/game-details/repacks-modal.tsx index 33db267b7..c5830de79 100644 --- a/src/renderer/pages/game-details/repacks-modal.tsx +++ b/src/renderer/pages/game-details/repacks-modal.tsx @@ -82,14 +82,13 @@ export function RepacksModal({ setDownloadStarting(false); }); }; - + const getDiskFreeSpace = (path: string) => { window.electron.getDiskFreeSpace(path).then((result) => { setDiskFreeSpace(result); }); }; - return ( Date: Sun, 21 Apr 2024 22:55:00 -0300 Subject: [PATCH 09/70] Revert "feat: change selectFolderModal to a section inside repacksModal" This reverts commit 934fb1145e5ebf9ac47648dbbf5610eed1f2776f. --- src/renderer/declaration.d.ts | 2 +- .../pages/game-details/game-details.tsx | 4 + .../pages/game-details/repacks-modal.css.ts | 20 ---- .../pages/game-details/repacks-modal.tsx | 107 +++++------------- .../game-details/select-folder-modal.css.tsx | 19 ++++ .../game-details/select-folder-modal.tsx | 99 ++++++++++++++++ 6 files changed, 154 insertions(+), 97 deletions(-) create mode 100644 src/renderer/pages/game-details/select-folder-modal.css.tsx create mode 100644 src/renderer/pages/game-details/select-folder-modal.tsx diff --git a/src/renderer/declaration.d.ts b/src/renderer/declaration.d.ts index 3184940ac..f8d1018c3 100644 --- a/src/renderer/declaration.d.ts +++ b/src/renderer/declaration.d.ts @@ -76,7 +76,7 @@ declare global { ) => Promise; /* Hardware */ - getDiskFreeSpace: (path: string) => Promise; + getDiskFreeSpace: () => Promise; /* Misc */ getOrCacheImage: (url: string) => Promise; diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index 9c9178bfa..aae3f2675 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -51,6 +51,7 @@ export function GameDetails() { const { t, i18n } = useTranslation("game_details"); const [showRepacksModal, setShowRepacksModal] = useState(false); + const [showSelectFolderModal, setShowSelectFolderModal] = useState(false); const randomGameObjectID = useRef(null); @@ -153,6 +154,7 @@ export function GameDetails() { ).then(() => { getGame(); setShowRepacksModal(false); + setShowSelectFolderModal(false); }); }; @@ -177,6 +179,8 @@ export function GameDetails() { visible={showRepacksModal} gameDetails={gameDetails} startDownload={handleStartDownload} + showSelectFolderModal={showSelectFolderModal} + setShowSelectFolderModal={setShowSelectFolderModal} onClose={() => setShowRepacksModal(false)} /> )} diff --git a/src/renderer/pages/game-details/repacks-modal.css.ts b/src/renderer/pages/game-details/repacks-modal.css.ts index 6a1d37f36..c2568594e 100644 --- a/src/renderer/pages/game-details/repacks-modal.css.ts +++ b/src/renderer/pages/game-details/repacks-modal.css.ts @@ -16,23 +16,3 @@ export const repackButton = style({ color: vars.color.bodyText, padding: `${SPACING_UNIT * 2}px`, }); - -export const container = style({ - width: "100%", - display: "flex", - flexDirection: "column", - gap: `${SPACING_UNIT * 2}px`, - marginBottom: SPACING_UNIT * 2, - paddingBottom: SPACING_UNIT * 2, - borderBottom: `solid 1px ${vars.color.borderColor}`, -}); - -export const downloadsPathField = style({ - display: "flex", - gap: `${SPACING_UNIT * 2}px`, -}); - -export const hintText = style({ - fontSize: 12, - color: vars.color.bodyText, -}); diff --git a/src/renderer/pages/game-details/repacks-modal.tsx b/src/renderer/pages/game-details/repacks-modal.tsx index c5830de79..7908c486b 100644 --- a/src/renderer/pages/game-details/repacks-modal.tsx +++ b/src/renderer/pages/game-details/repacks-modal.tsx @@ -6,16 +6,18 @@ import type { GameRepack, ShopDetails } from "@types"; import * as styles from "./repacks-modal.css"; -import { useAppSelector } from "@renderer/hooks"; -import { SPACING_UNIT } from "@renderer/theme.css"; -import { formatBytes } from "@renderer/utils"; import type { DiskSpace } from "check-disk-space"; import { format } from "date-fns"; -import { Link } from "react-router-dom"; +import { SPACING_UNIT } from "@renderer/theme.css"; +import { formatBytes } from "@renderer/utils"; +import { useAppSelector } from "@renderer/hooks"; +import { SelectFolderModal } from "./select-folder-modal"; export interface RepacksModalProps { visible: boolean; gameDetails: ShopDetails; + showSelectFolderModal: boolean; + setShowSelectFolderModal: (value: boolean) => void; startDownload: (repackId: number, downloadPath: string) => Promise; onClose: () => void; } @@ -23,13 +25,18 @@ export interface RepacksModalProps { export function RepacksModal({ visible, gameDetails, + showSelectFolderModal, + setShowSelectFolderModal, startDownload, onClose, }: RepacksModalProps) { const [diskFreeSpace, setDiskFreeSpace] = useState(null); const [filteredRepacks, setFilteredRepacks] = useState([]); - const [selectedPath, setSelectedPath] = useState(""); - const [downloadStarting, setDownloadStarting] = useState(false); + const [repack, setRepack] = useState(null); + + const repackersFriendlyNames = useAppSelector( + (state) => state.repackersFriendlyNames.value + ); const { t } = useTranslation("game_details"); @@ -37,22 +44,20 @@ export function RepacksModal({ setFilteredRepacks(gameDetails.repacks); }, [gameDetails.repacks]); - useEffect(() => { - visible && getDiskFreeSpace(selectedPath); - }, [selectedPath, visible]); + const getDiskFreeSpace = () => { + window.electron.getDiskFreeSpace().then((result) => { + setDiskFreeSpace(result); + }); + }; useEffect(() => { - Promise.all([ - window.electron.getDefaultDownloadsPath(), - window.electron.getUserPreferences(), - ]).then(([path, userPreferences]) => { - setSelectedPath(userPreferences?.downloadsPath || path); - }); - }, []); + getDiskFreeSpace(); + }, [visible]); - const repackersFriendlyNames = useAppSelector( - (state) => state.repackersFriendlyNames.value - ); + const handleRepackClick = (repack: GameRepack) => { + setRepack(repack); + setShowSelectFolderModal(true); + }; const handleFilter: React.ChangeEventHandler = (event) => { setFilteredRepacks( @@ -64,31 +69,6 @@ export function RepacksModal({ ); }; - const handleChooseDownloadsPath = async () => { - const { filePaths } = await window.electron.showOpenDialog({ - defaultPath: selectedPath, - properties: ["openDirectory"], - }); - - if (filePaths && filePaths.length > 0) { - const path = filePaths[0]; - setSelectedPath(path); - } - }; - - const handleRepackClick = (repack: GameRepack) => { - setDownloadStarting(true); - startDownload(repack.id, selectedPath).finally(() => { - setDownloadStarting(false); - }); - }; - - const getDiskFreeSpace = (path: string) => { - window.electron.getDiskFreeSpace(path).then((result) => { - setDiskFreeSpace(result); - }); - }; - return ( -
-
- - - -
-

- {t("select_folder_hint")}{" "} - - {t("hydra_settings")} - -

-
+ setShowSelectFolderModal(false)} + gameDetails={gameDetails} + startDownload={startDownload} + repack={repack} + />
@@ -140,7 +96,6 @@ export function RepacksModal({ theme="dark" onClick={() => handleRepackClick(repack)} className={styles.repackButton} - disabled={downloadStarting} >

{repack.title}

diff --git a/src/renderer/pages/game-details/select-folder-modal.css.tsx b/src/renderer/pages/game-details/select-folder-modal.css.tsx new file mode 100644 index 000000000..7d32bccc6 --- /dev/null +++ b/src/renderer/pages/game-details/select-folder-modal.css.tsx @@ -0,0 +1,19 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "@renderer/theme.css"; + +export const container = style({ + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 2}px`, + width: "100%", +}); + +export const downloadsPathField = style({ + display: "flex", + gap: `${SPACING_UNIT * 2}px`, +}); + +export const hintText = style({ + fontSize: 12, + color: vars.color.bodyText, +}); diff --git a/src/renderer/pages/game-details/select-folder-modal.tsx b/src/renderer/pages/game-details/select-folder-modal.tsx new file mode 100644 index 000000000..4edfa6b1e --- /dev/null +++ b/src/renderer/pages/game-details/select-folder-modal.tsx @@ -0,0 +1,99 @@ +import { Button, Modal, TextField } from "@renderer/components"; +import { GameRepack, ShopDetails } from "@types"; +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import * as styles from "./select-folder-modal.css"; +import { Link } from "react-router-dom"; + +export interface SelectFolderModalProps { + visible: boolean; + gameDetails: ShopDetails; + onClose: () => void; + startDownload: (repackId: number, downloadPath: string) => Promise; + repack: GameRepack; +} + +export function SelectFolderModal({ + visible, + gameDetails, + onClose, + startDownload, + repack, +}: SelectFolderModalProps) { + const { t } = useTranslation("game_details"); + + const [selectedPath, setSelectedPath] = useState(""); + const [downloadStarting, setDownloadStarting] = useState(false); + + useEffect(() => { + Promise.all([ + window.electron.getDefaultDownloadsPath(), + window.electron.getUserPreferences(), + ]).then(([path, userPreferences]) => { + setSelectedPath(userPreferences?.downloadsPath || path); + }); + }, []); + + const handleChooseDownloadsPath = async () => { + const { filePaths } = await window.electron.showOpenDialog({ + defaultPath: selectedPath, + properties: ["openDirectory"], + }); + + if (filePaths && filePaths.length > 0) { + const path = filePaths[0]; + setSelectedPath(path); + } + }; + + const handleStartClick = () => { + setDownloadStarting(true); + startDownload(repack.id, selectedPath).finally(() => { + setDownloadStarting(false); + }); + }; + + return ( + +

+
+ + + +
+

+ {t("select_folder_hint")}{" "} + + {t("hydra_settings")} + +

+ +
+
+ ); +} From d488a9858d981a55ad9d1790ad36462eea47eb1a Mon Sep 17 00:00:00 2001 From: Hydra Date: Sun, 21 Apr 2024 22:57:31 -0300 Subject: [PATCH 10/70] feat: change diskFreeSpace from rapacks modal to select folder modal description --- src/locales/en/translation.json | 2 +- src/locales/pt/translation.json | 2 +- src/renderer/declaration.d.ts | 2 +- .../pages/game-details/game-details.tsx | 4 ++-- .../pages/game-details/repacks-modal.tsx | 21 +++---------------- .../game-details/select-folder-modal.tsx | 19 +++++++++++++++-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 3898ddb94..20043f54e 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -79,7 +79,7 @@ "close": "Close", "playing_now": "Playing now", "change": "Change", - "select_folder_description": "Select the folder where the game will be installed", + "repacks_modal_description": "Choose the repack you want to download", "downloads_path": "Downloads path", "select_folder_hint": "To change the default folder, access the", "hydra_settings": "Hydra settings", diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 110e0e71b..5733059d5 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -75,7 +75,7 @@ "deleting": "Excluindo instalador…", "playing_now": "Jogando agora", "change": "Mudar", - "select_folder_description": "Selecione a pasta em que o jogo será baixado", + "repacks_modal_description": "Escolha o repack do jogo que deseja baixar", "downloads_path": "Diretório do download", "select_folder_hint": "Para trocar a pasta padrão, acesse as ", "hydra_settings": "Configurações do Hydra", diff --git a/src/renderer/declaration.d.ts b/src/renderer/declaration.d.ts index f8d1018c3..3184940ac 100644 --- a/src/renderer/declaration.d.ts +++ b/src/renderer/declaration.d.ts @@ -76,7 +76,7 @@ declare global { ) => Promise; /* Hardware */ - getDiskFreeSpace: () => Promise; + getDiskFreeSpace: (path: string) => Promise; /* Misc */ getOrCacheImage: (url: string) => Promise; diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index aae3f2675..7f7441989 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -19,15 +19,15 @@ import { useAppDispatch, useDownload } from "@renderer/hooks"; import starsAnimation from "@renderer/assets/lottie/stars.json"; import { vars } from "@renderer/theme.css"; +import Lottie from "lottie-react"; import { useTranslation } from "react-i18next"; import { SkeletonTheme } from "react-loading-skeleton"; +import { DescriptionHeader } from "./description-header"; import { GameDetailsSkeleton } from "./game-details-skeleton"; import * as styles from "./game-details.css"; import { HeroPanel } from "./hero-panel"; import { HowLongToBeatSection } from "./how-long-to-beat-section"; import { RepacksModal } from "./repacks-modal"; -import Lottie from "lottie-react"; -import { DescriptionHeader } from "./description-header"; export function GameDetails() { const { objectID, shop } = useParams(); diff --git a/src/renderer/pages/game-details/repacks-modal.tsx b/src/renderer/pages/game-details/repacks-modal.tsx index 7908c486b..3e2404616 100644 --- a/src/renderer/pages/game-details/repacks-modal.tsx +++ b/src/renderer/pages/game-details/repacks-modal.tsx @@ -6,11 +6,9 @@ import type { GameRepack, ShopDetails } from "@types"; import * as styles from "./repacks-modal.css"; -import type { DiskSpace } from "check-disk-space"; -import { format } from "date-fns"; -import { SPACING_UNIT } from "@renderer/theme.css"; -import { formatBytes } from "@renderer/utils"; import { useAppSelector } from "@renderer/hooks"; +import { SPACING_UNIT } from "@renderer/theme.css"; +import { format } from "date-fns"; import { SelectFolderModal } from "./select-folder-modal"; export interface RepacksModalProps { @@ -30,7 +28,6 @@ export function RepacksModal({ startDownload, onClose, }: RepacksModalProps) { - const [diskFreeSpace, setDiskFreeSpace] = useState(null); const [filteredRepacks, setFilteredRepacks] = useState([]); const [repack, setRepack] = useState(null); @@ -44,16 +41,6 @@ export function RepacksModal({ setFilteredRepacks(gameDetails.repacks); }, [gameDetails.repacks]); - const getDiskFreeSpace = () => { - window.electron.getDiskFreeSpace().then((result) => { - setDiskFreeSpace(result); - }); - }; - - useEffect(() => { - getDiskFreeSpace(); - }, [visible]); - const handleRepackClick = (repack: GameRepack) => { setRepack(repack); setShowSelectFolderModal(true); @@ -73,9 +60,7 @@ export function RepacksModal({ (null); const [selectedPath, setSelectedPath] = useState(""); const [downloadStarting, setDownloadStarting] = useState(false); + useEffect(() => { + visible && getDiskFreeSpace(selectedPath); + }, [visible, selectedPath]); + useEffect(() => { Promise.all([ window.electron.getDefaultDownloadsPath(), @@ -35,6 +42,12 @@ export function SelectFolderModal({ }); }, []); + const getDiskFreeSpace = (path: string) => { + window.electron.getDiskFreeSpace(path).then((result) => { + setDiskFreeSpace(result); + }); + }; + const handleChooseDownloadsPath = async () => { const { filePaths } = await window.electron.showOpenDialog({ defaultPath: selectedPath, @@ -58,7 +71,9 @@ export function SelectFolderModal({
From 0f60f5ca83ffe8950f39880f8add0c41463c9ce8 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sun, 21 Apr 2024 22:59:03 -0300 Subject: [PATCH 11/70] fix: disable change path button when downloading is starting --- src/renderer/pages/game-details/select-folder-modal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/pages/game-details/select-folder-modal.tsx b/src/renderer/pages/game-details/select-folder-modal.tsx index 5799600c4..d37271c8f 100644 --- a/src/renderer/pages/game-details/select-folder-modal.tsx +++ b/src/renderer/pages/game-details/select-folder-modal.tsx @@ -89,6 +89,7 @@ export function SelectFolderModal({ style={{ alignSelf: "flex-end" }} theme="outline" onClick={handleChooseDownloadsPath} + disabled={downloadStarting} > {t("change")} From dbdb36999df1628ee9fd708e411a17f990cb12e7 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 09:34:07 -0300 Subject: [PATCH 12/70] feat: close modal on Esc press --- src/renderer/components/modal/modal.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 35be1bf90..4586a7412 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -38,6 +38,16 @@ export function Modal({ }); }; + useEffect(() => { + const close = (e: KeyboardEvent) => { + if (e.key === "Escape") { + handleCloseClick(); + } + }; + window.addEventListener("keydown", close); + return () => window.removeEventListener("keydown", close); + }, []); + useEffect(() => { dispatch(toggleDragging(visible)); }, [dispatch, visible]); From f8947d4f496fb30cbc4b25030e1156feeef6f868 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 15:01:02 -0300 Subject: [PATCH 13/70] feat: correctly check if modal is top most modal --- src/renderer/components/modal/modal.tsx | 46 +++++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 4586a7412..38236fb60 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useId, useState } from "react"; import { createPortal } from "react-dom"; import { XIcon } from "@primer/octicons-react"; @@ -23,6 +23,7 @@ export function Modal({ }: ModalProps) { const [isClosing, setIsClosing] = useState(false); const dispatch = useAppDispatch(); + const componentId = useId(); const handleCloseClick = () => { setIsClosing(true); @@ -38,14 +39,39 @@ export function Modal({ }); }; + const isTopMostModal = () => { + const openModals = document.getElementsByClassName("modal-container"); + return openModals.length && openModals[openModals.length - 1].id === componentId; + }; + useEffect(() => { - const close = (e: KeyboardEvent) => { - if (e.key === "Escape") { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape" && isTopMostModal()) { handleCloseClick(); } }; - window.addEventListener("keydown", close); - return () => window.removeEventListener("keydown", close); + + window.addEventListener("keydown", onKeyDown, false); + return () => window.removeEventListener("keydown", onKeyDown, false); + }, []); + + useEffect(() => { + const onMouseUp = (e: MouseEvent) => { + if (!isTopMostModal()) return; + + const modalContent = document.getElementById( + "modal-content-" + componentId + ); + + const clickInsideContent = modalContent.contains(e.target as Node); + + if (!clickInsideContent) { + handleCloseClick(); + } + }; + + window.addEventListener("mousedown", onMouseUp); + return () => window.removeEventListener("mousedown", onMouseUp); }, []); useEffect(() => { @@ -55,8 +81,14 @@ export function Modal({ if (!visible) return null; return createPortal( -
-
+
+

{title}

From 67af138e5af473babf6ac34adbb623cf39242bf2 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 15:04:22 -0300 Subject: [PATCH 14/70] feat: apply prettier and rename variable --- src/renderer/components/modal/modal.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 38236fb60..e6ab29739 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -41,7 +41,9 @@ export function Modal({ const isTopMostModal = () => { const openModals = document.getElementsByClassName("modal-container"); - return openModals.length && openModals[openModals.length - 1].id === componentId; + return ( + openModals.length && openModals[openModals.length - 1].id === componentId + ); }; useEffect(() => { @@ -63,9 +65,9 @@ export function Modal({ "modal-content-" + componentId ); - const clickInsideContent = modalContent.contains(e.target as Node); + const clickedOutsideContent = !modalContent.contains(e.target as Node); - if (!clickInsideContent) { + if (clickedOutsideContent) { handleCloseClick(); } }; From 9fa88255a80d1cbdfb6eac8d72222eb733277daf Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 15:07:31 -0300 Subject: [PATCH 15/70] feat: rename modal container id --- src/renderer/components/modal/modal.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index e6ab29739..0f15a0dec 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -42,7 +42,8 @@ export function Modal({ const isTopMostModal = () => { const openModals = document.getElementsByClassName("modal-container"); return ( - openModals.length && openModals[openModals.length - 1].id === componentId + openModals.length && + openModals[openModals.length - 1].id === "modal-container-" + componentId ); }; @@ -85,7 +86,7 @@ export function Modal({ return createPortal(
Date: Tue, 23 Apr 2024 15:47:10 -0300 Subject: [PATCH 16/70] feat: use role attribute instead of using class with no style --- src/renderer/components/modal/modal.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 0f15a0dec..6cd417dd6 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -40,7 +40,7 @@ export function Modal({ }; const isTopMostModal = () => { - const openModals = document.getElementsByClassName("modal-container"); + const openModals = document.querySelectorAll("[role=modal-container]"); return ( openModals.length && openModals[openModals.length - 1].id === "modal-container-" + componentId @@ -85,7 +85,8 @@ export function Modal({ return createPortal(
Date: Tue, 23 Apr 2024 15:47:34 -0300 Subject: [PATCH 17/70] feat: rename function --- src/renderer/components/modal/modal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 6cd417dd6..eed462538 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -59,7 +59,7 @@ export function Modal({ }, []); useEffect(() => { - const onMouseUp = (e: MouseEvent) => { + const onMouseDown = (e: MouseEvent) => { if (!isTopMostModal()) return; const modalContent = document.getElementById( @@ -73,8 +73,8 @@ export function Modal({ } }; - window.addEventListener("mousedown", onMouseUp); - return () => window.removeEventListener("mousedown", onMouseUp); + window.addEventListener("mousedown", onMouseDown); + return () => window.removeEventListener("mousedown", onMouseDown); }, []); useEffect(() => { From d9c6d8704db66073ec802ffe99d7d93939f2890a Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 15:48:00 -0300 Subject: [PATCH 18/70] feat: useRef instead of getting element by id --- src/renderer/components/modal/modal.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index eed462538..2b31998ba 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useId, useState } from "react"; +import { useEffect, useId, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { XIcon } from "@primer/octicons-react"; @@ -23,6 +23,7 @@ export function Modal({ }: ModalProps) { const [isClosing, setIsClosing] = useState(false); const dispatch = useAppDispatch(); + const modalContentRef = useRef(null); const componentId = useId(); const handleCloseClick = () => { @@ -54,19 +55,15 @@ export function Modal({ } }; - window.addEventListener("keydown", onKeyDown, false); - return () => window.removeEventListener("keydown", onKeyDown, false); + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); }, []); useEffect(() => { const onMouseDown = (e: MouseEvent) => { if (!isTopMostModal()) return; - const modalContent = document.getElementById( - "modal-content-" + componentId - ); - - const clickedOutsideContent = !modalContent.contains(e.target as Node); + const clickedOutsideContent = !modalContentRef.current.contains(e.target as Node); if (clickedOutsideContent) { handleCloseClick(); @@ -91,7 +88,7 @@ export function Modal({ >
From c1055463cce84c16e06f2eaea13ffe10c889aa6a Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:11:38 -0300 Subject: [PATCH 19/70] feat: removing necessity of id on outer div --- src/renderer/components/modal/modal.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 2b31998ba..a1cfb0873 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -41,10 +41,10 @@ export function Modal({ }; const isTopMostModal = () => { - const openModals = document.querySelectorAll("[role=modal-container]"); + const openModals = document.querySelectorAll("[role=modal-content]"); return ( openModals.length && - openModals[openModals.length - 1].id === "modal-container-" + componentId + openModals[openModals.length - 1] === modalContentRef.current ); }; @@ -63,7 +63,9 @@ export function Modal({ const onMouseDown = (e: MouseEvent) => { if (!isTopMostModal()) return; - const clickedOutsideContent = !modalContentRef.current.contains(e.target as Node); + const clickedOutsideContent = !modalContentRef.current.contains( + e.target as Node + ); if (clickedOutsideContent) { handleCloseClick(); @@ -81,13 +83,10 @@ export function Modal({ if (!visible) return null; return createPortal( -
+
From 5bcce3eca22eb62f38e3a7293a61bd77adb639b6 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:15:26 -0300 Subject: [PATCH 20/70] feat: rename role --- src/renderer/components/modal/modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index a1cfb0873..cc75db26b 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -41,7 +41,7 @@ export function Modal({ }; const isTopMostModal = () => { - const openModals = document.querySelectorAll("[role=modal-content]"); + const openModals = document.querySelectorAll("[role=modal]"); return ( openModals.length && openModals[openModals.length - 1] === modalContentRef.current @@ -86,7 +86,7 @@ export function Modal({
From 01c66d79afb8ff24255f593ca20ec4ac88f4b340 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:25:47 -0300 Subject: [PATCH 21/70] fix: add handleCloseClick to useEffect dependency --- src/renderer/components/modal/modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index cc75db26b..f2575e1fa 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -57,7 +57,7 @@ export function Modal({ window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); - }, []); + }, [handleCloseClick]); useEffect(() => { const onMouseDown = (e: MouseEvent) => { @@ -74,7 +74,7 @@ export function Modal({ window.addEventListener("mousedown", onMouseDown); return () => window.removeEventListener("mousedown", onMouseDown); - }, []); + }, [handleCloseClick]); useEffect(() => { dispatch(toggleDragging(visible)); From 7ba7391c833c145f5d7709afefbc342182a39763 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:28:32 -0300 Subject: [PATCH 22/70] fix: remove unused variable --- src/renderer/components/modal/modal.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index f2575e1fa..288930e3c 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useId, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { XIcon } from "@primer/octicons-react"; @@ -24,7 +24,6 @@ export function Modal({ const [isClosing, setIsClosing] = useState(false); const dispatch = useAppDispatch(); const modalContentRef = useRef(null); - const componentId = useId(); const handleCloseClick = () => { setIsClosing(true); From d11c142032194ed87a220fedb7ebc4a039cc93fa Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:35:40 -0300 Subject: [PATCH 23/70] trigger pipeline --- src/renderer/components/modal/modal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 288930e3c..33e07680c 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -33,6 +33,7 @@ export function Modal({ if (time - zero <= 400) { requestAnimationFrame(animateClosing); } else { + onClose(); setIsClosing(false); } From 6131c1406793414364e1502e073facdc1715a924 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:35:48 -0300 Subject: [PATCH 24/70] trigger pipeline --- src/renderer/components/modal/modal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 33e07680c..288930e3c 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -33,7 +33,6 @@ export function Modal({ if (time - zero <= 400) { requestAnimationFrame(animateClosing); } else { - onClose(); setIsClosing(false); } From b60c319dfd675ec7ebfcde468e94ebd217e3b362 Mon Sep 17 00:00:00 2001 From: Fernando Zanutto Date: Tue, 23 Apr 2024 19:44:01 -0300 Subject: [PATCH 25/70] lint warnings --- src/renderer/components/modal/modal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/components/modal/modal.tsx b/src/renderer/components/modal/modal.tsx index 288930e3c..9b5f8cf5b 100644 --- a/src/renderer/components/modal/modal.tsx +++ b/src/renderer/components/modal/modal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { XIcon } from "@primer/octicons-react"; @@ -25,7 +25,7 @@ export function Modal({ const dispatch = useAppDispatch(); const modalContentRef = useRef(null); - const handleCloseClick = () => { + const handleCloseClick = useCallback(() => { setIsClosing(true); const zero = performance.now(); @@ -37,7 +37,7 @@ export function Modal({ setIsClosing(false); } }); - }; + }, [onClose]); const isTopMostModal = () => { const openModals = document.querySelectorAll("[role=modal]"); From e18200d79628da6009fb83108050c46bd4dd637e Mon Sep 17 00:00:00 2001 From: Magrid Date: Wed, 24 Apr 2024 20:21:37 +0200 Subject: [PATCH 26/70] fix french translation --- src/locales/fr/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index e3d2bd3d5..154186be3 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -25,7 +25,7 @@ "downloads": "Téléchargements", "search_results": "Résultats de la recherche", "settings": "Paramètres", - "home": "Maison" + "home": "Accueil" }, "bottom_panel": { "no_downloads_in_progress": "Aucun téléchargement en cours", From c07f82ce4977c9c006e85d1cf07ee4cf26832301 Mon Sep 17 00:00:00 2001 From: Hydra Date: Thu, 25 Apr 2024 05:57:01 +0100 Subject: [PATCH 27/70] chore: merge with main --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c06126d31..d168333d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: with: vt_api_key: ${{ secrets.VT_API_KEY }} files: | - .exe$ + ./hydra-download-manager/hydra-download-manager.exe # - name: Publish # run: yarn run publish From f9223ad36d0e7902a1e4aa33a2c9295bf36cb61a Mon Sep 17 00:00:00 2001 From: Hydra Date: Thu, 25 Apr 2024 20:54:38 +0100 Subject: [PATCH 28/70] fix: fixing typechecks --- .eslintrc.cjs | 13 + .github/workflows/build.yml | 21 +- package.json | 6 +- src/main/events/catalogue/get-games.ts | 2 +- src/main/events/catalogue/get-random-game.ts | 2 +- src/main/events/index.ts | 1 - src/main/events/library/remove-game.ts | 16 +- .../events/torrenting/cancel-game-download.ts | 2 +- .../events/torrenting/pause-game-download.ts | 2 +- .../torrenting/remove-game-from-download.ts | 34 --- .../user-preferences/get-user-preferences.ts | 2 +- src/main/main.ts | 8 +- .../services/repack-tracker/cpg-repacks.ts | 8 +- src/main/services/repack-tracker/index.ts | 2 +- .../services/repack-tracker/online-fix.ts | 8 +- src/main/services/steam-250.ts | 11 +- src/main/services/window-manager.ts | 21 +- src/renderer/src/app.tsx | 2 +- .../components/async-image/async-image.tsx | 2 + .../components/bottom-panel/bottom-panel.tsx | 2 +- yarn.lock | 236 +++++++++++++++++- 21 files changed, 311 insertions(+), 90 deletions(-) delete mode 100644 src/main/events/torrenting/remove-game-from-download.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 9d21efd01..49aa25e2d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -3,7 +3,20 @@ module.exports = { "eslint:recommended", "plugin:react/recommended", "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", "@electron-toolkit/eslint-config-ts/recommended", "prettier", ], + rules: { + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + }, + ], + "@typescript-eslint/no-explicit-any": "warn", + }, }; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d168333d5..973f95bc7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,6 +47,17 @@ jobs: - name: Build with cx_Freeze run: python torrent-client/setup.py build + - name: Publish + run: yarn run publish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} + RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} + MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }} + MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }} + - name: VirusTotal Scan uses: crazy-max/ghaction-virustotal@v4 with: @@ -54,16 +65,6 @@ jobs: files: | ./hydra-download-manager/hydra-download-manager.exe - # - name: Publish - # run: yarn run publish - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }} - # SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - # SENTRY_DSN: ${{ vars.SENTRY_DSN }} - # ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }} - # ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }} - - name: Create artifact uses: actions/upload-artifact@v4 with: diff --git a/package.json b/package.json index 07e3e8835..60f05f455 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hydra", "version": "1.0.0", - "description": "An Electron application with React and TypeScript", + "description": "Hydra", "main": "./out/main/index.js", "author": "Los Broxas", "homepage": "https://electron-vite.org", @@ -43,6 +43,7 @@ "color.js": "^1.2.0", "date-fns": "^3.6.0", "flexsearch": "^0.7.43", + "got-scraping": "^4.0.5", "i18next": "^23.11.2", "i18next-browser-languagedetector": "^7.2.1", "jsdom": "^24.0.0", @@ -56,6 +57,7 @@ "react-router-dom": "^6.22.3", "tasklist": "^5.0.0", "typeorm": "^0.3.20", + "windows-1251": "^3.0.4", "winston": "^3.13.0", "yaml": "^2.4.1" }, @@ -64,6 +66,7 @@ "@electron-toolkit/eslint-config-ts": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1", "@swc/core": "^1.4.16", + "@types/jsdom": "^21.1.6", "@types/lodash-es": "^4.17.12", "@types/node": "^20.12.7", "@types/react": "^18.2.48", @@ -75,6 +78,7 @@ "electron-vite": "^2.0.0", "eslint": "^8.56.0", "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", "prettier": "^3.2.4", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/main/events/catalogue/get-games.ts b/src/main/events/catalogue/get-games.ts index 64c491aeb..b27669281 100644 --- a/src/main/events/catalogue/get-games.ts +++ b/src/main/events/catalogue/get-games.ts @@ -9,7 +9,7 @@ const steamGames = stateManager.getValue("steamGames"); const getGames = async ( _event: Electron.IpcMainInvokeEvent, - take?: number, + take = 12, cursor = 0 ): Promise<{ results: CatalogueEntry[]; cursor: number }> => { const results: CatalogueEntry[] = []; diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts index 335fa0263..a7483f0e7 100644 --- a/src/main/events/catalogue/get-random-game.ts +++ b/src/main/events/catalogue/get-random-game.ts @@ -6,7 +6,7 @@ import { registerEvent } from "../register-event"; import { searchGames, searchRepacks } from "../helpers/search-games"; import { formatName } from "@main/helpers"; -const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { +const getRandomGame = async () => { return getRandomSteam250List().then(async (games) => { const shuffledList = shuffle(games); diff --git a/src/main/events/index.ts b/src/main/events/index.ts index e62720fb8..4ce550c64 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -25,7 +25,6 @@ import "./torrenting/cancel-game-download"; import "./torrenting/pause-game-download"; import "./torrenting/resume-game-download"; import "./torrenting/start-game-download"; -import "./torrenting/remove-game-from-download"; import "./user-preferences/get-user-preferences"; import "./user-preferences/update-user-preferences"; diff --git a/src/main/events/library/remove-game.ts b/src/main/events/library/remove-game.ts index 64e84b7de..d571e8217 100644 --- a/src/main/events/library/remove-game.ts +++ b/src/main/events/library/remove-game.ts @@ -1,10 +1,24 @@ import { registerEvent } from "../register-event"; import { gameRepository } from "../../repository"; +import { GameStatus } from "@main/constants"; const removeGame = async ( _event: Electron.IpcMainInvokeEvent, gameId: number -) => gameRepository.delete({ id: gameId }); +) => { + await gameRepository.update( + { + id: gameId, + status: GameStatus.Cancelled, + }, + { + status: null, + downloadPath: null, + bytesDownloaded: 0, + progress: 0, + } + ); +}; registerEvent(removeGame, { name: "removeGame", diff --git a/src/main/events/torrenting/cancel-game-download.ts b/src/main/events/torrenting/cancel-game-download.ts index a1a2e6b7c..77e633b04 100644 --- a/src/main/events/torrenting/cancel-game-download.ts +++ b/src/main/events/torrenting/cancel-game-download.ts @@ -42,7 +42,7 @@ const cancelGameDownload = async ( game.status !== GameStatus.Seeding ) { writePipe.write({ action: "cancel" }); - if (result.affected) WindowManager.mainWindow.setProgressBar(-1); + if (result.affected) WindowManager.mainWindow?.setProgressBar(-1); } }); }; diff --git a/src/main/events/torrenting/pause-game-download.ts b/src/main/events/torrenting/pause-game-download.ts index d89f2f725..943bea375 100644 --- a/src/main/events/torrenting/pause-game-download.ts +++ b/src/main/events/torrenting/pause-game-download.ts @@ -24,7 +24,7 @@ const pauseGameDownload = async ( .then((result) => { if (result.affected) { writePipe.write({ action: "pause" }); - WindowManager.mainWindow.setProgressBar(-1); + WindowManager.mainWindow?.setProgressBar(-1); } }); }; diff --git a/src/main/events/torrenting/remove-game-from-download.ts b/src/main/events/torrenting/remove-game-from-download.ts deleted file mode 100644 index 47c1ebe63..000000000 --- a/src/main/events/torrenting/remove-game-from-download.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { GameStatus } from "@main/constants"; -import { gameRepository } from "@main/repository"; - -import { registerEvent } from "../register-event"; - -const removeGameFromDownload = async ( - _event: Electron.IpcMainInvokeEvent, - gameId: number -) => { - const game = await gameRepository.findOne({ - where: { - id: gameId, - status: GameStatus.Cancelled, - }, - }); - - if (!game) return; - - gameRepository.update( - { - id: game.id, - }, - { - status: null, - downloadPath: null, - bytesDownloaded: 0, - progress: 0, - } - ); -}; - -registerEvent(removeGameFromDownload, { - name: "removeGameFromDownload", -}); diff --git a/src/main/events/user-preferences/get-user-preferences.ts b/src/main/events/user-preferences/get-user-preferences.ts index 219713eb2..03b4ae9d9 100644 --- a/src/main/events/user-preferences/get-user-preferences.ts +++ b/src/main/events/user-preferences/get-user-preferences.ts @@ -1,7 +1,7 @@ import { userPreferencesRepository } from "@main/repository"; import { registerEvent } from "../register-event"; -const getUserPreferences = async (_event: Electron.IpcMainInvokeEvent) => +const getUserPreferences = async () => userPreferencesRepository.findOne({ where: { id: 1 }, }); diff --git a/src/main/main.ts b/src/main/main.ts index a98b804c0..c54a7e9a2 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -5,7 +5,7 @@ import { getNewRepacksFromCPG, getNewRepacksFromUser, // getNewRepacksFromXatab, - // getNewRepacksFromOnlineFix, + getNewRepacksFromOnlineFix, readPipe, startProcessWatcher, writePipe, @@ -79,9 +79,9 @@ const checkForNewRepacks = async () => { getNewRepacksFromCPG( existingRepacks.filter((repack) => repack.repacker === "CPG") ), - // getNewRepacksFromOnlineFix( - // existingRepacks.filter((repack) => repack.repacker === "onlinefix") - // ), + getNewRepacksFromOnlineFix( + existingRepacks.filter((repack) => repack.repacker === "onlinefix") + ), track1337xUsers(existingRepacks), ]).then(() => { repackRepository.count().then((count) => { diff --git a/src/main/services/repack-tracker/cpg-repacks.ts b/src/main/services/repack-tracker/cpg-repacks.ts index 0d7c172b6..c4b62c1f1 100644 --- a/src/main/services/repack-tracker/cpg-repacks.ts +++ b/src/main/services/repack-tracker/cpg-repacks.ts @@ -19,17 +19,17 @@ export const getNewRepacksFromCPG = async ( try { Array.from(window.document.querySelectorAll(".post")).forEach(($post) => { const $title = $post.querySelector(".entry-title"); - const uploadDate = $post.querySelector("time").getAttribute("datetime"); + const uploadDate = $post.querySelector("time")?.getAttribute("datetime"); const $downloadInfo = Array.from( $post.querySelectorAll(".wp-block-heading") - ).find(($heading) => $heading.textContent.startsWith("Download")); + ).find(($heading) => $heading.textContent?.startsWith("Download")); /* Side note: CPG often misspells "Magnet" as "Magent" */ const $magnet = Array.from($post.querySelectorAll("a")).find( ($a) => - $a.textContent.startsWith("Magnet") || - $a.textContent.startsWith("Magent") + $a.textContent?.startsWith("Magnet") || + $a.textContent?.startsWith("Magent") ); const fileSize = $downloadInfo.textContent diff --git a/src/main/services/repack-tracker/index.ts b/src/main/services/repack-tracker/index.ts index fb345fae2..7bd3ce31c 100644 --- a/src/main/services/repack-tracker/index.ts +++ b/src/main/services/repack-tracker/index.ts @@ -2,4 +2,4 @@ export * from "./1337x"; export * from "./xatab"; export * from "./cpg-repacks"; export * from "./gog"; -// export * from "./online-fix"; +export * from "./online-fix"; diff --git a/src/main/services/repack-tracker/online-fix.ts b/src/main/services/repack-tracker/online-fix.ts index c572629cd..2a69dd705 100644 --- a/src/main/services/repack-tracker/online-fix.ts +++ b/src/main/services/repack-tracker/online-fix.ts @@ -88,7 +88,7 @@ export const getNewRepacksFromOnlineFix = async ( const repacks: GameRepackInput[] = []; const articles = Array.from(document.querySelectorAll(".news")); const totalPages = Number( - document.querySelector("nav > a:nth-child(13)").textContent + document.querySelector("nav > a:nth-child(13)")?.textContent ); try { @@ -186,8 +186,10 @@ export const getNewRepacksFromOnlineFix = async ( }); }) ); - } catch (err) { - logger.error(err.message, { method: "getNewRepacksFromOnlineFix" }); + } catch (err: unknown) { + logger.error((err as Error).message, { + method: "getNewRepacksFromOnlineFix", + }); } const newRepacks = repacks.filter( diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index 32ccdc563..bec2c6041 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -7,17 +7,16 @@ export const requestSteam250 = async (path: string) => { const { window } = new JSDOM(response.data); const { document } = window; - return Array.from(document.querySelectorAll(".appline .title a")).map( - ($title: HTMLAnchorElement) => { - const steamGameUrl = $title.href; - if (!steamGameUrl) return null; + return Array.from(document.querySelectorAll(".appline .title a")) + .filter(($title) => Boolean(($title as HTMLAnchorElement).href)) + .map(($title) => { + const steamGameUrl = ($title as HTMLAnchorElement).href; return { title: $title.textContent, objectID: steamGameUrl.split("/").pop(), }; - } - ); + }); }); }; diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 0eea8e96a..766eb8262 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -12,13 +12,16 @@ export class WindowManager { // HMR for renderer base on electron-vite cli. // Load the remote URL for development or the local html file for production. if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { - this.mainWindow.loadURL( + this.mainWindow?.loadURL( `${process.env["ELECTRON_RENDERER_URL"]}#/${hash}` ); } else { - this.mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"), { - hash, - }); + this.mainWindow?.loadFile( + path.join(__dirname, "../renderer/index.html"), + { + hash, + } + ); } } @@ -47,7 +50,7 @@ export class WindowManager { this.mainWindow.removeMenu(); this.mainWindow.on("close", () => { - WindowManager.mainWindow.setProgressBar(-1); + WindowManager.mainWindow?.setProgressBar(-1); }); } @@ -55,8 +58,8 @@ export class WindowManager { if (!this.mainWindow) this.createMainWindow(); this.loadURL(hash); - if (this.mainWindow.isMinimized()) this.mainWindow.restore(); - this.mainWindow.focus(); + if (this.mainWindow?.isMinimized()) this.mainWindow.restore(); + this.mainWindow?.focus(); } public static createSystemTray(language: string) { @@ -93,10 +96,10 @@ export class WindowManager { if (process.platform === "win32") { tray.addListener("click", () => { if (this.mainWindow) { - if (WindowManager.mainWindow.isMinimized()) + if (WindowManager.mainWindow?.isMinimized()) WindowManager.mainWindow.restore(); - WindowManager.mainWindow.focus(); + WindowManager.mainWindow?.focus(); return; } diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index dd3488cc0..6461c0d01 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -12,7 +12,7 @@ import { import * as styles from "./app.css"; import { themeClass } from "./theme.css"; -import { Outlet, useLocation, useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { setSearch, clearSearch, diff --git a/src/renderer/src/components/async-image/async-image.tsx b/src/renderer/src/components/async-image/async-image.tsx index 857a99429..e3c0ee459 100644 --- a/src/renderer/src/components/async-image/async-image.tsx +++ b/src/renderer/src/components/async-image/async-image.tsx @@ -25,3 +25,5 @@ export const AsyncImage = forwardRef( return ; } ); + +AsyncImage.displayName = "AsyncImage"; diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.tsx b/src/renderer/src/components/bottom-panel/bottom-panel.tsx index df1f49cbb..ed9a54ed6 100644 --- a/src/renderer/src/components/bottom-panel/bottom-panel.tsx +++ b/src/renderer/src/components/bottom-panel/bottom-panel.tsx @@ -61,7 +61,7 @@ export function BottomPanel() { - v{version} "{VERSION_CODENAME}" + v{version} "{VERSION_CODENAME}" ); diff --git a/yarn.lock b/yarn.lock index 6bf63b34f..e708f28d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1024,11 +1024,16 @@ dependencies: "@sentry/types" "7.111.0" -"@sindresorhus/is@^4.0.0": +"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.2.0": version "4.6.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sindresorhus/is@^5.2.0", "@sindresorhus/is@^5.3.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + "@sqltools/formatter@^1.2.5": version "1.2.5" resolved "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz" @@ -1205,6 +1210,13 @@ dependencies: defer-to-connect "^2.0.0" +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + "@thaunknown/thirty-two@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@thaunknown/thirty-two/-/thirty-two-1.0.3.tgz" @@ -1279,11 +1291,20 @@ dependencies: "@types/node" "*" -"@types/http-cache-semantics@*": +"@types/http-cache-semantics@*", "@types/http-cache-semantics@^4.0.2": version "4.0.4" resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz" integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== +"@types/jsdom@^21.1.6": + version "21.1.6" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.6.tgz#bcbc7b245787ea863f3da1ef19aa1dcfb9271a1b" + integrity sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/json-schema@^7.0.12": version "7.0.15" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" @@ -1367,6 +1388,11 @@ resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + "@types/triple-beam@^1.3.2": version "1.3.5" resolved "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz" @@ -1573,6 +1599,11 @@ acorn@^8.11.3, acorn@^8.9.0: resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== +adm-zip@^0.5.9: + version "0.5.12" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.12.tgz#87786328e91d54b37358d8a50f954c4cd73ba60b" + integrity sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ== + agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -1912,7 +1943,7 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.22.2: +browserslist@^4.21.1, browserslist@^4.22.2: version "4.23.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz" integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== @@ -1993,6 +2024,24 @@ cacheable-lookup@^5.0.3: resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + cacheable-request@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz" @@ -2017,11 +2066,16 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" -callsites@^3.0.0: +callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +callsites@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-4.1.0.tgz#de72b98612eed4e1e2564c952498677faa9d86c2" + integrity sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw== + camelcase@^6.2.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" @@ -2392,7 +2446,7 @@ deepmerge@4.3.0, deepmerge@^4.2.2: resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz" integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== -defer-to-connect@^2.0.0: +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== @@ -2495,6 +2549,20 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dot-prop@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + +dot-prop@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-7.2.0.tgz#468172a3529779814d21a779c1ba2f6d76609809" + integrity sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA== + dependencies: + type-fest "^2.11.2" + dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz" @@ -2829,6 +2897,11 @@ eslint-plugin-prettier@^5.0.1: prettier-linter-helpers "^1.0.0" synckit "^0.8.6" +eslint-plugin-react-hooks@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + eslint-plugin-react@^7.33.2: version "7.34.1" resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz" @@ -3118,6 +3191,11 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + form-data@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" @@ -3204,6 +3282,14 @@ functions-have-names@^1.2.3: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +generative-bayesian-network@^2.1.50: + version "2.1.50" + resolved "https://registry.yarnpkg.com/generative-bayesian-network/-/generative-bayesian-network-2.1.50.tgz#a576130befe0e30ccfebe5280fb2550649abadc9" + integrity sha512-iVmmQ4lpa41xqtrg6cbWuH1Qa2+C6tndb2dJmJazBEIQcnvz29ZYxbnqB1DAvbico3nGIVzF2Hvj2gZU9EewAQ== + dependencies: + adm-zip "^0.5.9" + tslib "^2.4.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" @@ -3237,6 +3323,11 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz" @@ -3338,6 +3429,19 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +got-scraping@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/got-scraping/-/got-scraping-4.0.5.tgz#e1cab8ff2420d9c9f406bac405c10e8b324ec4ec" + integrity sha512-g+cMC5WOVOHd6S3JdTtm+zCwpWdd3jA1MYnkOwVF7MpbKP7EWv7ORUfXDcG3gTANJ1zYj9XffCrAjbH8ssHmfw== + dependencies: + got "^13.0.0" + header-generator "^2.1.41" + http2-wrapper "^2.2.0" + mimic-response "^4.0.0" + ow "^1.1.1" + quick-lru "^7.0.0" + tslib "^2.6.2" + got@^11.8.5: version "11.8.6" resolved "https://registry.npmjs.org/got/-/got-11.8.6.tgz" @@ -3355,6 +3459,23 @@ got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" +got@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/got/-/got-13.0.0.tgz#a2402862cef27a5d0d1b07c0fb25d12b58175422" + integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" @@ -3411,6 +3532,16 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" +header-generator@^2.1.41: + version "2.1.50" + resolved "https://registry.yarnpkg.com/header-generator/-/header-generator-2.1.50.tgz#338fd7d92131dcaf71918b6ac8b9a26d6bb6acc0" + integrity sha512-Z37QBqcPzEqCCFQcOv1Kth1My3h4Vx+2V+aBipjrefZ2MFbVfYB/mo1v+OxiEJir5zSp9rX/z+BoqTuSAIGBLQ== + dependencies: + browserslist "^4.21.1" + generative-bayesian-network "^2.1.50" + ow "^0.28.1" + tslib "^2.4.0" + highlight.js@^10.7.1: version "10.7.3" resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz" @@ -3444,7 +3575,7 @@ html-parse-stringify@^3.0.1: dependencies: void-elements "3.1.0" -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -3474,6 +3605,14 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" +http2-wrapper@^2.1.10, http2-wrapper@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" @@ -3700,6 +3839,11 @@ is-number@^7.0.0: resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" @@ -3973,6 +4117,11 @@ lodash-es@^4.17.21: resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" @@ -4026,6 +4175,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + lru-cache@^10.2.0: version "10.2.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz" @@ -4115,6 +4269,11 @@ mimic-response@^3.1.0: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + minimatch@9.0.3, minimatch@^9.0.1: version "9.0.3" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" @@ -4274,6 +4433,11 @@ normalize-url@^6.0.1: resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +normalize-url@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a" + integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== + nwsapi@^2.2.7: version "2.2.8" resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.8.tgz" @@ -4372,11 +4536,38 @@ outdent@^0.8.0: resolved "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz" integrity sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A== +ow@^0.28.1: + version "0.28.2" + resolved "https://registry.yarnpkg.com/ow/-/ow-0.28.2.tgz#782b28102124e665c49ec7725e2066a129acf6bf" + integrity sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q== + dependencies: + "@sindresorhus/is" "^4.2.0" + callsites "^3.1.0" + dot-prop "^6.0.1" + lodash.isequal "^4.5.0" + vali-date "^1.0.0" + +ow@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ow/-/ow-1.1.1.tgz#354a0f7df9d8d0cf961b29116daf972ef6be1632" + integrity sha512-sJBRCbS5vh1Jp9EOgwp1Ws3c16lJrUkJYlvWTYC03oyiYVwS/ns7lKRWow4w4XjDyTrA2pplQv4B2naWSR6yDA== + dependencies: + "@sindresorhus/is" "^5.3.0" + callsites "^4.0.0" + dot-prop "^7.2.0" + lodash.isequal "^4.5.0" + vali-date "^1.0.0" + p-cancelable@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" @@ -4437,7 +4628,7 @@ parse5@^6.0.1: resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^7.1.2: +parse5@^7.0.0, parse5@^7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -4629,6 +4820,11 @@ quick-lru@^5.1.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +quick-lru@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-7.0.0.tgz#447f6925b33ae4d2d637e211967d74bae4b99c3f" + integrity sha512-MX8gB7cVYTrYcFfAnfLlhRd0+Toyl8yX8uBx1MrX7K0jegiz9TumwOK27ldXrgDlHRdVi+MqU9Ssw6dr4BNreg== + rc@^1.2.7: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" @@ -4784,7 +4980,7 @@ reselect@^5.0.1: resolved "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz" integrity sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg== -resolve-alpn@^1.0.0: +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -4810,6 +5006,13 @@ responselike@^2.0.0: dependencies: lowercase-keys "^2.0.0" +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + retry@^0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" @@ -5403,7 +5606,7 @@ ts-api-utils@^1.0.1: resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== -tslib@^2.0.3, tslib@^2.5.0, tslib@^2.6.2: +tslib@^2.0.3, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -5432,6 +5635,11 @@ type-fest@^0.20.2: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^2.11.2: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" @@ -5587,6 +5795,11 @@ uuid@^9.0.0: resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +vali-date@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" + integrity sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg== + verror@^1.10.0: version "1.10.1" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" @@ -5726,6 +5939,11 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +windows-1251@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/windows-1251/-/windows-1251-3.0.4.tgz#984b9f2e76befd9ec2e825f9fe77b681fadcdb55" + integrity sha512-H6W68MVertlR74xVuwa2pdQ1jR5qksk+oZX6QXFhL5OYj/ZZxViob8UyGLfXPwsCijuaV7NUYOYkK0oXSaWW5g== + winston-transport@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz" From 5c9e1099b4868523d24212f042b83068b0ceb9b2 Mon Sep 17 00:00:00 2001 From: Netflixy Date: Thu, 25 Apr 2024 23:24:50 +0200 Subject: [PATCH 29/70] fix: fix french terms in translation --- src/locales/fr/translation.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index 154186be3..3e247b07e 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -30,8 +30,8 @@ "bottom_panel": { "no_downloads_in_progress": "Aucun téléchargement en cours", "downloading_metadata": "Téléchargement des métadonnées de {{title}}…", - "checking_files": "Vérification des fichiers de {{title}}… ({{percentage}} complet)", - "downloading": "Téléchargement de {{title}}… ({{percentage}} complet) - Conclusion dans {{eta}} - {{speed}}" + "checking_files": "Vérification des fichiers de {{title}}… ({{percentage}} terminé)", + "downloading": "Téléchargement de {{title}}… ({{percentage}} terminé) - Fin dans {{eta}} - {{speed}}" }, "game_details": { "open_download_options": "Ouvrir les options de téléchargement", @@ -45,15 +45,15 @@ "remove": "Supprimer", "remove_from_list": "Retirer", "space_left_on_disk": "{{space}} restant sur le disque", - "eta": "Conclusion dans {{eta}}", + "eta": "Fin dans {{eta}}", "downloading_metadata": "Téléchargement des métadonnées en cours…", "checking_files": "Vérification des fichiers…", - "filter": "Filtrer les réductions", + "filter": "Filtrer les repacks", "requirements": "Configuration requise", "minimum": "Minimum", "recommended": "Recommandée", - "no_minimum_requirements": "{{title}} ne fournit pas d'informations sur les exigences minimales", - "no_recommended_requirements": "{{title}} ne fournit pas d'informations sur les exigences recommandées", + "no_minimum_requirements": "{{title}} ne fournit pas d'informations sur les configurations minimales", + "no_recommended_requirements": "{{title}} ne fournit pas d'informations sur les configurations recommandées", "paused_progress": "{{progress}} (En pause)", "release_date": "Sorti le {{date}}", "publisher": "Édité par {{publisher}}", @@ -72,21 +72,21 @@ "not_played_yet": "Vous n'avez pas encore joué à {{title}}", "close": "Fermer", "deleting": "Suppression du programme d'installation…", - "playing_now": "Je joue maintenant", - "last_time_played": "Dernière lecture {{période}}" + "playing_now": "Jeu en cours", + "last_time_played": "Dernièrement joué {{période}}" }, "activation": { "title": "Activer Hydra", "installation_id": "ID d'installation :", "enter_activation_code": "Entrez votre code d'activation", - "message": "Si vous ne savez pas où demander cela, vous ne devriez pas l'avoir.", + "message": "Si vous ne savez pas où demander ceci, vous ne devriez pas l'avoir.", "activate": "Activer", "loading": "Chargement en cours…" }, "downloads": { "resume": "Reprendre", "pause": "Pause", - "eta": "Conclusion dans {{eta}}", + "eta": "Fin dans {{eta}}", "paused": "En pause", "verifying": "Vérification en cours…", "completed_at": "Terminé en {{date}}", @@ -111,16 +111,16 @@ "change": "Mettre à jour", "notifications": "Notifications", "enable_download_notifications": "Quand un téléchargement est terminé", - "enable_repack_list_notifications": "Quand une nouvelle réduction est ajoutée", + "enable_repack_list_notifications": "Quand un nouveau repack est ajouté", "telemetry": "Télémétrie", "telemetry_description": "Activer les statistiques d'utilisation anonymes" }, "notifications": { "download_complete": "Téléchargement terminé", "game_ready_to_install": "{{title}} est prêt à être installé", - "repack_list_updated": "Liste de réductions mise à jour", - "repack_count_one": "{{count}} réduction ajoutée", - "repack_count_other": "{{count}} réductions ajoutées" + "repack_list_updated": "Liste de repacks mise à jour", + "repack_count_one": "{{count}} repack ajouté", + "repack_count_other": "{{count}} repacks ajoutés" }, "system_tray": { "open": "Ouvrir Hydra", From ecac6376d75da72db0732fd73a14e8a8d1e19cf9 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Tue, 23 Apr 2024 21:10:15 -0300 Subject: [PATCH 30/70] feat: use psList on windows (~25ms vs ~320ms) --- src/main/helpers/ps.ts | 7 ------- src/main/services/process-watcher.ts | 17 +++++++++-------- yarn.lock | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/helpers/ps.ts b/src/main/helpers/ps.ts index f7ce3391c..5a30a9edb 100644 --- a/src/main/helpers/ps.ts +++ b/src/main/helpers/ps.ts @@ -1,12 +1,5 @@ import psList from "ps-list"; -import { tasklist } from "tasklist"; export const getProcesses = async () => { - if (process.platform === "win32") { - return tasklist().then((tasks) => - tasks.map((task) => ({ ...task, name: task.imageName })) - ); - } - return psList(); }; diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 96244a607..c67f68158 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { IsNull, Not } from "typeorm"; - +import { exec } from "child_process" import { gameRepository } from "@main/repository"; import { getProcesses } from "@main/helpers"; import { WindowManager } from "./window-manager"; @@ -14,13 +14,21 @@ export const startProcessWatcher = async () => { // eslint-disable-next-line no-constant-condition while (true) { + await sleep(sleepTime); + const games = await gameRepository.find({ where: { executablePath: Not(IsNull()), }, }); + if (games.length == 0) { + continue; + } + + console.time("getProcesses") const processes = await getProcesses(); + console.timeEnd("getProcesses") for (const game of games) { const gameProcess = processes.find((runningProcess) => { @@ -55,15 +63,10 @@ export const startProcessWatcher = async () => { gameRepository.update(game.id, { lastTimePlayed: new Date().toUTCString(), }); - - gamesPlaytime.set(game.id, performance.now()); - await sleep(sleepTime); - continue; } gamesPlaytime.set(game.id, performance.now()); - await sleep(sleepTime); continue; } @@ -73,8 +76,6 @@ export const startProcessWatcher = async () => { WindowManager.mainWindow.webContents.send("on-game-close", game.id); } } - - await sleep(sleepTime); } } }; diff --git a/yarn.lock b/yarn.lock index 8b36bcdb7..c6450c09b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8866,7 +8866,7 @@ proxy-from-env@^1.1.0: ps-list@^8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/ps-list/-/ps-list-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/ps-list/-/ps-list-8.1.1.tgz#9ff1952b26a9a07fcc05270407e60544237ae581" integrity sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ== psl@^1.1.33: From 2675641f7bd70cd22e293d5800d065d5eed6c413 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Tue, 23 Apr 2024 21:42:35 -0300 Subject: [PATCH 31/70] feat: add total loop time log --- src/main/services/process-watcher.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index c67f68158..89e49b511 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -16,6 +16,7 @@ export const startProcessWatcher = async () => { while (true) { await sleep(sleepTime); + console.log("loopTotalTime") const games = await gameRepository.find({ where: { executablePath: Not(IsNull()), @@ -31,13 +32,13 @@ export const startProcessWatcher = async () => { console.timeEnd("getProcesses") for (const game of games) { - const gameProcess = processes.find((runningProcess) => { - const basename = path.win32.basename(game.executablePath); + const basename = path.win32.basename(game.executablePath); const basenameWithoutExtension = path.win32.basename( game.executablePath, path.extname(game.executablePath) ); + const gameProcess = processes.find((runningProcess) => { if (process.platform === "win32") { return runningProcess.name === basename; } From 1620437d1e107520f7c69d63147a9d4f4df32147 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Tue, 23 Apr 2024 21:44:09 -0300 Subject: [PATCH 32/70] format with prettier --- src/main/services/process-watcher.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 89e49b511..2ed46cd00 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -1,7 +1,6 @@ import path from "node:path"; import { IsNull, Not } from "typeorm"; -import { exec } from "child_process" import { gameRepository } from "@main/repository"; import { getProcesses } from "@main/helpers"; import { WindowManager } from "./window-manager"; @@ -16,7 +15,7 @@ export const startProcessWatcher = async () => { while (true) { await sleep(sleepTime); - console.log("loopTotalTime") + console.log("loopTotalTime"); const games = await gameRepository.find({ where: { executablePath: Not(IsNull()), @@ -27,16 +26,16 @@ export const startProcessWatcher = async () => { continue; } - console.time("getProcesses") + console.time("getProcesses"); const processes = await getProcesses(); - console.timeEnd("getProcesses") + console.timeEnd("getProcesses"); for (const game of games) { const basename = path.win32.basename(game.executablePath); - const basenameWithoutExtension = path.win32.basename( - game.executablePath, - path.extname(game.executablePath) - ); + const basenameWithoutExtension = path.win32.basename( + game.executablePath, + path.extname(game.executablePath) + ); const gameProcess = processes.find((runningProcess) => { if (process.platform === "win32") { From 854b23e4ef164d396ada17bb6cedc6af5fd9b067 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Tue, 23 Apr 2024 22:21:07 -0300 Subject: [PATCH 33/70] correctly print time logs --- src/main/services/process-watcher.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 2ed46cd00..c040373ae 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -15,7 +15,7 @@ export const startProcessWatcher = async () => { while (true) { await sleep(sleepTime); - console.log("loopTotalTime"); + console.time("loopTotalTime"); const games = await gameRepository.find({ where: { executablePath: Not(IsNull()), @@ -66,16 +66,13 @@ export const startProcessWatcher = async () => { } gamesPlaytime.set(game.id, performance.now()); - - continue; - } - - if (gamesPlaytime.has(game.id)) { + } else if (gamesPlaytime.has(game.id)) { gamesPlaytime.delete(game.id); if (WindowManager.mainWindow) { WindowManager.mainWindow.webContents.send("on-game-close", game.id); } } } + console.timeEnd("loopTotalTime"); } }; From 0bbd7d013fcf3cf0355783196c61be8115470ac2 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Thu, 25 Apr 2024 21:40:46 -0300 Subject: [PATCH 34/70] feat: run fastfile binary from resources folder feat: run process watcher each 300ms feat: add build step to copy fastlist from node_modules feat: create postinstall script to copy fastlist binary remove debug logs --- .gitignore | 3 +++ package.json | 3 ++- postinstall.py | 5 +++++ src/main/events/library/close-game.ts | 3 ++- src/main/helpers/ps.ts | 31 +++++++++++++++++++++++++-- src/main/services/process-watcher.ts | 9 +++----- 6 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 postinstall.py diff --git a/.gitignore b/.gitignore index 0d0363c87..360a16b45 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,6 @@ resources/dist/ # Sentry Config File .env.sentry-build-plugin + +# Fastlist binary +resources/fastlist.exe diff --git a/package.json b/package.json index 848538982..381dd285f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "make": "electron-forge make", "publish": "electron-forge publish", "lint": "eslint .", - "format": "prettier . --write" + "format": "prettier . --write", + "postinstall": "python3 ./postinstall.py" }, "devDependencies": { "@electron-forge/cli": "^7.3.0", diff --git a/postinstall.py b/postinstall.py new file mode 100644 index 000000000..3fb85d8d7 --- /dev/null +++ b/postinstall.py @@ -0,0 +1,5 @@ +import shutil +import platform + +if platform.system() == "Windows": + shutil.copy("node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe", "resources/fastlist.exe") diff --git a/src/main/events/library/close-game.ts b/src/main/events/library/close-game.ts index 0d556925a..586253b99 100644 --- a/src/main/events/library/close-game.ts +++ b/src/main/events/library/close-game.ts @@ -4,12 +4,13 @@ import { gameRepository } from "@main/repository"; import { registerEvent } from "../register-event"; import { getProcesses } from "@main/helpers"; +import { app } from "electron"; const closeGame = async ( _event: Electron.IpcMainInvokeEvent, gameId: number ) => { - const processes = await getProcesses(); + const processes = await getProcesses(app.isPackaged); const game = await gameRepository.findOne({ where: { id: gameId } }); const gameProcess = processes.find((runningProcess) => { diff --git a/src/main/helpers/ps.ts b/src/main/helpers/ps.ts index 5a30a9edb..449d103fd 100644 --- a/src/main/helpers/ps.ts +++ b/src/main/helpers/ps.ts @@ -1,5 +1,32 @@ import psList from "ps-list"; +import path from "node:path"; +import childProcess from "node:child_process"; +import { promisify } from "node:util"; -export const getProcesses = async () => { - return psList(); +const TEN_MEGABYTES = 1000 * 1000 * 10; +const execFile = promisify(childProcess.execFile); + +export const getProcesses = async (isPackaged: boolean) => { + if (process.platform == "win32") { + const binaryPath = isPackaged + ? path.join(process.resourcesPath, "fastlist.exe") + : path.join(__dirname, "..", "..", "resources", "fastlist.exe"); + + const { stdout } = await execFile(binaryPath, { + maxBuffer: TEN_MEGABYTES, + windowsHide: true, + }); + + return stdout + .trim() + .split("\r\n") + .map((line) => line.split("\t")) + .map(([pid, ppid, name]) => ({ + pid: Number.parseInt(pid, 10), + ppid: Number.parseInt(ppid, 10), + name, + })); + } else { + return psList(); + } }; diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index c040373ae..0668c1db1 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -4,18 +4,18 @@ import { IsNull, Not } from "typeorm"; import { gameRepository } from "@main/repository"; import { getProcesses } from "@main/helpers"; import { WindowManager } from "./window-manager"; +import { app } from "electron"; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const startProcessWatcher = async () => { - const sleepTime = 100; + const sleepTime = 300; const gamesPlaytime = new Map(); // eslint-disable-next-line no-constant-condition while (true) { await sleep(sleepTime); - console.time("loopTotalTime"); const games = await gameRepository.find({ where: { executablePath: Not(IsNull()), @@ -26,9 +26,7 @@ export const startProcessWatcher = async () => { continue; } - console.time("getProcesses"); - const processes = await getProcesses(); - console.timeEnd("getProcesses"); + const processes = await getProcesses(app.isPackaged); for (const game of games) { const basename = path.win32.basename(game.executablePath); @@ -73,6 +71,5 @@ export const startProcessWatcher = async () => { } } } - console.timeEnd("loopTotalTime"); } }; From fd6ab62e13a584198af6345c5c5b5186b8e7ddfd Mon Sep 17 00:00:00 2001 From: Zamitto <> Date: Fri, 26 Apr 2024 00:12:17 -0300 Subject: [PATCH 35/70] add fastlist.exe to extraResources --- forge.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/forge.config.ts b/forge.config.ts index 94cfac577..4e6cdb210 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -30,6 +30,7 @@ const config: ForgeConfig = { executableName: "Hydra", extraResource: [ "./resources/hydra.db", + "./resources/fastlist.exe", "./resources/icon_tray.png", "./resources/dist", ], From 7f7d4a1db3ce94636200493d0899dd0830d2dd0f Mon Sep 17 00:00:00 2001 From: Zamitto Date: Fri, 26 Apr 2024 00:22:28 -0300 Subject: [PATCH 36/70] chage fastlis.exe location to not conflict with linux build --- forge.config.ts | 1 - postinstall.py | 6 +++++- src/main/helpers/ps.ts | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/forge.config.ts b/forge.config.ts index 4e6cdb210..94cfac577 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -30,7 +30,6 @@ const config: ForgeConfig = { executableName: "Hydra", extraResource: [ "./resources/hydra.db", - "./resources/fastlist.exe", "./resources/icon_tray.png", "./resources/dist", ], diff --git a/postinstall.py b/postinstall.py index 3fb85d8d7..c55dca603 100644 --- a/postinstall.py +++ b/postinstall.py @@ -1,5 +1,9 @@ import shutil import platform +import os if platform.system() == "Windows": - shutil.copy("node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe", "resources/fastlist.exe") + if not os.path.exists("resources/dist"): + os.mkdir("resources/dist") + + shutil.copy("node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe", "resources/dist/fastlist.exe") diff --git a/src/main/helpers/ps.ts b/src/main/helpers/ps.ts index 449d103fd..3d83cc7d7 100644 --- a/src/main/helpers/ps.ts +++ b/src/main/helpers/ps.ts @@ -9,8 +9,8 @@ const execFile = promisify(childProcess.execFile); export const getProcesses = async (isPackaged: boolean) => { if (process.platform == "win32") { const binaryPath = isPackaged - ? path.join(process.resourcesPath, "fastlist.exe") - : path.join(__dirname, "..", "..", "resources", "fastlist.exe"); + ? path.join(process.resourcesPath, "dist", "fastlist.exe") + : path.join(__dirname, "..", "..", "resources", "dist", "fastlist.exe"); const { stdout } = await execFile(binaryPath, { maxBuffer: TEN_MEGABYTES, From 2363a6d0b29cb17078875d1cf23df59bacba01fd Mon Sep 17 00:00:00 2001 From: Zamitto Date: Fri, 26 Apr 2024 22:39:25 -0300 Subject: [PATCH 37/70] feat: remove unneeded gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 360a16b45..0d0363c87 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,3 @@ resources/dist/ # Sentry Config File .env.sentry-build-plugin - -# Fastlist binary -resources/fastlist.exe From e15479cd6acdfa957ea731b3450d93e346d12dcb Mon Sep 17 00:00:00 2001 From: Zamitto Date: Fri, 26 Apr 2024 22:42:07 -0300 Subject: [PATCH 38/70] feat: remove tasklist ts declaration and remove tasklist dependency --- package.json | 1 - src/declaration.d.ts | 13 ------------ yarn.lock | 50 -------------------------------------------- 3 files changed, 64 deletions(-) delete mode 100644 src/declaration.d.ts diff --git a/package.json b/package.json index 381dd285f..39029b300 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,6 @@ "react-redux": "^9.1.0", "react-router-dom": "^6.22.3", "sqlite3": "^5.1.7", - "tasklist": "^5.0.0", "tough-cookie": "^4.1.3", "typeorm": "^0.3.20", "update-electron-app": "^3.0.0", diff --git a/src/declaration.d.ts b/src/declaration.d.ts deleted file mode 100644 index 26dfcc27c..000000000 --- a/src/declaration.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -declare module "tasklist" { - interface Task { - imageName: string; - pid: number; - sessionName: string; - sessionNumber: number; - memUsage: number; - } - - function tasklist(): Promise; - - export { tasklist }; -} diff --git a/yarn.lock b/yarn.lock index c6450c09b..78c336e04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4611,31 +4611,6 @@ csstype@^3.0.2, csstype@^3.0.7: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -csv-generate@^3.4.3: - version "3.4.3" - resolved "https://registry.npmjs.org/csv-generate/-/csv-generate-3.4.3.tgz" - integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== - -csv-parse@^4.16.3: - version "4.16.3" - resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz" - integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== - -csv-stringify@^5.6.5: - version "5.6.5" - resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz" - integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== - -csv@^5.5.0: - version "5.5.3" - resolved "https://registry.npmjs.org/csv/-/csv-5.5.3.tgz" - integrity sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== - dependencies: - csv-generate "^3.4.3" - csv-parse "^4.16.3" - csv-stringify "^5.6.5" - stream-transform "^2.1.3" - cycle@1.0.x: version "1.0.3" resolved "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz" @@ -7936,11 +7911,6 @@ minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mixme@^0.5.1: - version "0.5.10" - resolved "https://registry.npmjs.org/mixme/-/mixme-0.5.10.tgz" - integrity sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q== - mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" @@ -9463,11 +9433,6 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -sec@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/sec/-/sec-2.0.0.tgz" - integrity sha512-uq35HWa7mG6YyojrduMXjF8UhOySEf3X0V1uMpSOBYUF09xAMnJaKNSmWXeE3mN7NfJTpNUkmGa6nIpEBMN8Xw== - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" @@ -9882,13 +9847,6 @@ statuses@2.0.1: resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -stream-transform@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.3.tgz" - integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== - dependencies: - mixme "^0.5.1" - "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -10144,14 +10102,6 @@ tar@^6.0.2, tar@^6.0.5, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -tasklist@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/tasklist/-/tasklist-5.0.0.tgz" - integrity sha512-qPB4J6pseXRqdxAFT1GhlvDPv4FHxWkXs8QVYQWIqusGwn7UXVKOoYu09DZuYWe1K7T5iusHfSoKrv8k9+RfxA== - dependencies: - csv "^5.5.0" - sec "^2.0.0" - temp-dir@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz" From 9b9eabd8011fef3b99b5d6cefdc0524ece053004 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Fri, 26 Apr 2024 23:02:21 -0300 Subject: [PATCH 39/70] migrate postinstall from py to js --- .eslintignore | 1 + package.json | 2 +- postinstall.js | 9 +++++++++ postinstall.py | 9 --------- 4 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 .eslintignore create mode 100644 postinstall.js delete mode 100644 postinstall.py diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..0854696dc --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +postinstall.js \ No newline at end of file diff --git a/package.json b/package.json index 39029b300..b83967133 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "publish": "electron-forge publish", "lint": "eslint .", "format": "prettier . --write", - "postinstall": "python3 ./postinstall.py" + "postinstall": "node ./postinstall.js" }, "devDependencies": { "@electron-forge/cli": "^7.3.0", diff --git a/postinstall.js b/postinstall.js new file mode 100644 index 000000000..63b6399eb --- /dev/null +++ b/postinstall.js @@ -0,0 +1,9 @@ +const fs = require("fs") + +if (process.platform === "win32"){ + if (!fs.existsSync("resources/dist")) { + fs.mkdirSync("resources/dist") + } + + fs.copyFileSync("node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe", "resources/dist/fastlist.exe") +} diff --git a/postinstall.py b/postinstall.py deleted file mode 100644 index c55dca603..000000000 --- a/postinstall.py +++ /dev/null @@ -1,9 +0,0 @@ -import shutil -import platform -import os - -if platform.system() == "Windows": - if not os.path.exists("resources/dist"): - os.mkdir("resources/dist") - - shutil.copy("node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe", "resources/dist/fastlist.exe") From a4edff36191a80b4503305368540290782879174 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Fri, 26 Apr 2024 23:02:53 -0300 Subject: [PATCH 40/70] use app.isPackaged directly inside getProcesses function --- src/main/events/library/close-game.ts | 3 +-- src/main/helpers/ps.ts | 5 +++-- src/main/services/process-watcher.ts | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/events/library/close-game.ts b/src/main/events/library/close-game.ts index 586253b99..0d556925a 100644 --- a/src/main/events/library/close-game.ts +++ b/src/main/events/library/close-game.ts @@ -4,13 +4,12 @@ import { gameRepository } from "@main/repository"; import { registerEvent } from "../register-event"; import { getProcesses } from "@main/helpers"; -import { app } from "electron"; const closeGame = async ( _event: Electron.IpcMainInvokeEvent, gameId: number ) => { - const processes = await getProcesses(app.isPackaged); + const processes = await getProcesses(); const game = await gameRepository.findOne({ where: { id: gameId } }); const gameProcess = processes.find((runningProcess) => { diff --git a/src/main/helpers/ps.ts b/src/main/helpers/ps.ts index 3d83cc7d7..dbc11f09c 100644 --- a/src/main/helpers/ps.ts +++ b/src/main/helpers/ps.ts @@ -2,13 +2,14 @@ import psList from "ps-list"; import path from "node:path"; import childProcess from "node:child_process"; import { promisify } from "node:util"; +import { app } from "electron"; const TEN_MEGABYTES = 1000 * 1000 * 10; const execFile = promisify(childProcess.execFile); -export const getProcesses = async (isPackaged: boolean) => { +export const getProcesses = async () => { if (process.platform == "win32") { - const binaryPath = isPackaged + const binaryPath = app.isPackaged ? path.join(process.resourcesPath, "dist", "fastlist.exe") : path.join(__dirname, "..", "..", "resources", "dist", "fastlist.exe"); diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 0668c1db1..0d699165a 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -4,7 +4,6 @@ import { IsNull, Not } from "typeorm"; import { gameRepository } from "@main/repository"; import { getProcesses } from "@main/helpers"; import { WindowManager } from "./window-manager"; -import { app } from "electron"; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -26,7 +25,7 @@ export const startProcessWatcher = async () => { continue; } - const processes = await getProcesses(app.isPackaged); + const processes = await getProcesses(); for (const game of games) { const basename = path.win32.basename(game.executablePath); From bcfa3bb795906bb17e1a8529358362a425a629ee Mon Sep 17 00:00:00 2001 From: Zamitto Date: Fri, 26 Apr 2024 23:04:57 -0300 Subject: [PATCH 41/70] add new line to .eslintignore --- .eslintignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index 0854696dc..289e7a420 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1 @@ -postinstall.js \ No newline at end of file +postinstall.js From 6132ddd2b77d37e4a11522038cb4e5aa054ae8bc Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 18:40:05 -0300 Subject: [PATCH 42/70] feat: get all games on first request --- src/main/events/catalogue/get-random-game.ts | 39 ++++++++++++++------ src/main/services/steam-250.ts | 13 +++++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts index 07a827dd9..d4bfd6af0 100644 --- a/src/main/events/catalogue/get-random-game.ts +++ b/src/main/events/catalogue/get-random-game.ts @@ -1,27 +1,44 @@ import shuffle from "lodash/shuffle"; -import { getRandomSteam250List } from "@main/services"; +import { Steam250Game, getSteam250List } from "@main/services"; import { registerEvent } from "../register-event"; import { searchGames, searchRepacks } from "../helpers/search-games"; import { formatName } from "@main/helpers"; +let gamesList = new Array(); +let nextGameIndex = 0; + const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { - return getRandomSteam250List().then(async (games) => { - const shuffledList = shuffle(games); + if (gamesList.length == 0) { + console.log("fetching steam 250 pages"); + gamesList = shuffle(await getSteam250List()); + } else { + console.log("getting cached list"); + } + + let resultObjectId = ""; - for (const game of shuffledList) { - const repacks = searchRepacks(formatName(game.title)); + while (!resultObjectId) { + const game = gamesList[nextGameIndex]; + const repacks = searchRepacks(formatName(game.title)); - if (repacks.length) { - const results = await searchGames({ query: game.title }); + if (repacks.length) { + const results = await searchGames({ query: game.title }); - if (results.length) { - return results[0].objectID; - } + if (results.length) { + resultObjectId = results[0].objectID; } } - }); + nextGameIndex += 1; + + if (nextGameIndex == gamesList.length - 1) { + nextGameIndex = 0; + gamesList = shuffle(gamesList); + } + } + + return resultObjectId; }; registerEvent(getRandomGame, { diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index 6447c2265..079f067ff 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -1,6 +1,10 @@ import axios from "axios"; import { JSDOM } from "jsdom"; -import shuffle from "lodash/shuffle"; + +export interface Steam250Game { + title: string; + objectID: string; +} export const requestSteam250 = async (path: string) => { return axios.get(`https://steam250.com${path}`).then((response) => { @@ -28,7 +32,8 @@ const steam250Paths = [ "/most_played", ]; -export const getRandomSteam250List = async () => { - const [path] = shuffle(steam250Paths); - return requestSteam250(path); +export const getSteam250List = async () => { + const gamesPromises = steam250Paths.map((path) => requestSteam250(path)); + const gamesList = (await Promise.all(gamesPromises)).flat(); + return [...new Set(gamesList)]; }; From c9d678be1126862182ba5233fd47ef84d0de940e Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 18:41:37 -0300 Subject: [PATCH 43/70] feat: game-details call getRandomCall on button click --- .../pages/game-details/game-details.tsx | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index 7f7441989..9015823f3 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -53,18 +53,10 @@ export function GameDetails() { const [showRepacksModal, setShowRepacksModal] = useState(false); const [showSelectFolderModal, setShowSelectFolderModal] = useState(false); - const randomGameObjectID = useRef(null); - const dispatch = useAppDispatch(); const { game: gameDownloading, startDownload, isDownloading } = useDownload(); - const getRandomGame = useCallback(() => { - window.electron.getRandomGame().then((objectID) => { - randomGameObjectID.current = objectID; - }); - }, []); - const handleImageSettled = useCallback((url: string) => { average(url, { amount: 1, format: "hex" }) .then((color) => { @@ -89,8 +81,6 @@ export function GameDetails() { setIsGamePlaying(false); dispatch(setHeaderTitle("")); - getRandomGame(); - window.electron .getGameShopDetails(objectID, "steam", getSteamLanguage(i18n.language)) .then((result) => { @@ -114,7 +104,7 @@ export function GameDetails() { getGame(); setHowLongToBeat({ isLoading: true, data: null }); - }, [getGame, getRandomGame, dispatch, navigate, objectID, i18n.language]); + }, [getGame, dispatch, navigate, objectID, i18n.language]); const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; @@ -158,16 +148,14 @@ export function GameDetails() { }); }; - const handleRandomizerClick = () => { - if (!randomGameObjectID.current) return; + const handleRandomizerClick = async () => { + const randomGameObjectID = await window.electron.getRandomGame(); const searchParams = new URLSearchParams({ fromRandomizer: "1", }); - navigate( - `/game/steam/${randomGameObjectID.current}?${searchParams.toString()}` - ); + navigate(`/game/steam/${randomGameObjectID}?${searchParams.toString()}`); }; const fromRandomizer = searchParams.get("fromRandomizer"); From 170a58d3cd40b50660726305a4346c170599be7d Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 19:54:37 -0300 Subject: [PATCH 44/70] feat: adjustments --- src/main/events/catalogue/get-random-game.ts | 10 +++++----- src/main/services/steam-250.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts index d4bfd6af0..c7484d6ba 100644 --- a/src/main/events/catalogue/get-random-game.ts +++ b/src/main/events/catalogue/get-random-game.ts @@ -6,7 +6,7 @@ import { registerEvent } from "../register-event"; import { searchGames, searchRepacks } from "../helpers/search-games"; import { formatName } from "@main/helpers"; -let gamesList = new Array(); +let gamesList: Steam250Game[] = []; let nextGameIndex = 0; const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { @@ -24,15 +24,15 @@ const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { const repacks = searchRepacks(formatName(game.title)); if (repacks.length) { - const results = await searchGames({ query: game.title }); + const catalogueResults = await searchGames({ query: game.title }); - if (results.length) { - resultObjectId = results[0].objectID; + if (catalogueResults.length) { + resultObjectId = catalogueResults[0].objectID; } } nextGameIndex += 1; - if (nextGameIndex == gamesList.length - 1) { + if (nextGameIndex == gamesList.length) { nextGameIndex = 0; gamesList = shuffle(gamesList); } diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index 079f067ff..dc84cb218 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -11,17 +11,17 @@ export const requestSteam250 = async (path: string) => { const { window } = new JSDOM(response.data); const { document } = window; - return Array.from(document.querySelectorAll(".appline .title a")).map( - ($title: HTMLAnchorElement) => { + return Array.from(document.querySelectorAll(".appline .title a")) + .map(($title: HTMLAnchorElement) => { const steamGameUrl = $title.href; if (!steamGameUrl) return null; return { title: $title.textContent, objectID: steamGameUrl.split("/").pop(), - }; - } - ); + } as Steam250Game; + }) + .filter((game) => game != null); }); }; From 5d8813a78f163f52eab44ec4ac850b759462fbf2 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 20:15:24 -0300 Subject: [PATCH 45/70] fix: possible bug when catalogue returns games with similar name --- src/main/events/catalogue/get-random-game.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts index c7484d6ba..fc7cb5db3 100644 --- a/src/main/events/catalogue/get-random-game.ts +++ b/src/main/events/catalogue/get-random-game.ts @@ -27,7 +27,7 @@ const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { const catalogueResults = await searchGames({ query: game.title }); if (catalogueResults.length) { - resultObjectId = catalogueResults[0].objectID; + resultObjectId = game.objectID; } } nextGameIndex += 1; From 74a5d5c7f4941af2ea92e4e674050f0149bce4f9 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 20:42:28 -0300 Subject: [PATCH 46/70] replace Set with Map --- src/main/services/steam-250.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index dc84cb218..cec70774e 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -35,5 +35,11 @@ const steam250Paths = [ export const getSteam250List = async () => { const gamesPromises = steam250Paths.map((path) => requestSteam250(path)); const gamesList = (await Promise.all(gamesPromises)).flat(); - return [...new Set(gamesList)]; + + const gamesMap = gamesList.reduce((map, item) => { + map.set(item.objectID, item); + return map; + }, new Map()); + + return [...gamesMap.values()]; }; From 2904302c81ab44d6ed31214f1f9d18dbb440127b Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 20:42:50 -0300 Subject: [PATCH 47/70] renaming variables --- src/main/events/catalogue/get-random-game.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts index fc7cb5db3..d3f82590b 100644 --- a/src/main/events/catalogue/get-random-game.ts +++ b/src/main/events/catalogue/get-random-game.ts @@ -20,14 +20,13 @@ const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { let resultObjectId = ""; while (!resultObjectId) { - const game = gamesList[nextGameIndex]; - const repacks = searchRepacks(formatName(game.title)); + const nextGame = gamesList[nextGameIndex]; + const repacks = searchRepacks(formatName(nextGame.title)); if (repacks.length) { - const catalogueResults = await searchGames({ query: game.title }); - + const catalogueResults = await searchGames({ query: nextGame.title }); if (catalogueResults.length) { - resultObjectId = game.objectID; + resultObjectId = nextGame.objectID; } } nextGameIndex += 1; From a8b236e02cce3065c1c75c3018f77506b8f4d342 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 20:43:34 -0300 Subject: [PATCH 48/70] fix possibility of 404 on home if getRandomGame has no validy elements to return this can happen when user has no internet connection when opening hydra --- src/main/services/steam-250.ts | 35 +++++++++++++++++--------------- src/renderer/pages/home/home.tsx | 10 ++++----- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index cec70774e..28c27046a 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -7,22 +7,25 @@ export interface Steam250Game { } export const requestSteam250 = async (path: string) => { - return axios.get(`https://steam250.com${path}`).then((response) => { - const { window } = new JSDOM(response.data); - const { document } = window; - - return Array.from(document.querySelectorAll(".appline .title a")) - .map(($title: HTMLAnchorElement) => { - const steamGameUrl = $title.href; - if (!steamGameUrl) return null; - - return { - title: $title.textContent, - objectID: steamGameUrl.split("/").pop(), - } as Steam250Game; - }) - .filter((game) => game != null); - }); + return axios + .get(`https://steam250.com${path}`) + .then((response) => { + const { window } = new JSDOM(response.data); + const { document } = window; + + return Array.from(document.querySelectorAll(".appline .title a")) + .map(($title: HTMLAnchorElement) => { + const steamGameUrl = $title.href; + if (!steamGameUrl) return null; + + return { + title: $title.textContent, + objectID: steamGameUrl.split("/").pop(), + } as Steam250Game; + }) + .filter((game) => game != null); + }) + .catch((_) => []); }; const steam250Paths = [ diff --git a/src/renderer/pages/home/home.tsx b/src/renderer/pages/home/home.tsx index ed6502450..33811483a 100644 --- a/src/renderer/pages/home/home.tsx +++ b/src/renderer/pages/home/home.tsx @@ -58,14 +58,12 @@ export function Home() { const getRandomGame = useCallback(() => { setIsLoadingRandomGame(true); - window.electron - .getRandomGame() - .then((objectID) => { + window.electron.getRandomGame().then((objectID) => { + if (objectID) { randomGameObjectID.current = objectID; - }) - .finally(() => { setIsLoadingRandomGame(false); - }); + } + }); }, []); const handleRandomizerClick = () => { From 5f3dc5d0ea0dba0ceaf4d3115daad78023c692c9 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 21:00:57 -0300 Subject: [PATCH 49/70] feat: disable next random suggestion button until game details finish loading --- src/renderer/pages/game-details/game-details.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index 9015823f3..2dc444183 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -33,6 +33,7 @@ export function GameDetails() { const { objectID, shop } = useParams(); const [isLoading, setIsLoading] = useState(false); + const [isLoadingRandomGame, setIsLoadingRandomGame] = useState(false); const [color, setColor] = useState(""); const [gameDetails, setGameDetails] = useState(null); const [howLongToBeat, setHowLongToBeat] = useState<{ @@ -97,6 +98,7 @@ export function GameDetails() { setGameDetails(result); dispatch(setHeaderTitle(result.name)); + setIsLoadingRandomGame(false); }) .finally(() => { setIsLoading(false); @@ -149,6 +151,7 @@ export function GameDetails() { }; const handleRandomizerClick = async () => { + setIsLoadingRandomGame(true); const randomGameObjectID = await window.electron.getRandomGame(); const searchParams = new URLSearchParams({ @@ -269,6 +272,7 @@ export function GameDetails() { className={styles.randomizerButton} onClick={handleRandomizerClick} theme="outline" + disabled={isLoadingRandomGame} >
Date: Sat, 27 Apr 2024 22:04:35 -0300 Subject: [PATCH 50/70] removing async from searchGames --- src/main/events/helpers/search-games.ts | 15 +++++++-------- src/main/services/steam-250.ts | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/events/helpers/search-games.ts b/src/main/events/helpers/search-games.ts index 5fb3cea0f..6d6f1dedc 100644 --- a/src/main/events/helpers/search-games.ts +++ b/src/main/events/helpers/search-games.ts @@ -42,11 +42,12 @@ export interface SearchGamesArgs { skip?: number; } -export const searchGames = async ({ +// Check if this function really needed to be an async function +export const searchGames = ({ query, take, skip, -}: SearchGamesArgs): Promise => { +}: SearchGamesArgs): CatalogueEntry[] => { const results = steamGamesIndex .search(formatName(query || ""), { limit: take, offset: skip }) .map((index) => { @@ -61,11 +62,9 @@ export const searchGames = async ({ }; }); - return Promise.all(results).then((resultsWithRepacks) => - orderBy( - resultsWithRepacks, - [({ repacks }) => repacks.length, "repacks"], - ["desc"] - ) + return orderBy( + results, + [({ repacks }) => repacks.length, "repacks"], + ["desc"] ); }; diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index 28c27046a..f0aee3bfb 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -39,10 +39,10 @@ export const getSteam250List = async () => { const gamesPromises = steam250Paths.map((path) => requestSteam250(path)); const gamesList = (await Promise.all(gamesPromises)).flat(); - const gamesMap = gamesList.reduce((map, item) => { + const gamesMap: Map = gamesList.reduce((map, item) => { map.set(item.objectID, item); return map; - }, new Map()); + }, new Map()); return [...gamesMap.values()]; }; From 4ffaa510ccd233425108a1d121cbf176864b02d8 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sat, 27 Apr 2024 22:04:42 -0300 Subject: [PATCH 51/70] refactor get-random-game --- src/main/events/catalogue/get-random-game.ts | 38 ++++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts index d3f82590b..82a7966b6 100644 --- a/src/main/events/catalogue/get-random-game.ts +++ b/src/main/events/catalogue/get-random-game.ts @@ -4,37 +4,35 @@ import { Steam250Game, getSteam250List } from "@main/services"; import { registerEvent } from "../register-event"; import { searchGames, searchRepacks } from "../helpers/search-games"; -import { formatName } from "@main/helpers"; let gamesList: Steam250Game[] = []; let nextGameIndex = 0; const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { if (gamesList.length == 0) { - console.log("fetching steam 250 pages"); - gamesList = shuffle(await getSteam250List()); - } else { - console.log("getting cached list"); + const steam250List = await getSteam250List(); + + const filteredSteam250List = steam250List.filter((game) => { + const repacks = searchRepacks(game.title); + const catalogue = searchGames({ query: game.title }); + + return repacks.length && catalogue.length; + }); + + gamesList = shuffle(filteredSteam250List); } - let resultObjectId = ""; + if (gamesList.length == 0) { + return ""; + } - while (!resultObjectId) { - const nextGame = gamesList[nextGameIndex]; - const repacks = searchRepacks(formatName(nextGame.title)); + const resultObjectId = gamesList[nextGameIndex].objectID; - if (repacks.length) { - const catalogueResults = await searchGames({ query: nextGame.title }); - if (catalogueResults.length) { - resultObjectId = nextGame.objectID; - } - } - nextGameIndex += 1; + nextGameIndex += 1; - if (nextGameIndex == gamesList.length) { - nextGameIndex = 0; - gamesList = shuffle(gamesList); - } + if (nextGameIndex == gamesList.length) { + nextGameIndex = 0; + gamesList = shuffle(gamesList); } return resultObjectId; From bb9302908d070cb3cd2f79db79fc07039d8d10f9 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sun, 28 Apr 2024 00:13:29 -0300 Subject: [PATCH 52/70] make sure the registed event for seachGames returns a Promise --- src/main/events/catalogue/search-games.ts | 20 ++++++++++++-------- src/main/events/helpers/search-games.ts | 1 - src/main/services/steam-250.ts | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/events/catalogue/search-games.ts b/src/main/events/catalogue/search-games.ts index eb9c06403..cdb84d568 100644 --- a/src/main/events/catalogue/search-games.ts +++ b/src/main/events/catalogue/search-games.ts @@ -1,11 +1,15 @@ import { registerEvent } from "../register-event"; import { searchGames } from "../helpers/search-games"; +import { CatalogueEntry } from "@types"; -registerEvent( - (_event: Electron.IpcMainInvokeEvent, query: string) => - searchGames({ query, take: 12 }), - { - name: "searchGames", - memoize: true, - } -); +const searchGamesEvent = async ( + _event: Electron.IpcMainInvokeEvent, + query: string +): Promise => { + return Promise.all(searchGames({ query, take: 12 })); +}; + +registerEvent(searchGamesEvent, { + name: "searchGames", + memoize: true, +}); diff --git a/src/main/events/helpers/search-games.ts b/src/main/events/helpers/search-games.ts index 6d6f1dedc..50777dd73 100644 --- a/src/main/events/helpers/search-games.ts +++ b/src/main/events/helpers/search-games.ts @@ -42,7 +42,6 @@ export interface SearchGamesArgs { skip?: number; } -// Check if this function really needed to be an async function export const searchGames = ({ query, take, diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index f0aee3bfb..f5c923647 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -25,7 +25,7 @@ export const requestSteam250 = async (path: string) => { }) .filter((game) => game != null); }) - .catch((_) => []); + .catch((_) => [] as Steam250Game[]); }; const steam250Paths = [ From 632cd244b02a51bfe3e988164e09100637b2bf04 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Sun, 28 Apr 2024 00:28:12 -0300 Subject: [PATCH 53/70] remove unused import and fix lint --- src/renderer/pages/game-details/game-details.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/pages/game-details/game-details.tsx b/src/renderer/pages/game-details/game-details.tsx index 2dc444183..41ba215e6 100644 --- a/src/renderer/pages/game-details/game-details.tsx +++ b/src/renderer/pages/game-details/game-details.tsx @@ -1,6 +1,6 @@ import Color from "color"; import { average } from "color.js"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import type { From a4df6e9babe75bff40746f7fc562adc0815411a3 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sun, 28 Apr 2024 05:59:06 +0100 Subject: [PATCH 54/70] feat: adding tray icon --- .gitignore | 3 + electron.vite.config.ts | 27 +++- package.json | 1 + resources/tray-icon.png | Bin 0 -> 178866 bytes src/main/services/window-manager.ts | 2 +- yarn.lock | 222 ++++++++++++++++++++++++++-- 6 files changed, 241 insertions(+), 14 deletions(-) create mode 100644 resources/tray-icon.png diff --git a/.gitignore b/.gitignore index 3cb8d1cad..0767dd62a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ out *.log* .env .vite + +# Sentry Config File +.env.sentry-build-plugin diff --git a/electron.vite.config.ts b/electron.vite.config.ts index d3265042f..3bb8eb017 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -8,14 +8,21 @@ import { } from "electron-vite"; import react from "@vitejs/plugin-react"; import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin"; +import { sentryVitePlugin } from "@sentry/vite-plugin"; import svgr from "vite-plugin-svgr"; - export default defineConfig(({ mode }) => { loadEnv(mode); + const sentryPlugin = sentryVitePlugin({ + authToken: process.env.SENTRY_AUTH_TOKEN, + org: "hydra-launcher", + project: "hydra-launcher", + }); + return { main: { build: { + sourcemap: true, rollupOptions: { external: ["better-sqlite3"], }, @@ -26,19 +33,33 @@ export default defineConfig(({ mode }) => { "@locales": resolve("src/locales"), }, }, - plugins: [externalizeDepsPlugin(), swcPlugin(), bytecodePlugin()], + plugins: [ + externalizeDepsPlugin(), + swcPlugin(), + bytecodePlugin(), + sentryPlugin, + ], }, preload: { plugins: [externalizeDepsPlugin()], }, renderer: { + build: { + sourcemap: true, + }, resolve: { alias: { "@renderer": resolve("src/renderer/src"), "@locales": resolve("src/locales"), }, }, - plugins: [svgr(), react(), vanillaExtractPlugin(), bytecodePlugin()], + plugins: [ + svgr(), + react(), + vanillaExtractPlugin(), + bytecodePlugin(), + sentryPlugin, + ], }, }; }); diff --git a/package.json b/package.json index 60f05f455..df94702fb 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@reduxjs/toolkit": "^2.2.3", "@sentry/electron": "^4.23.0", "@sentry/react": "^7.111.0", + "@sentry/vite-plugin": "^2.16.1", "@vanilla-extract/css": "^1.14.2", "@vanilla-extract/recipes": "^0.5.2", "axios": "^1.6.8", diff --git a/resources/tray-icon.png b/resources/tray-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b4fb0394b0b75928ed61b8d601ec060a6039ea GIT binary patch literal 178866 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

34pb2v+K$l~EU!^2I4%a!>RDAJ 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%0Aj_JURON@BWT>) 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 Date: Mon, 29 Apr 2024 00:43:08 +0900 Subject: [PATCH 55/70] Update README.md minor fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cdde54606..2ba817f36 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ pip install -r requirements.txt ## Environment variables -You'll need an SteamGridDB API Key in order to fetch the game icons on installation. +You'll need a SteamGridDB API Key in order to fetch the game icons on installation. If you want to have onlinefix as a repacker you'll need to add your credentials to the .env Once you have it, you can paste the `.env.example` file and put it on `STEAMGRIDDB_API_KEY`, `ONLINEFIX_USERNAME`, `ONLINEFIX_PASSWORD`. From 5bec457ba621307340da2980fc3cd76ab97cf5c7 Mon Sep 17 00:00:00 2001 From: Hydra Date: Sun, 28 Apr 2024 19:30:03 +0100 Subject: [PATCH 56/70] fix: renaming extension of postinstall script --- package.json | 2 +- src/main/main.ts | 7 + yarn.lock | 382 ++--------------------------------------------- 3 files changed, 21 insertions(+), 370 deletions(-) diff --git a/package.json b/package.json index 5ed1c74fa..6eed5a1dd 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "start": "electron-vite preview", "dev": "electron-vite dev", "build": "npm run typecheck && electron-vite build", - "postinstall": "electron-builder install-app-deps && node ./postinstall.js", + "postinstall": "electron-builder install-app-deps && node ./postinstall.cjs", "build:unpack": "npm run build && electron-builder --dir", "build:win": "npm run build && electron-builder --win", "build:mac": "electron-vite build && electron-builder --mac", diff --git a/src/main/main.ts b/src/main/main.ts index eb853ae6c..d09f9c2fd 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -22,6 +22,13 @@ import { Repack } from "./entity"; import { Notification } from "electron"; import { t } from "i18next"; import { In } from "typeorm"; +import creatWorker from "./workers/test?nodeWorker"; + +creatWorker({ workerData: "worker" }) + .on("message", (message) => { + console.log(`\nMessage from worker: ${message}`); + }) + .postMessage(""); startProcessWatcher(); diff --git a/yarn.lock b/yarn.lock index 51dd65714..638ce44f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -623,11 +623,6 @@ resolved "https://registry.yarnpkg.com/@fontsource/fira-sans/-/fira-sans-5.0.20.tgz#92d22b1289f932db1bca7bd20902544fb9706252" integrity sha512-inmUjoKPrgnO4uUaZTAgP0b6YdhDfA52axHXvdTwgLvwd2kn3ZgK52UZoxD0VnrvTOjLA/iE4oC0tNtz4nyb5g== -"@gar/promisify@^1.0.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -729,22 +724,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/fs@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" - integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== - dependencies: - "@gar/promisify" "^1.0.1" - semver "^7.3.5" - -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -1344,11 +1323,6 @@ dependencies: uint8-util "^2.1.9" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -1707,11 +1681,6 @@ resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -1734,7 +1703,7 @@ adm-zip@^0.5.9: resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.12.tgz#87786328e91d54b37358d8a50f954c4cd73ba60b" integrity sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ== -agent-base@6, agent-base@^6.0.2: +agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -1748,21 +1717,6 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" -agentkeepalive@^4.1.3: - version "4.5.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" - integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== - dependencies: - humanize-ms "^1.2.1" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - ajv-keywords@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -1863,19 +1817,6 @@ app-root-path@^3.1.0: resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.1.0.tgz#5971a2fc12ba170369a7a1ef018c71e6e47c2e86" integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA== -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" - integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -2190,30 +2131,6 @@ cac@^6.7.14: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== -cacache@^15.2.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" - integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== - dependencies: - "@npmcli/fs" "^1.0.0" - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -2343,11 +2260,6 @@ classnames@^2.5.1: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - cli-highlight@^2.1.11: version "2.1.11" resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" @@ -2425,11 +2337,6 @@ color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - color.js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/color.js/-/color.js-1.2.0.tgz#18d9f55545111730d25ccf18ea8b6933c71440d7" @@ -2486,11 +2393,6 @@ config-file-ts@^0.2.4: glob "^10.3.10" typescript "^5.3.3" -console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -2607,7 +2509,7 @@ dayjs@^1.11.9: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2679,11 +2581,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - detect-libc@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" @@ -2871,13 +2768,6 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== -encoding@^0.1.12: - version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -3499,20 +3389,6 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - generative-bayesian-network@^2.1.50: version "2.1.50" resolved "https://registry.yarnpkg.com/generative-bayesian-network/-/generative-bayesian-network-2.1.50.tgz#a576130befe0e30ccfebe5280fb2550649abadc9" @@ -3598,7 +3474,7 @@ glob@^10.3.10: minipass "^7.0.4" path-scurry "^1.10.2" -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.3, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3717,7 +3593,7 @@ got@^13.0.0: p-cancelable "^3.0.0" responselike "^3.0.0" -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: +graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -3766,11 +3642,6 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -3821,20 +3692,11 @@ html-parse-stringify@^3.0.1: dependencies: void-elements "3.1.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -3884,13 +3746,6 @@ https-proxy-agent@^7.0.2: agent-base "^7.0.2" debug "4" -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - i18next-browser-languagedetector@^7.2.1: version "7.2.1" resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz#1968196d437b4c8db847410c7c33554f6c448f6f" @@ -3953,16 +3808,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3990,14 +3835,6 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" -ip-address@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" - integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== - dependencies: - jsbn "1.1.0" - sprintf-js "^1.1.3" - is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -4109,11 +3946,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-lambda@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" - integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== - is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" @@ -4284,11 +4116,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== - jsdom@^24.0.0: version "24.0.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.0.0.tgz#e2dc04e4c79da368481659818ee2b0cd7c39007c" @@ -4538,28 +4365,6 @@ magnet-uri@^7.0.5: bep53-range "^2.0.0" uint8-util "^2.1.9" -make-fetch-happen@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" - integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== - dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.2" - promise-retry "^2.0.1" - socks-proxy-agent "^6.0.0" - ssri "^8.0.0" - matcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" @@ -4659,46 +4464,7 @@ minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-fetch@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" - integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== - dependencies: - minipass "^3.1.0" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass-sized@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" - integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== - dependencies: - minipass "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: +minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -4720,7 +4486,7 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -minizlib@^2.0.0, minizlib@^2.1.1: +minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -4733,7 +4499,7 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -4763,7 +4529,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -4792,11 +4558,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@^0.6.2: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -4817,11 +4578,6 @@ node-addon-api@^1.6.3: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== -node-addon-api@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" - integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== - node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -4843,34 +4599,11 @@ node-fetch@^3.3.0: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-gyp@8.x: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - node-releases@^2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -4886,16 +4619,6 @@ normalize-url@^8.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a" integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== -npmlog@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== - dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.3" - set-blocking "^2.0.0" - nwsapi@^2.2.7: version "2.2.9" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.9.tgz#7f3303218372db2e9f27c27766bcfc59ae7e61c6" @@ -5040,13 +4763,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -5225,11 +4941,6 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - promise-retry@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" @@ -5640,11 +5351,6 @@ serialize-error@^7.0.1: dependencies: type-fest "^0.13.1" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -5697,11 +5403,6 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" @@ -5749,7 +5450,7 @@ slice-ansi@^3.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -smart-buffer@^4.0.2, smart-buffer@^4.2.0: +smart-buffer@^4.0.2: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== @@ -5762,23 +5463,6 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -socks-proxy-agent@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" - integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== - dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" - -socks@^2.6.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" - integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== - dependencies: - ip-address "^9.0.5" - smart-buffer "^4.2.0" - source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" @@ -5797,30 +5481,11 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sprintf-js@^1.1.2, sprintf-js@^1.1.3: +sprintf-js@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== -sqlite3@^5.1.7: - version "5.1.7" - resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.7.tgz#59ca1053c1ab38647396586edad019b1551041b7" - integrity sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog== - dependencies: - bindings "^1.5.0" - node-addon-api "^7.0.0" - prebuild-install "^7.1.1" - tar "^6.1.11" - optionalDependencies: - node-gyp "8.x" - -ssri@^8.0.0, ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" @@ -5840,7 +5505,7 @@ stat-mode@^1.0.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6007,7 +5672,7 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.2, tar@^6.1.11, tar@^6.1.12, tar@^6.1.2: +tar@^6.1.12: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -6245,20 +5910,6 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -6494,13 +6145,6 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - windows-1251@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/windows-1251/-/windows-1251-3.0.4.tgz#984b9f2e76befd9ec2e825f9fe77b681fadcdb55" From 3b5f3da2ab4148623aaf294b9a4211c3cdcf7139 Mon Sep 17 00:00:00 2001 From: Hydra Date: Mon, 29 Apr 2024 11:01:34 +0100 Subject: [PATCH 57/70] ci: testing pipeline --- .github/workflows/build.yml | 72 +- electron.vite.config.ts | 1 + package.json | 3 +- src/main/events/library/close-game.ts | 18 +- src/main/main.ts | 15 +- src/main/services/repack-tracker/1337x.ts | 5 +- .../services/repack-tracker/cpg-repacks.ts | 3 +- src/main/services/repack-tracker/gog.ts | 53 +- .../services/repack-tracker/online-fix.ts | 3 +- src/main/services/repack-tracker/xatab.ts | 58 +- src/main/services/window-manager.ts | 4 +- src/main/workers/torrent-parser.worker.ts | 17 + src/preload/index.d.ts | 3 +- src/preload/index.ts | 3 +- src/renderer/index.html | 3 +- .../components/bottom-panel/bottom-panel.tsx | 2 +- src/renderer/src/components/modal/modal.tsx | 14 +- src/renderer/src/declaration.d.ts | 2 +- .../src/features/use-preferences-slice.ts | 5 +- src/renderer/src/hooks/use-download.ts | 10 +- .../src/pages/downloads/downloads.tsx | 8 +- .../pages/game-details/hero-panel-actions.tsx | 6 +- .../game-details/select-folder-modal.tsx | 2 +- .../src/pages/home/search-results.tsx | 4 +- src/renderer/src/pages/settings/settings.tsx | 6 +- tsconfig.node.json | 3 +- yarn.lock | 2417 ++++++++--------- 27 files changed, 1345 insertions(+), 1395 deletions(-) create mode 100644 src/main/workers/torrent-parser.worker.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 973f95bc7..9eb8b0e9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,21 +8,9 @@ jobs: build: strategy: matrix: - os: - [ - { - name: windows-latest, - build_path: out/Hydra-win32-x64, - artifact: Hydra-win32-x64, - }, - { - name: ubuntu-latest, - build_path: out/Hydra-linux-x64, - artifact: Hydra-linux-x64, - }, - ] + os: [windows-latest, ubuntu-latest] - runs-on: ${{ matrix.os.name }} + runs-on: ${{ matrix.os }} steps: - name: Check out Git repository @@ -47,10 +35,21 @@ jobs: - name: Build with cx_Freeze run: python torrent-client/setup.py build - - name: Publish - run: yarn run publish + - name: Build Linux + if: matrix.os == 'ubuntu-latest' + run: yarn build:linux + env: + MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} + RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} + MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }} + MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }} + + - name: Build Windows + if: matrix.os == 'windows-latest' + run: yarn build:win env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} @@ -58,15 +57,44 @@ jobs: MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }} MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }} + - name: Create artifact + uses: actions/upload-artifact@v4 + with: + name: Build-${{ matrix.os }} + path: | + dist/*.exe + dist/*.zip + dist/*.dmg + dist/*.AppImage + dist/*.snap + dist/*.deb + dist/*.rpm + dist/*.tar.gz + dist/*.yml + dist/*.blockmap + - name: VirusTotal Scan uses: crazy-max/ghaction-virustotal@v4 + if: matrix.os == 'windows-latest' with: vt_api_key: ${{ secrets.VT_API_KEY }} files: | - ./hydra-download-manager/hydra-download-manager.exe + ./dist/*.exe - - name: Create artifact - uses: actions/upload-artifact@v4 + - name: Publish + uses: softprops/action-gh-release@v1 with: - name: ${{ matrix.os.artifact }} - path: ${{ matrix.os.build_path }} + draft: true + files: | + dist/*.exe + dist/*.zip + dist/*.dmg + dist/*.AppImage + dist/*.snap + dist/*.deb + dist/*.rpm + dist/*.tar.gz + dist/*.yml + dist/*.blockmap + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 3bb8eb017..d98d02384 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -31,6 +31,7 @@ export default defineConfig(({ mode }) => { alias: { "@main": resolve("src/main"), "@locales": resolve("src/locales"), + "@resources": resolve("resources"), }, }, plugins: [ diff --git a/package.json b/package.json index 6eed5a1dd..e4ed2ebda 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "build": "npm run typecheck && electron-vite build", "postinstall": "electron-builder install-app-deps && node ./postinstall.cjs", "build:unpack": "npm run build && electron-builder --dir", - "build:win": "npm run build && electron-builder --win", + "build:win": "electron-builder --win", "build:mac": "electron-vite build && electron-builder --mac", "build:linux": "electron-vite build && electron-builder --linux" }, @@ -70,6 +70,7 @@ "@types/jsdom": "^21.1.6", "@types/lodash-es": "^4.17.12", "@types/node": "^20.12.7", + "@types/parse-torrent": "^5.8.7", "@types/react": "^18.2.48", "@types/react-dom": "^18.2.18", "@vanilla-extract/vite-plugin": "^4.0.7", diff --git a/src/main/events/library/close-game.ts b/src/main/events/library/close-game.ts index 0d556925a..d549f3b7d 100644 --- a/src/main/events/library/close-game.ts +++ b/src/main/events/library/close-game.ts @@ -1,9 +1,9 @@ import path from "node:path"; import { gameRepository } from "@main/repository"; +import { getProcesses } from "@main/helpers"; import { registerEvent } from "../register-event"; -import { getProcesses } from "@main/helpers"; const closeGame = async ( _event: Electron.IpcMainInvokeEvent, @@ -12,13 +12,17 @@ const closeGame = async ( const processes = await getProcesses(); const game = await gameRepository.findOne({ where: { id: gameId } }); - const gameProcess = processes.find((runningProcess) => { - const basename = path.win32.basename(game.executablePath); - const basenameWithoutExtension = path.win32.basename( - game.executablePath, - path.extname(game.executablePath) - ); + if (!game) return false; + const executablePath = game.executablePath!; + + const basename = path.win32.basename(executablePath); + const basenameWithoutExtension = path.win32.basename( + executablePath, + path.extname(executablePath) + ); + + const gameProcess = processes.find((runningProcess) => { if (process.platform === "win32") { return runningProcess.name === basename; } diff --git a/src/main/main.ts b/src/main/main.ts index d09f9c2fd..ae5917204 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -4,7 +4,7 @@ import { getNewGOGGames, getNewRepacksFromCPG, getNewRepacksFromUser, - // getNewRepacksFromXatab, + getNewRepacksFromXatab, getNewRepacksFromOnlineFix, readPipe, startProcessWatcher, @@ -22,13 +22,6 @@ import { Repack } from "./entity"; import { Notification } from "electron"; import { t } from "i18next"; import { In } from "typeorm"; -import creatWorker from "./workers/test?nodeWorker"; - -creatWorker({ workerData: "worker" }) - .on("message", (message) => { - console.log(`\nMessage from worker: ${message}`); - }) - .postMessage(""); startProcessWatcher(); @@ -80,9 +73,9 @@ const checkForNewRepacks = async () => { getNewGOGGames( existingRepacks.filter((repack) => repack.repacker === "GOG") ), - // getNewRepacksFromXatab( - // existingRepacks.filter((repack) => repack.repacker === "Xatab") - // ), + getNewRepacksFromXatab( + existingRepacks.filter((repack) => repack.repacker === "Xatab") + ), getNewRepacksFromCPG( existingRepacks.filter((repack) => repack.repacker === "CPG") ), diff --git a/src/main/services/repack-tracker/1337x.ts b/src/main/services/repack-tracker/1337x.ts index 1cbafa3bf..8573079bc 100644 --- a/src/main/services/repack-tracker/1337x.ts +++ b/src/main/services/repack-tracker/1337x.ts @@ -4,7 +4,6 @@ import { formatUploadDate } from "@main/helpers"; import { Repack } from "@main/entity"; import { requestWebPage, savePage } from "./helpers"; -import type { GameRepackInput } from "./helpers"; export const request1337x = async (path: string) => requestWebPage(`https://1337xx.to${path}`); @@ -68,7 +67,7 @@ export const extractTorrentsFromDocument = async ( user: string, document: Document, existingRepacks: Repack[] = [] -): Promise => { +) => { const $trs = Array.from(document.querySelectorAll("tbody tr")); return Promise.all( @@ -108,7 +107,7 @@ export const getNewRepacksFromUser = async ( user: string, existingRepacks: Repack[], page = 1 -): Promise => { +) => { const response = await request1337x(`/user/${user}/${page}`); const { window } = new JSDOM(response); diff --git a/src/main/services/repack-tracker/cpg-repacks.ts b/src/main/services/repack-tracker/cpg-repacks.ts index c4b62c1f1..2b939d08c 100644 --- a/src/main/services/repack-tracker/cpg-repacks.ts +++ b/src/main/services/repack-tracker/cpg-repacks.ts @@ -3,7 +3,6 @@ import { JSDOM } from "jsdom"; import { Repack } from "@main/entity"; import { requestWebPage, savePage } from "./helpers"; -import type { GameRepackInput } from "./helpers"; import { logger } from "../logger"; export const getNewRepacksFromCPG = async ( @@ -14,7 +13,7 @@ export const getNewRepacksFromCPG = async ( const { window } = new JSDOM(data); - const repacks: GameRepackInput[] = []; + const repacks = []; try { Array.from(window.document.querySelectorAll(".post")).forEach(($post) => { diff --git a/src/main/services/repack-tracker/gog.ts b/src/main/services/repack-tracker/gog.ts index 73daebf73..00c78e365 100644 --- a/src/main/services/repack-tracker/gog.ts +++ b/src/main/services/repack-tracker/gog.ts @@ -1,7 +1,8 @@ import { JSDOM, VirtualConsole } from "jsdom"; -import { GameRepackInput, requestWebPage, savePage } from "./helpers"; +import { requestWebPage, savePage } from "./helpers"; import { Repack } from "@main/entity"; -import { logger } from "../logger"; + +import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; const virtualConsole = new VirtualConsole(); @@ -36,43 +37,35 @@ const getGOGGame = async (url: string) => { }; export const getNewGOGGames = async (existingRepacks: Repack[] = []) => { - try { - const data = await requestWebPage( - "https://freegogpcgames.com/a-z-games-list/" - ); + const data = await requestWebPage( + "https://freegogpcgames.com/a-z-games-list/" + ); - const { window } = new JSDOM(data, { virtualConsole }); + const { window } = new JSDOM(data, { virtualConsole }); - const $uls = Array.from(window.document.querySelectorAll(".az-columns")); + const $uls = Array.from(window.document.querySelectorAll(".az-columns")); - for (const $ul of $uls) { - const repacks: GameRepackInput[] = []; - const $lis = Array.from($ul.querySelectorAll("li")); + for (const $ul of $uls) { + const repacks: QueryDeepPartialEntity[] = []; + const $lis = Array.from($ul.querySelectorAll("li")); - for (const $li of $lis) { - const $a = $li.querySelector("a"); - const href = $a.href; + for (const $li of $lis) { + const $a = $li.querySelector("a"); + const href = $a.href; - const title = $a.textContent.trim(); + const title = $a.textContent.trim(); - const gameExists = existingRepacks.some( - (existingRepack) => existingRepack.title === title - ); + const gameExists = existingRepacks.some( + (existingRepack) => existingRepack.title === title + ); - if (!gameExists) { - try { - const game = await getGOGGame(href); + if (!gameExists) { + const game = await getGOGGame(href); - repacks.push({ ...game, title }); - } catch (err) { - logger.error(err.message, { method: "getGOGGame", url: href }); - } - } + repacks.push({ ...game, title }); } - - if (repacks.length) await savePage(repacks); } - } catch (err) { - logger.error(err.message, { method: "getNewGOGGames" }); + + if (repacks.length) await savePage(repacks); } }; diff --git a/src/main/services/repack-tracker/online-fix.ts b/src/main/services/repack-tracker/online-fix.ts index 2a69dd705..c627eccb8 100644 --- a/src/main/services/repack-tracker/online-fix.ts +++ b/src/main/services/repack-tracker/online-fix.ts @@ -1,6 +1,5 @@ import { Repack } from "@main/entity"; import { savePage } from "./helpers"; -import type { GameRepackInput } from "./helpers"; import { logger } from "../logger"; import parseTorrent, { toMagnetURI, @@ -85,7 +84,7 @@ export const getNewRepacksFromOnlineFix = async ( }); const document = new JSDOM(home.body).window.document; - const repacks: GameRepackInput[] = []; + const repacks = []; const articles = Array.from(document.querySelectorAll(".news")); const totalPages = Number( document.querySelector("nav > a:nth-child(13)")?.textContent diff --git a/src/main/services/repack-tracker/xatab.ts b/src/main/services/repack-tracker/xatab.ts index 847a1026f..de9f5285a 100644 --- a/src/main/services/repack-tracker/xatab.ts +++ b/src/main/services/repack-tracker/xatab.ts @@ -1,16 +1,14 @@ import { JSDOM } from "jsdom"; -import parseTorrent, { toMagnetURI } from "parse-torrent"; - import { Repack } from "@main/entity"; import { logger } from "../logger"; import { requestWebPage, savePage } from "./helpers"; -import type { GameRepackInput } from "./helpers"; -const getTorrentBuffer = (url: string) => - fetch(url, { method: "GET" }).then((response) => - response.arrayBuffer().then((buffer) => Buffer.from(buffer)) - ); +import createWorker from "@main/workers/torrent-parser.worker?nodeWorker"; +import { toMagnetURI } from "parse-torrent"; +import type { Instance } from "parse-torrent"; + +const worker = createWorker({}); const formatXatabDate = (str: string) => { const date = new Date(); @@ -28,29 +26,36 @@ const formatXatabDate = (str: string) => { const formatXatabDownloadSize = (str: string) => str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB"); -const getXatabRepack = async (url: string) => { - const data = await requestWebPage(url); - const { window } = new JSDOM(data); - const { document } = window; +const getXatabRepack = (url: string) => { + return new Promise((resolve) => { + (async () => { + const data = await requestWebPage(url); + const { window } = new JSDOM(data); + const { document } = window; + + const $uploadDate = document.querySelector(".entry__date"); + const $size = document.querySelector(".entry__info-size"); - const $uploadDate = document.querySelector(".entry__date"); - const $size = document.querySelector(".entry__info-size"); + const $downloadButton = document.querySelector( + ".download-torrent" + ) as HTMLAnchorElement; - const $downloadButton = document.querySelector( - ".download-torrent" - ) as HTMLAnchorElement; + if (!$downloadButton) throw new Error("Download button not found"); - if (!$downloadButton) throw new Error("Download button not found"); + const onMessage = (torrent: Instance) => { + resolve({ + fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(), + magnet: toMagnetURI(torrent), + uploadDate: formatXatabDate($uploadDate.textContent), + }); - const torrentBuffer = await getTorrentBuffer($downloadButton.href); + worker.removeListener("message", onMessage); + }; - return { - fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(), - magnet: toMagnetURI({ - infoHash: parseTorrent(torrentBuffer).infoHash, - }), - uploadDate: formatXatabDate($uploadDate.textContent), - }; + worker.on("message", onMessage); + worker.postMessage($downloadButton.href); + })(); + }); }; export const getNewRepacksFromXatab = async ( @@ -61,7 +66,7 @@ export const getNewRepacksFromXatab = async ( const { window } = new JSDOM(data); - const repacks: GameRepackInput[] = []; + const repacks = []; for (const $a of Array.from( window.document.querySelectorAll(".entry__title a") @@ -84,7 +89,6 @@ export const getNewRepacksFromXatab = async ( const newRepacks = repacks.filter( (repack) => - repack.uploadDate && !existingRepacks.some( (existingRepack) => existingRepack.title === repack.title ) diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 0aa8afd21..05cb95d6f 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -2,8 +2,8 @@ import { BrowserWindow, Menu, Tray, app } from "electron"; import { is } from "@electron-toolkit/utils"; import { t } from "i18next"; import path from "node:path"; -import icon from "../../../resources/icon.png?asset"; -import trayIcon from "../../../resources/tray-icon.png?asset"; +import icon from "@resources/icon.png?asset"; +import trayIcon from "@resources/tray-icon.png?asset"; export class WindowManager { public static mainWindow: Electron.BrowserWindow | null = null; diff --git a/src/main/workers/torrent-parser.worker.ts b/src/main/workers/torrent-parser.worker.ts new file mode 100644 index 000000000..7502fd5ff --- /dev/null +++ b/src/main/workers/torrent-parser.worker.ts @@ -0,0 +1,17 @@ +import { parentPort } from "worker_threads"; +import parseTorrent from "parse-torrent"; + +const port = parentPort; +if (!port) throw new Error("IllegalState"); + +const getTorrentBuffer = (url: string) => + fetch(url, { method: "GET" }).then((response) => + response.arrayBuffer().then((buffer) => Buffer.from(buffer)) + ); + +port.on("message", async (url: string) => { + const buffer = await getTorrentBuffer(url); + const torrent = await parseTorrent(buffer); + + port.postMessage(torrent); +}); diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 97dcca0b6..4ca7b2fb8 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -71,7 +71,8 @@ contextBridge.exposeInMainWorld("electron", { openGame: (gameId: number, executablePath: string) => ipcRenderer.invoke("openGame", gameId, executablePath), closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), - removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId), + removeGameFromLibrary: (gameId: number) => + ipcRenderer.invoke("removeGameFromLibrary", gameId), deleteGameFolder: (gameId: number) => ipcRenderer.invoke("deleteGameFolder", gameId), getGameByObjectID: (objectID: string) => diff --git a/src/preload/index.ts b/src/preload/index.ts index 66fff80fa..c4f8ca96f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -80,7 +80,8 @@ contextBridge.exposeInMainWorld("electron", { openGame: (gameId: number, executablePath: string) => ipcRenderer.invoke("openGame", gameId, executablePath), closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), - removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId), + removeGameFromLibrary: (gameId: number) => + ipcRenderer.invoke("removeGameFromLibrary", gameId), deleteGameFolder: (gameId: number) => ipcRenderer.invoke("deleteGameFolder", gameId), getGameByObjectID: (objectID: string) => diff --git a/src/renderer/index.html b/src/renderer/index.html index abb1eaae3..1917de459 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -9,9 +9,8 @@ content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;" /> - +

-

hello

diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.tsx b/src/renderer/src/components/bottom-panel/bottom-panel.tsx index ed9a54ed6..993d6aa51 100644 --- a/src/renderer/src/components/bottom-panel/bottom-panel.tsx +++ b/src/renderer/src/components/bottom-panel/bottom-panel.tsx @@ -22,7 +22,7 @@ export function BottomPanel() { }, []); const status = useMemo(() => { - if (isDownloading) { + if (isDownloading && game) { if (game.status === "downloading_metadata") return t("downloading_metadata", { title: game.title }); diff --git a/src/renderer/src/components/modal/modal.tsx b/src/renderer/src/components/modal/modal.tsx index 9b5f8cf5b..1f07e7d7a 100644 --- a/src/renderer/src/components/modal/modal.tsx +++ b/src/renderer/src/components/modal/modal.tsx @@ -62,12 +62,14 @@ export function Modal({ const onMouseDown = (e: MouseEvent) => { if (!isTopMostModal()) return; - const clickedOutsideContent = !modalContentRef.current.contains( - e.target as Node - ); - - if (clickedOutsideContent) { - handleCloseClick(); + if (modalContentRef.current) { + const clickedOutsideContent = modalContentRef.current.contains( + e.target as Node + ); + + if (clickedOutsideContent) { + handleCloseClick(); + } } }; diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 1d198ea58..a478f687f 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -62,7 +62,7 @@ declare global { openGameInstaller: (gameId: number) => Promise; openGame: (gameId: number, executablePath: string) => Promise; closeGame: (gameId: number) => Promise; - removeGame: (gameId: number) => Promise; + removeGameFromLibrary: (gameId: number) => Promise; deleteGameFolder: (gameId: number) => Promise; getGameByObjectID: (objectID: string) => Promise; onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer; diff --git a/src/renderer/src/features/use-preferences-slice.ts b/src/renderer/src/features/use-preferences-slice.ts index f6a3cf65d..d735e7a2d 100644 --- a/src/renderer/src/features/use-preferences-slice.ts +++ b/src/renderer/src/features/use-preferences-slice.ts @@ -14,7 +14,10 @@ export const userPreferencesSlice = createSlice({ name: "userPreferences", initialState, reducers: { - setUserPreferences: (state, action: PayloadAction) => { + setUserPreferences: ( + state, + action: PayloadAction + ) => { state.value = action.payload; }, }, diff --git a/src/renderer/src/hooks/use-download.ts b/src/renderer/src/hooks/use-download.ts index 27f90e8d7..44392242a 100644 --- a/src/renderer/src/hooks/use-download.ts +++ b/src/renderer/src/hooks/use-download.ts @@ -58,17 +58,17 @@ export function useDownload() { deleteGame(gameId); }); - const removeGame = (gameId: number) => - window.electron.removeGame(gameId).then(() => { + const removeGameFromLibrary = (gameId: number) => + window.electron.removeGameFromLibrary(gameId).then(() => { updateLibrary(); }); const isVerifying = ["downloading_metadata", "checking_files"].includes( - lastPacket?.game.status + lastPacket?.game.status ?? "" ); const getETA = () => { - if (isVerifying || !isFinite(lastPacket?.timeRemaining)) { + if (isVerifying || !isFinite(lastPacket?.timeRemaining ?? 0)) { return ""; } @@ -124,7 +124,7 @@ export function useDownload() { pauseDownload, resumeDownload, cancelDownload, - removeGame, + removeGameFromLibrary, deleteGame, isGameDeleting, clearDownload: () => dispatch(clearDownload()), diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx index aecf7b5eb..ca9903a35 100644 --- a/src/renderer/src/pages/downloads/downloads.tsx +++ b/src/renderer/src/pages/downloads/downloads.tsx @@ -33,6 +33,7 @@ export function Downloads() { numSeeds, pauseDownload, resumeDownload, + removeGameFromLibrary, cancelDownload, deleteGame, isGameDeleting, @@ -52,11 +53,6 @@ export function Downloads() { updateLibrary(); }); - const removeGame = (gameId: number) => - window.electron.removeGame(gameId).then(() => { - updateLibrary(); - }); - const getFinalDownloadSize = (game: Game) => { const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; @@ -194,7 +190,7 @@ export function Downloads() {
From 06d334d2d70f31671f762077891c1223ef7096cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 03:15:11 +0000 Subject: [PATCH 69/70] docs(contributor): contrib-readme-action has updated readme --- README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/README.md b/README.md index 981a87073..ea6256e6c 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,87 @@ yarn make ## Contributors + + + + + + + + + + + + + + +
+ + hydralauncher +
+ Hydra +
+
+ + zamitto +
+ Null +
+
+ + fzanutto +
+ Null +
+
+ + JackEnx +
+ Null +
+
+ + fhilipecrash +
+ Fhilipe Coelho +
+
+ + Magrid0 +
+ Magrid +
+
+ + ferivoq +
+ FeriVOQ +
+
+ + xbozo +
+ Guilherme Viana +
+
+ + pmenta +
+ João Martins +
+
+ + eltociear +
+ Ikko Eltociear Ashimine +
+
+ + Netflixyapp +
+ Netflixy +
+
## License From f14ddba8075f964fe318c11ed35f5f28f0d15445 Mon Sep 17 00:00:00 2001 From: Zamitto Date: Tue, 30 Apr 2024 16:04:42 -0300 Subject: [PATCH 70/70] fix: update fastlist directory --- .gitignore | 1 + electron-builder.yml | 1 + postinstall.cjs | 6 +----- src/main/helpers/ps.ts | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 0767dd62a..675a83ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .vscode node_modules hydra-download-manager +fastlist.exe __pycache__ dist out diff --git a/electron-builder.yml b/electron-builder.yml index 9101b0284..9acb2eac7 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -5,6 +5,7 @@ directories: extraResources: - hydra-download-manager - hydra.db + - fastlist.exe files: - "!**/.vscode/*" - "!src/*" diff --git a/postinstall.cjs b/postinstall.cjs index 0b47b3803..8ca8f1014 100644 --- a/postinstall.cjs +++ b/postinstall.cjs @@ -1,12 +1,8 @@ const fs = require("fs"); if (process.platform === "win32") { - if (!fs.existsSync("resources/dist")) { - fs.mkdirSync("resources/dist"); - } - fs.copyFileSync( "node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe", - "resources/dist/fastlist.exe" + "fastlist.exe" ); } diff --git a/src/main/helpers/ps.ts b/src/main/helpers/ps.ts index dbc11f09c..380b51169 100644 --- a/src/main/helpers/ps.ts +++ b/src/main/helpers/ps.ts @@ -10,8 +10,8 @@ const execFile = promisify(childProcess.execFile); export const getProcesses = async () => { if (process.platform == "win32") { const binaryPath = app.isPackaged - ? path.join(process.resourcesPath, "dist", "fastlist.exe") - : path.join(__dirname, "..", "..", "resources", "dist", "fastlist.exe"); + ? path.join(process.resourcesPath, "fastlist.exe") + : path.join(__dirname, "..", "..", "fastlist.exe"); const { stdout } = await execFile(binaryPath, { maxBuffer: TEN_MEGABYTES,