mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07a353393b | |||
| 7b22387fc5 | |||
| 2bba3c5232 | |||
| b97e03041a | |||
| fb7f08ee2e | |||
| 1ba0b0beac | |||
| fa50d90941 | |||
| 0539b0da8d | |||
| ea33ee3a62 | |||
| fd4918017b | |||
| 95688b9d33 |
+70
-20
@@ -1,21 +1,71 @@
|
||||
[
|
||||
{"username": "Shidorien"},
|
||||
{"username": "iPixelGalaxy"},
|
||||
{"username": "Emyte", "type": "gold"},
|
||||
{"username": "GoodOldNervy"},
|
||||
{"username": "Naysy"},
|
||||
{"username": "Falmil"},
|
||||
{"username": "K1Lc4m"},
|
||||
{"username": "Anonymously42", "type": "diamond", "link": "https://www.twitch.tv/anonymously42tv"},
|
||||
{"username": "Burt", "type": "gold"},
|
||||
{"username": "Ilnahro"},
|
||||
{"username": "Phil", "type": "gold"},
|
||||
{"username": "Protocrush", "type": "gold"},
|
||||
{"username": "ServerMensch", "type": "gold"},
|
||||
{"username": "Karlito", "type": "gold"},
|
||||
{"username": "Cathery", "type": "gold"},
|
||||
{"username": ".sharkey"},
|
||||
{"username": "Z3t4"},
|
||||
{"username": "blot455", "type": "diamond"},
|
||||
{"username": "Lumberjack462", "type": "diamond", "link": "https://www.youtube.com/@lumberjack462"}
|
||||
]
|
||||
{
|
||||
"username": "Shidorien"
|
||||
},
|
||||
{
|
||||
"username": "iPixelGalaxy"
|
||||
},
|
||||
{
|
||||
"username": "Emyte",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "GoodOldNervy"
|
||||
},
|
||||
{
|
||||
"username": "Naysy"
|
||||
},
|
||||
{
|
||||
"username": "Falmil"
|
||||
},
|
||||
{
|
||||
"username": "K1Lc4m"
|
||||
},
|
||||
{
|
||||
"username": "Anonymously42",
|
||||
"type": "diamond",
|
||||
"link": "https://www.twitch.tv/anonymously42tv"
|
||||
},
|
||||
{
|
||||
"username": "Burt",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "Ilnahro"
|
||||
},
|
||||
{
|
||||
"username": "Phil",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "Protocrush",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "ServerMensch",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "Karlito",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "Cathery",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": ".sharkey"
|
||||
},
|
||||
{
|
||||
"username": "Z3t4"
|
||||
},
|
||||
{
|
||||
"username": "blot455",
|
||||
"type": "diamond"
|
||||
},
|
||||
{
|
||||
"username": "Lumberjack462",
|
||||
"type": "diamond",
|
||||
"link": "https://www.youtube.com/@lumberjack462"
|
||||
}
|
||||
]
|
||||
@@ -505,8 +505,7 @@
|
||||
"choose-store": {
|
||||
"title": "Welche Plattform?",
|
||||
"body": "Wählen Sie die Plattform, von der Sie Beat Saber herunterladen möchten.",
|
||||
"set-in-settings": "Die Standardplattform in den Einstellungen festlegen",
|
||||
"unavailable": "Aufgrund kürzlicher Änderungen von Oculus ist das Downgrade von Oculus-Versionen derzeit nicht mehr verfügbar. Wir untersuchen das Problem und werden Sie auf dem BSManager-Discord-Server auf dem Laufenden halten."
|
||||
"set-in-settings": "Die Standardplattform in den Einstellungen festlegen"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -708,12 +707,20 @@
|
||||
"understood": "Verstanden"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Meta-Token",
|
||||
"title": "Oculus Token",
|
||||
"body": {
|
||||
"need-token": "Um Beat Saber herunterzuladen, ist Ihr Meta-Anmelde-Token erforderlich.",
|
||||
"how-obtain-token": "Wie erhalte ich mein Meta-Token?",
|
||||
"input-label": "Meta-Token",
|
||||
"token-is-invalid": "Das Token ist ungültig."
|
||||
"info-enter-token": "Um Beat Saber herunterzuladen, ist Ihr Oculus-Anmeldetoken erforderlich.",
|
||||
"how-obtain-token": "Wie erhalte ich mein Oculus Token?",
|
||||
"oculus-token": "Oculus Token",
|
||||
"token-is-invalid": "Das Token ist ungültig.",
|
||||
"save-my-token": "Mein Token speichern",
|
||||
"have-token-saved": "Ich habe bereits ein Token gespeichert",
|
||||
"save-token-info": "Dies wird Ihr Token zur einfacheren Wiederverwendung speichern. Sie müssen ein Passwort erstellen, um Ihr Token zu verschlüsseln und zu speichern. Wenn Sie Ihr Passwort vergessen, müssen Sie nur Ihr Token erneut angeben.",
|
||||
"password": "Passwort",
|
||||
"password-too-short": "Passwort zu kurz",
|
||||
"info-enter-password": "Um Beat Saber herunterzuladen, ist Ihr Oculus-Anmeldetoken erforderlich. Geben Sie das Passwort ein, mit dem Ihr Oculus-Token gespeichert wurde.",
|
||||
"info-disabled-btn-password": "Der mit dem angegebenen Passwort entschlüsselte Token ist ungültig. Stellen Sie sicher, dass das Passwort dasselbe ist wie das zum Speichern des Tokens verwendete.",
|
||||
"enter-oculus-token": "Ein Oculus-Token eingeben"
|
||||
},
|
||||
"valid-btn": "Validieren"
|
||||
}
|
||||
|
||||
@@ -505,8 +505,7 @@
|
||||
"choose-store": {
|
||||
"title": "Which platform?",
|
||||
"body": "Select the platform from which you wish to download Beat Saber.",
|
||||
"set-in-settings": "Set the default platform in settings",
|
||||
"unavailable": "Due to recent changes by Oculus, downgrading Oculus versions is currently no longer available. We're investigating the issue and will keep you posted on the BSManager Discord server."
|
||||
"set-in-settings": "Set the default platform in settings"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -708,12 +707,20 @@
|
||||
"understood": "Understood"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Meta Token",
|
||||
"title": "Oculus Token",
|
||||
"body": {
|
||||
"need-token": "To download Beat Saber, your Meta login token is required.",
|
||||
"how-obtain-token": "How do I obtain my Meta Token?",
|
||||
"input-label": "Meta Token",
|
||||
"token-is-invalid": "The Token is invalid."
|
||||
"info-enter-token": "To download Beat Saber, your Oculus login token is required.",
|
||||
"how-obtain-token": "How do I obtain my Oculus Token?",
|
||||
"oculus-token": "Oculus Token",
|
||||
"token-is-invalid": "The Token is invalid.",
|
||||
"save-my-token": "Save my token",
|
||||
"have-token-saved": "I already have a token saved",
|
||||
"save-token-info": "This will save your token for easier reuse. You will need to create a password to encrypt your token for storage purposes. If you ever forget your password, you just need to provide your token again.",
|
||||
"password": "Password",
|
||||
"password-too-short": "Password too short",
|
||||
"info-enter-password": "To download Beat Saber, your Oculus login token is required. Enter the password used to save your Oculus token.",
|
||||
"info-disabled-btn-password": "The token decrypted with the provided password is invalid. Make sure the password is the same as the one used to save the token.",
|
||||
"enter-oculus-token": "Enter an Oculus token"
|
||||
},
|
||||
"valid-btn": "Validate"
|
||||
}
|
||||
|
||||
@@ -504,8 +504,7 @@
|
||||
"choose-store": {
|
||||
"title": "¿Qué plataforma?",
|
||||
"body": "Selecciona la plataforma desde la que deseas descargar Beat Saber.",
|
||||
"set-in-settings": "Establecer la plataforma predeterminada en los ajustes",
|
||||
"unavailable": "Debido a cambios recientes de Oculus, la degradación de versiones de Oculus ya no está disponible. Estamos investigando el problema y te mantendremos informado en el servidor de Discord de BSManager."
|
||||
"set-in-settings": "Establecer la plataforma predeterminada en los ajustes"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -707,12 +706,20 @@
|
||||
"understood": "Entendido"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Token de Meta",
|
||||
"title": "Token de Oculus",
|
||||
"body": {
|
||||
"need-token": "Para descargar Beat Saber, se requiere su token de inicio de sesión de Meta.",
|
||||
"how-obtain-token": "¿Cómo obtengo mi Token de Meta?",
|
||||
"input-label": "Token de Meta",
|
||||
"token-is-invalid": "El Token es inválido."
|
||||
"info-enter-token": "Para descargar Beat Saber, se requiere tu token de inicio de sesión de Oculus.",
|
||||
"how-obtain-token": "¿Cómo obtengo mi Token de Oculus?",
|
||||
"oculus-token": "Token de Oculus",
|
||||
"token-is-invalid": "El token es inválido.",
|
||||
"save-my-token": "Guardar mi token",
|
||||
"have-token-saved": "Ya tengo un token guardado",
|
||||
"save-token-info": "Esto guardará tu token para reutilizarlo más fácilmente. Necesitarás crear una contraseña para encriptar tu token para su almacenamiento. Si alguna vez olvidas tu contraseña, solo necesitas proporcionar tu token nuevamente.",
|
||||
"password": "Contraseña",
|
||||
"password-too-short": "Contraseña demasiado corta",
|
||||
"info-enter-password": "Para descargar Beat Saber, se requiere tu token de inicio de sesión de Oculus. Introduce la contraseña utilizada para guardar tu token de Oculus.",
|
||||
"info-disabled-btn-password": "El token descifrado con la contraseña proporcionada es inválido. Asegúrate de que la contraseña sea la misma que la utilizada para guardar el token.",
|
||||
"enter-oculus-token": "Introducir un token de Oculus"
|
||||
},
|
||||
"valid-btn": "Validar"
|
||||
}
|
||||
|
||||
@@ -501,8 +501,7 @@
|
||||
"choose-store": {
|
||||
"title": "Quelle plateforme ?",
|
||||
"body": "Sélectionnez la plateforme depuis laquelle vous souhaitez télécharger Beat Saber.",
|
||||
"set-in-settings": "Définir la platforme par défaut dans les paramètres",
|
||||
"unavailable": "En raison de récents changements apportés par Oculus, la rétrogradation des versions d'Oculus n'est actuellement plus disponible. Nous étudions le problème et vous tiendrons au courant sur le serveur Discord de BSManager."
|
||||
"set-in-settings": "Définir la platforme par défaut dans les paramètres"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -704,12 +703,20 @@
|
||||
"understood": "J'ai compris"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Token Meta",
|
||||
"title": "Token Oculus",
|
||||
"body": {
|
||||
"need-token": "Pour télécharger Beat Saber, votre token de connexion à Meta est nécessaire.",
|
||||
"how-obtain-token": "Comment obtenir mon Token Meta ?",
|
||||
"input-label": "Token Meta",
|
||||
"token-is-invalid": "Le Token est invalide."
|
||||
"info-enter-token": "Pour télécharger Beat Saber, votre token de connexion à Oculus est nécessaire.",
|
||||
"how-obtain-token": "Comment obtenir mon Token Oculus ?",
|
||||
"oculus-token": "Token Oculus",
|
||||
"token-is-invalid": "Le Token est invalide.",
|
||||
"save-my-token": "Sauvegarder mon token",
|
||||
"have-token-saved": "J'ai déjà un token sauvegardé",
|
||||
"save-token-info": "Ceci enregistrera votre token pour le réutiliser plus facilement. Vous devrez créer un mot de passe pour crypter votre token à des fins de stockage. Si jamais vous oubliez votre mot de passe, il vous suffit de fournir à nouveau votre token.",
|
||||
"password": "Mot de passe",
|
||||
"password-too-short": "Mot de passe trop court",
|
||||
"info-enter-password": "Pour télécharger Beat Saber, votre token de connexion à Oculus est nécessaire. Entrez le mot de passe utilisé pour sauvegarder votre token Oculus.",
|
||||
"info-disabled-btn-password": "Le token déchiffré à l'aide du mot de passe fourni est invalide. Assurez-vous que le mot de passe est le même que celui utilisé pour sauvegarder le token.",
|
||||
"enter-oculus-token": "Entrer un token Oculus"
|
||||
},
|
||||
"valid-btn": "Valider"
|
||||
}
|
||||
|
||||
@@ -505,8 +505,7 @@
|
||||
"choose-store": {
|
||||
"title": "どのプラットフォーム?",
|
||||
"body": "Beat Saberをダウンロードするプラットフォームを選択してください。",
|
||||
"set-in-settings": "設定からデフォルトのプラットフォームを設定する",
|
||||
"unavailable": "Oculusによる最近の変更により、現在Oculusのバージョンダウングレードは利用できなくなりました。私たちは問題を調査しており、BSManagerのDiscordサーバーで最新情報をお知らせします。"
|
||||
"set-in-settings": "設定からデフォルトのプラットフォームを設定する"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steamガード",
|
||||
@@ -708,14 +707,22 @@
|
||||
"understood": "分かった"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Metaトークン",
|
||||
"title": "Oculusトークン",
|
||||
"body": {
|
||||
"need-token": "Beat Saberをダウンロードするには、Metaのログイントークンが必要です。",
|
||||
"how-obtain-token": "Metaトークンはどうやって入手すればいいですか?",
|
||||
"input-label": "Metaトークン",
|
||||
"token-is-invalid": "トークンが無効です。"
|
||||
"info-enter-token": "Beat Saberをダウンロードするには、あなたのオキュラスログイントークンが必要です。",
|
||||
"how-obtain-token": "オキュラストークンはどうやって取得するの?",
|
||||
"oculus-token": "オキュラストークン",
|
||||
"token-is-invalid": "トークンが無効です。",
|
||||
"save-my-token": "私のトークンを保存する",
|
||||
"have-token-saved": "すでにトークンを保存しています",
|
||||
"save-token-info": "これにより、トークンを簡単に再利用するために保存します。保管のためにトークンを暗号化するパスワードを作成する必要があります。パスワードを忘れた場合は、トークンを再度提供するだけです。",
|
||||
"password": "パスワード",
|
||||
"password-too-short": "パスワードが短すぎます",
|
||||
"info-enter-password": "Beat Saberをダウンロードするには、あなたのオキュラスログイントークンが必要です。オキュラストークンを保存するために使用したパスワードを入力してください。",
|
||||
"info-disabled-btn-password": "提供されたパスワードで復号化されたトークンは無効です。トークンを保存するために使用されたパスワードと同じであることを確認してください。",
|
||||
"enter-oculus-token": "オキュラストークンを入力する"
|
||||
},
|
||||
"valid-btn": "検証"
|
||||
"valid-btn": "確認"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -505,8 +505,7 @@
|
||||
"choose-store": {
|
||||
"title": "Какая платформа?",
|
||||
"body": "Выберите платформу, с которой вы хотите скачать Beat Saber.",
|
||||
"set-in-settings": "Установить платформу по умолчанию в настройках",
|
||||
"unavailable": "В связи с недавними изменениями от Oculus, понижение версий Oculus в настоящее время больше не доступно. Мы изучаем проблему и будем держать вас в курсе на сервере BSManager в Discord."
|
||||
"set-in-settings": "Установить платформу по умолчанию в настройках"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -708,12 +707,20 @@
|
||||
"understood": "Понял"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Мета Токен",
|
||||
"title": "Токен Oculus",
|
||||
"body": {
|
||||
"need-token": "Для загрузки Beat Saber требуется ваш токен входа в Meta.",
|
||||
"how-obtain-token": "Как получить мой Мета Токен?",
|
||||
"input-label": "Мета Токен",
|
||||
"token-is-invalid": "Токен недействителен."
|
||||
"info-enter-token": "Для загрузки Beat Saber требуется ваш токен входа в Oculus.",
|
||||
"how-obtain-token": "Как получить мой токен Oculus?",
|
||||
"oculus-token": "Токен Oculus",
|
||||
"token-is-invalid": "Токен недействителен.",
|
||||
"save-my-token": "Сохранить мой токен",
|
||||
"have-token-saved": "У меня уже есть сохраненный токен",
|
||||
"save-token-info": "Это сохранит ваш токен для удобства повторного использования. Вам необходимо создать пароль для шифрования вашего токена для хранения. Если вы забудете пароль, вам просто нужно будет снова предоставить свой токен.",
|
||||
"password": "Пароль",
|
||||
"password-too-short": "Слишком короткий пароль",
|
||||
"info-enter-password": "Для загрузки Beat Saber требуется ваш токен входа в Oculus. Введите пароль, используемый для сохранения вашего токена Oculus.",
|
||||
"info-disabled-btn-password": "Токен, расшифрованный с использованием предоставленного пароля, недействителен. Убедитесь, что пароль совпадает с тем, который использовался для сохранения токена.",
|
||||
"enter-oculus-token": "Введите токен Oculus"
|
||||
},
|
||||
"valid-btn": "Подтвердить"
|
||||
}
|
||||
|
||||
@@ -505,8 +505,7 @@
|
||||
"choose-store": {
|
||||
"title": "選擇哪個平台?",
|
||||
"body": "選擇你希望為其下載 BeatSaber 的平台。",
|
||||
"set-in-settings": "在設定中設定預設平台",
|
||||
"unavailable": "由於Oculus最近的更改,目前不再提供降級Oculus版本的服務。我們正在調查此問題,並將在BSManager Discord伺服器上保持更新。"
|
||||
"set-in-settings": "在設定中設定預設平台"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -709,12 +708,20 @@
|
||||
"understood": "明白了"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Meta 令牌",
|
||||
"title": "Oculus令牌",
|
||||
"body": {
|
||||
"need-token": "要下載 BeatSaber,需要你的 Meta 登錄令牌。",
|
||||
"how-obtain-token": "如何獲取我的 Meta 令牌?",
|
||||
"input-label": "Meta 令牌",
|
||||
"token-is-invalid": "令牌無效。"
|
||||
"info-enter-token": "下載Beat Saber需要您的Oculus登錄令牌。",
|
||||
"how-obtain-token": "我如何獲得我的Oculus令牌?",
|
||||
"oculus-token": "Oculus令牌",
|
||||
"token-is-invalid": "令牌無效。",
|
||||
"save-my-token": "保存我的令牌",
|
||||
"have-token-saved": "我已經保存了一個令牌",
|
||||
"save-token-info": "這將保存您的令牌以便更容易地重新使用。您需要創建一個密碼來加密您的令牌以進行存儲。如果您忘記了密碼,只需再次提供您的令牌即可。",
|
||||
"password": "密碼",
|
||||
"password-too-short": "密碼太短",
|
||||
"info-enter-password": "下載Beat Saber需要您的Oculus登錄令牌。請輸入用於保存您的Oculus令牌的密碼。",
|
||||
"info-disabled-btn-password": "使用提供的密碼解密的令牌無效。確保密碼與保存令牌時使用的密碼相同。",
|
||||
"enter-oculus-token": "輸入Oculus令牌"
|
||||
},
|
||||
"valid-btn": "驗證"
|
||||
}
|
||||
|
||||
@@ -505,8 +505,7 @@
|
||||
"choose-store": {
|
||||
"title": "选择哪个平台?",
|
||||
"body": "选择你希望为其下载 BeatSaber 的平台。",
|
||||
"set-in-settings": "在设置中设定默认平台",
|
||||
"unavailable": "由于Oculus最近的更改,目前不再提供降级Oculus版本的服务。我们正在调查此问题,并将在BSManager Discord服务器上保持更新。"
|
||||
"set-in-settings": "在设置中设定默认平台"
|
||||
},
|
||||
"guard": {
|
||||
"title": "Steam Guard",
|
||||
@@ -709,12 +708,20 @@
|
||||
"understood": "明白了"
|
||||
},
|
||||
"enter-meta-token": {
|
||||
"title": "Meta 令牌",
|
||||
"title": "Oculus令牌",
|
||||
"body": {
|
||||
"need-token": "要下载 BeatSaber,需要你的 Meta 登录令牌。",
|
||||
"how-obtain-token": "如何获取我的 Meta 令牌?",
|
||||
"input-label": "Meta 令牌",
|
||||
"token-is-invalid": "令牌无效。"
|
||||
"info-enter-token": "下载Beat Saber需要您的Oculus登录令牌。",
|
||||
"how-obtain-token": "我如何获得我的Oculus令牌?",
|
||||
"oculus-token": "Oculus令牌",
|
||||
"token-is-invalid": "令牌无效。",
|
||||
"save-my-token": "保存我的令牌",
|
||||
"have-token-saved": "我已经保存了一个令牌",
|
||||
"save-token-info": "这将保存您的令牌以便更容易地重新使用。您需要创建一个密码来加密您的令牌以进行存储。如果您忘记了密码,只需再次提供您的令牌即可。",
|
||||
"password": "密码",
|
||||
"password-too-short": "密码太短",
|
||||
"info-enter-password": "下载Beat Saber需要您的Oculus登录令牌。请输入用于保存您的Oculus令牌的密码。",
|
||||
"info-disabled-btn-password": "使用提供的密码解密的令牌无效。确保密码与保存令牌时使用的密码相同。",
|
||||
"enter-oculus-token": "输入Oculus令牌"
|
||||
},
|
||||
"valid-btn": "验证"
|
||||
}
|
||||
|
||||
Generated
+24
@@ -13,6 +13,7 @@
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"archiver": "^6.0.1",
|
||||
"color": "^4.2.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dateformat": "^5.0.3",
|
||||
"dot-prop": "^8.0.2",
|
||||
"electron-debug": "^3.2.0",
|
||||
@@ -60,6 +61,7 @@
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@types/archiver": "^5.3.1",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"@types/dateformat": "^5.0.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "17.0.23",
|
||||
@@ -2524,6 +2526,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/crypto-js": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.1.tgz",
|
||||
"integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/dateformat": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.0.tgz",
|
||||
@@ -5711,6 +5719,11 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||
},
|
||||
"node_modules/css-color-names": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
@@ -21062,6 +21075,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/crypto-js": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.1.tgz",
|
||||
"integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/dateformat": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.0.tgz",
|
||||
@@ -23585,6 +23604,11 @@
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||
},
|
||||
"css-color-names": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
"@types/use-double-click": "^1.0.1",
|
||||
"@types/webpack-bundle-analyzer": "^4.4.2",
|
||||
"@types/webpack-env": "^1.18.0",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.34.0",
|
||||
"@typescript-eslint/parser": "^5.34.0",
|
||||
"autoprefixer": "^10.4.8",
|
||||
@@ -238,6 +239,7 @@
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"archiver": "^6.0.1",
|
||||
"color": "^4.2.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dateformat": "^5.0.3",
|
||||
"dot-prop": "^8.0.2",
|
||||
"electron-debug": "^3.2.0",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bs-manager",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.3",
|
||||
"description": "BSManager",
|
||||
"main": "./dist/main/main.js",
|
||||
"author": {
|
||||
|
||||
@@ -18,6 +18,8 @@ import readline from "readline";
|
||||
import { Observable, Subject, catchError, finalize, from, map, switchMap, throwError } from "rxjs";
|
||||
import { BsStore } from "../../shared/models/bs-store.enum";
|
||||
import { CustomError } from "../../shared/models/exceptions/custom-error.class";
|
||||
import crypto from "crypto";
|
||||
|
||||
|
||||
export class BSLocalVersionService {
|
||||
private static instance: BSLocalVersionService;
|
||||
@@ -119,7 +121,14 @@ export class BSLocalVersionService {
|
||||
folderVersion.ino = folderStats.ino;
|
||||
}
|
||||
|
||||
folderVersion.metadata = await this.getAllVersionMetadata(folderVersion, { store: BsStore.STEAM });
|
||||
|
||||
let metadata = await this.getAllVersionMetadata(folderVersion);
|
||||
|
||||
// Will be removed in future version. It just to prepare future features
|
||||
if(!metadata?.id){
|
||||
metadata = await this.initVersionMetadata(folderVersion, metadata ?? { store: BsStore.STEAM });
|
||||
}
|
||||
folderVersion.metadata = metadata;
|
||||
|
||||
const customVersion = this.getCustomVersions().find(customVersion => {
|
||||
return customVersion.BSVersion === folderVersion.BSVersion && customVersion.name === folderVersion.name;
|
||||
@@ -130,36 +139,27 @@ export class BSLocalVersionService {
|
||||
return folderVersion;
|
||||
}
|
||||
|
||||
public getAllVersionMetadata(version: BSVersion, defaultMetadata?: BSVersionMetadata): Promise<BSVersionMetadata>{
|
||||
private async writeVersionMetadata(version: BSVersion, metadata: BSVersionMetadata): Promise<void>{
|
||||
const versionPath = await this.getVersionPath(version);
|
||||
const metadataPath = path.join(versionPath, this.METADATA_FILE);
|
||||
return writeFile(metadataPath, JSON.stringify(metadata));
|
||||
}
|
||||
|
||||
public initVersionMetadata(version: BSVersion, metadata: Omit<BSVersionMetadata, "id">): Promise<BSVersionMetadata>{
|
||||
const firstMetadata = { id: crypto.randomUUID(), ...metadata };
|
||||
return this.writeVersionMetadata(version, firstMetadata).then(() => firstMetadata).catch(err => {
|
||||
log.error("initVersionMetadata error", err);
|
||||
return firstMetadata;
|
||||
});
|
||||
}
|
||||
|
||||
public getAllVersionMetadata(version: BSVersion): Promise<BSVersionMetadata>{
|
||||
return (async () => {
|
||||
const versionPath = await this.getVersionPath(version);
|
||||
const contents = await readFile(path.join(versionPath, this.METADATA_FILE), "utf-8");
|
||||
return JSON.parse(contents);
|
||||
})().catch(e => {
|
||||
log.warn("Not a critical error", e);
|
||||
return {};
|
||||
}).then(metadata => ({...defaultMetadata, ...metadata}));
|
||||
}
|
||||
|
||||
public getVersionMetadata<K extends keyof BSVersionMetadata, T extends BSVersionMetadata[K]>(version: BSVersion, key: K, defaultValue?: T): Promise<T|undefined>{
|
||||
return (async () => {
|
||||
const metadata = await this.getAllVersionMetadata(version);
|
||||
return metadata?.[key];
|
||||
})().catch(e => {
|
||||
log.warn("Not a critical error", e);
|
||||
return null;
|
||||
}).then(value => (value ?? defaultValue) as T);
|
||||
}
|
||||
|
||||
public setVersionMetadata<K extends keyof BSVersionMetadata, T extends BSVersionMetadata[K]>(version: BSVersion, key: K, value: T): Promise<void>{
|
||||
return (async () => {
|
||||
const versionPath = await this.getVersionPath(version);
|
||||
const metadataPath = path.join(versionPath, this.METADATA_FILE);
|
||||
const metadata = await this.getAllVersionMetadata(version) ?? ({} as BSVersionMetadata);
|
||||
metadata[key] = value;
|
||||
await writeFile(metadataPath, JSON.stringify(metadata));
|
||||
})().catch(e => {
|
||||
log.error(e);
|
||||
log.warn("Error getAllVersionMetadata", e);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ export class BSLocalVersionService {
|
||||
}),
|
||||
finalize(async () => {
|
||||
if(failed){ return; }
|
||||
await this.setVersionMetadata(versionDest.version, "store", store);
|
||||
await this.initVersionMetadata(versionDest.version, { store });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class BsOculusDownloaderService {
|
||||
version: {
|
||||
...version,
|
||||
...(path.basename(dest) !== version.BSVersion && { name: path.basename(dest) }),
|
||||
metadata: { store: BsStore.OCULUS }
|
||||
metadata: { store: BsStore.OCULUS, id: "" }
|
||||
},
|
||||
dest
|
||||
};
|
||||
@@ -129,6 +129,7 @@ export class BsOculusDownloaderService {
|
||||
|
||||
return tokenObs$.pipe(
|
||||
switchMap(token => {
|
||||
isOculusTokenValid(token, log.info); // Log token validity
|
||||
if(!downloadInfo.isVerification){
|
||||
return this.createDownloadVersion(downloadInfo.bsVersion).then(({version, dest}) => ({token, version, dest}))
|
||||
}
|
||||
@@ -140,7 +141,7 @@ export class BsOculusDownloaderService {
|
||||
progress => ({...progress, data: version})
|
||||
));
|
||||
}),
|
||||
finalize(() => downloadVersion && this.versions.setVersionMetadata(downloadVersion, "store", BsStore.OCULUS)),
|
||||
finalize(() => downloadVersion && this.versions.initVersionMetadata(downloadVersion, { store: BsStore.OCULUS })),
|
||||
finalize(() => this.oculusDownloader.stopDownload())
|
||||
);
|
||||
}
|
||||
@@ -170,7 +171,7 @@ export class BsOculusDownloaderService {
|
||||
map(progress => ({...progress, data: version})),
|
||||
);
|
||||
}),
|
||||
finalize(() => downloadVersion && this.versions.setVersionMetadata(downloadVersion, "store", BsStore.OCULUS)),
|
||||
finalize(() => downloadVersion && this.versions.initVersionMetadata(downloadVersion, { store: BsStore.OCULUS })),
|
||||
finalize(() => this.oculusDownloader.stopDownload()),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export class BsSteamDownloaderService {
|
||||
const downloadVersion: BSVersion = {
|
||||
...downloadInfos.bsVersion,
|
||||
...(path.basename(dest) !== downloadInfos.bsVersion.BSVersion && { name: path.basename(dest) }),
|
||||
metadata: { store: BsStore.STEAM }
|
||||
metadata: { store: BsStore.STEAM, id: "" }
|
||||
};
|
||||
|
||||
const depotDownloaderOptions: DepotDownloaderArgsOptions = {
|
||||
@@ -118,7 +118,7 @@ export class BsSteamDownloaderService {
|
||||
}
|
||||
return event;
|
||||
}),
|
||||
finalize(() => this.localVersionService.setVersionMetadata(version, "store", BsStore.STEAM))
|
||||
finalize(() => this.localVersionService.initVersionMetadata(version, { store: BsStore.STEAM }))
|
||||
).subscribe(sub);
|
||||
|
||||
}).catch(err => sub.error({
|
||||
|
||||
@@ -3,7 +3,6 @@ import { AvailableVersionItem } from "./available-version-item.component";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { AvailableVersionsContext } from "renderer/pages/available-versions-list.components";
|
||||
import equal from "fast-deep-equal";
|
||||
import { swapElements } from "shared/helpers/array.helpers";
|
||||
|
||||
type Props = {
|
||||
versions: BSVersion[]
|
||||
@@ -21,10 +20,12 @@ export function AvailableVersionsSlide({ versions }: Props) {
|
||||
}
|
||||
|
||||
const getVersions = () => {
|
||||
if(!versions?.length) { return []; }
|
||||
const recommendedIndex = versions.findIndex(v => v.recommended);
|
||||
if(recommendedIndex === -1) { return versions; }
|
||||
return swapElements(recommendedIndex, 0, [...versions]);
|
||||
const copy = [...(versions ?? [])];
|
||||
const recommendedVersion = copy.find(v => v.recommended);
|
||||
if(!recommendedVersion) { return copy; }
|
||||
copy.splice(copy.indexOf(recommendedVersion), 1);
|
||||
copy.unshift(recommendedVersion);
|
||||
return copy;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+4
-8
@@ -7,8 +7,6 @@ import { useState } from "react";
|
||||
import tailwindConfig from "../../../../../../tailwind.config";
|
||||
import Color from "color";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { followCursor } from "tippy.js";
|
||||
|
||||
export const ChooseStore: ModalComponent<BsStore> = ({ resolver }) => {
|
||||
|
||||
@@ -49,12 +47,10 @@ export const ChooseStore: ModalComponent<BsStore> = ({ resolver }) => {
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.choose-store.title")}</h1>
|
||||
<p className="w-auto text-gray-800 dark:text-gray-200 text-center">{t("modals.choose-store.body")}</p>
|
||||
<div className="flex flex-row w-full flex-grow gap-3">
|
||||
<Tippy className="!bg-neutral-900" content={t("modals.choose-store.unavailable")} allowHTML hideOnClick={false} followCursor plugins={[followCursor]} duration={200} arrow={false} maxWidth={300}>
|
||||
<div className="flex-grow basis-0 flex flex-col gap-2 text-center px-5 pt-3 pb-1 rounded-md border-main-color-3 border-2 cursor-not-allowed" style={{backgroundColor: oculusHover ? bg.dim : bg.bright}}>
|
||||
<OculusIcon className="flex-grow aspect-square text-black bg-white rounded-full p-5"/>
|
||||
<h2 className="font-bold">Oculus Store (PC)</h2>
|
||||
</div>
|
||||
</Tippy>
|
||||
<div className="flex flex-col flex-grow basis-0 gap-2 text-center px-5 pt-3 pb-1 rounded-md border-main-color-3 border-2 cursor-pointer" onMouseEnter={() => setOculusHover(true)} onMouseLeave={() => setOculusHover(false)} onClick={() => chooseStore(BsStore.OCULUS)} style={{backgroundColor: oculusHover ? bg.dim : bg.bright}}>
|
||||
<OculusIcon className="flex-grow aspect-square text-black bg-white rounded-full p-5"/>
|
||||
<h2 className="font-bold">Oculus Store (PC)</h2>
|
||||
</div>
|
||||
<div className="flex flex-col flex-grow basis-0 gap-2 text-center px-5 pt-3 pb-1 rounded-md border-main-color-3 border-2 cursor-pointer" onMouseEnter={() => setSteamHover(true)} onMouseLeave={() => setSteamHover(false)} onClick={() => chooseStore(BsStore.STEAM)} style={{backgroundColor: steamHover ? bg.dim : bg.bright}}>
|
||||
<SteamIcon className="flex-grow"/>
|
||||
<h2 className="font-bold">Steam</h2>
|
||||
|
||||
+176
-24
@@ -5,55 +5,207 @@ import BeatWaiting from "../../../../../../assets/images/apngs/beat-impatient.pn
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useState } from "react";
|
||||
import { isOculusTokenValid } from "shared/helpers/oculus.helpers";
|
||||
import crypto from "crypto-js";
|
||||
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { tryit } from "shared/helpers/error.helpers";
|
||||
import { logRenderError } from "renderer";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
|
||||
const OCULUS_TOKEN_STORAGE_KEY = "meta-token";
|
||||
|
||||
export const EnterMetaTokenModal: ModalComponent<string> = ({resolver}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const [token, setToken] = useState("");
|
||||
const [showToken, setShowToken] = useState(false);
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const submit = () => {
|
||||
const [passwordView, setPasswordView] = useState(() => !!config.get(OCULUS_TOKEN_STORAGE_KEY));
|
||||
|
||||
const submit = (token: string) => {
|
||||
resolver({exitCode: ModalExitCode.COMPLETED, data: token});
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
resolver({exitCode: ModalExitCode.CANCELED});
|
||||
}
|
||||
|
||||
const isTokenValid = (() => {
|
||||
return isOculusTokenValid(token, logRenderError);
|
||||
})();
|
||||
|
||||
return (
|
||||
<form className="flex flex-col w-80 gap-4">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("modals.enter-meta-token.title")}</h1>
|
||||
|
||||
<BsmImage className="h-20 w-20 mx-auto" image={BeatWaiting}/>
|
||||
{passwordView ? (
|
||||
<EnterPasswordView onValid={submit} onCancel={cancel} dontHaveToken={() => setPasswordView(() => false)}/>
|
||||
) : (
|
||||
<EnterOculusTokenView onValid={submit} onCancel={cancel} alreadyHaveToken={() => setPasswordView(() => true)}/>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
<p>{t("modals.enter-meta-token.body.need-token")}</p>
|
||||
const EnterPasswordView = ({onValid, onCancel, dontHaveToken}: {onValid: (token: string) => void, onCancel: () => void, dontHaveToken: () => void}) => {
|
||||
|
||||
<a href="https://github.com/Zagrios/bs-manager/wiki/How-to-obtain-your-Oculus-Token" target="_blank" className="underline">{t("modals.enter-meta-token.body.how-obtain-token")}</a>
|
||||
|
||||
<div>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<label className="font-bold cursor-pointer tracking-wide inline-block" htmlFor="meta_token">{t("modals.enter-meta-token.body.input-label")}</label>
|
||||
{!isTokenValid && token.length > 0 && (
|
||||
<span className="text-orange-700 dark:text-orange-400 text-xs whitespace-normal min-w-0">{t("modals.enter-meta-token.body.token-is-invalid")}</span>
|
||||
)}
|
||||
const t = useTranslation();
|
||||
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const getTokenFromStorage = () => {
|
||||
const cryptedToken = config.get<string>(OCULUS_TOKEN_STORAGE_KEY);
|
||||
if(!cryptedToken) return null;
|
||||
const { result: token, error } = tryit(() => crypto.AES.decrypt(cryptedToken, password).toString(crypto.enc.Utf8));
|
||||
|
||||
if(error){
|
||||
logRenderError(error, "Often indicate that the password is not the same as the one used to save the token. This error can occur when the use is writing the password.");
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
const isTokenValid = () => {
|
||||
const token = getTokenFromStorage();
|
||||
return isOculusTokenValid(token);
|
||||
}
|
||||
|
||||
const valid = () => {
|
||||
const token = getTokenFromStorage();
|
||||
onValid(token);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{t("modals.enter-meta-token.body.info-enter-password")}</p>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div>
|
||||
<PasswordInput onChange={v => setPassword(v.password)} value={password}/>
|
||||
</div>
|
||||
<div className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md flex box-border h-9">
|
||||
<input className="grow px-1 py-[2px] outline-none bg-transparent" onChange={e => setToken(e.target.value)} value={token} type={showToken ? "text" : "password"} name="meta_token" id="meta_token" placeholder="OCAStr43sdx2..." />
|
||||
<BsmButton className="shrink-0 m-1 rounded-md p-0.5 !bg-light-main-color-3 dark:!bg-main-color-3" icon={showToken ? "eye-cross" : "eye"} withBar={false} onClick={() => setShowToken(prev => !prev)} />
|
||||
|
||||
<div>
|
||||
<span className="underline italic text-sm float-right cursor-pointer" onClick={dontHaveToken}>{t("modals.enter-meta-token.body.enter-oculus-token")}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row gap-2">
|
||||
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="cancel" text="misc.cancel" withBar={false} onClick={cancel}/>
|
||||
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="primary" text="modals.enter-meta-token.valid-btn" withBar={false} onClick={submit} disabled={!isTokenValid}/>
|
||||
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="cancel" text="misc.cancel" withBar={false} onClick={onCancel}/>
|
||||
<Tippy className="!bg-neutral-900" disabled={!password || isTokenValid()} arrow={false} content={t("modals.enter-meta-token.body.info-disabled-btn-password")} hideOnClick={false}>
|
||||
<div className="w-full">
|
||||
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="primary" text="modals.enter-meta-token.valid-btn" withBar={false} onClick={valid} disabled={!isTokenValid()}/>
|
||||
</div>
|
||||
</Tippy>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
const EnterOculusTokenView = ({onValid, onCancel, alreadyHaveToken}: {onValid: (token: string) => void, onCancel: () => void, alreadyHaveToken: () => void}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const [token, setToken] = useState("");
|
||||
const [showToken, setShowToken] = useState(false);
|
||||
const [stay, setStay] = useState(false);
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordValid, setPasswordValid] = useState(false);
|
||||
|
||||
const storeToken = (token: string, password: string) => {
|
||||
const cryptedToken = crypto.AES.encrypt(token, password).toString();
|
||||
config.set(OCULUS_TOKEN_STORAGE_KEY, cryptedToken);
|
||||
}
|
||||
|
||||
const valid = () => {
|
||||
|
||||
if(stay){
|
||||
storeToken(token, password);
|
||||
} else {
|
||||
config.delete(OCULUS_TOKEN_STORAGE_KEY);
|
||||
}
|
||||
|
||||
onValid(token);
|
||||
}
|
||||
|
||||
const isCryptedTokenPresent = () => {
|
||||
return !!config.get(OCULUS_TOKEN_STORAGE_KEY);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{t("modals.enter-meta-token.body.info-enter-token")}</p>
|
||||
<a href="https://github.com/Zagrios/bs-manager/wiki/How-to-obtain-your-Oculus-Token" target="_blank" className="underline">{t("modals.enter-meta-token.body.how-obtain-token")}</a>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<label className="font-bold cursor-pointer tracking-wide inline-block" htmlFor="meta_token">{t("modals.enter-meta-token.body.oculus-token")}</label>
|
||||
{!isOculusTokenValid(token) && token.length > 0 && (
|
||||
<span className="text-orange-700 dark:text-orange-400 text-xs whitespace-normal min-w-0">{t("modals.enter-meta-token.body.token-is-invalid")}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md flex box-border h-9">
|
||||
<input className="grow px-1 py-[2px] outline-none bg-transparent" onChange={e => setToken(e.target.value)} value={token} type={showToken ? "text" : "password"} name="meta_token" id="meta_token" placeholder="FRLAStr43sdx2..." />
|
||||
<BsmButton className="shrink-0 m-1 rounded-md p-0.5 !bg-light-main-color-3 dark:!bg-main-color-3" icon={showToken ? "eye-cross" : "eye"} withBar={false} onClick={() => setShowToken(prev => !prev)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tippy className="!bg-neutral-900" arrow={false} content={t("modals.enter-meta-token.body.save-token-info")}>
|
||||
<div className="flex flex-row items-center gap-1 w-fit">
|
||||
<BsmCheckbox className="h-6 w-6 relative z-[1]" onChange={setStay} checked={stay}/>
|
||||
<span className="font-bold cursor-help">{t("modals.enter-meta-token.body.save-my-token")}</span>
|
||||
</div>
|
||||
</Tippy>
|
||||
|
||||
<div className="grid grid-rows-[0fr] transition-[grid-template-rows]" style={{gridTemplateRows: stay && "1fr"}}>
|
||||
<div className="overflow-hidden">
|
||||
<PasswordInput onChange={v => {setPassword(v.password); setPasswordValid(v.valid)}} value={password}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCryptedTokenPresent() && (
|
||||
<div>
|
||||
<span className="underline italic text-sm float-right cursor-pointer" onClick={alreadyHaveToken}>{t("modals.enter-meta-token.body.have-token-saved")}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row gap-2">
|
||||
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="cancel" text="misc.cancel" withBar={false} onClick={onCancel}/>
|
||||
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="primary" text="modals.enter-meta-token.valid-btn" withBar={false} onClick={valid} disabled={!isOculusTokenValid(token) || (stay && !passwordValid)}/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
const PasswordInput = ({onChange, value}: {onChange: (value : {password: string, valid: boolean}) => void, value: string}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const isPasswordValid = (password: string) => {
|
||||
return password.length >= 8;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<label className="font-bold cursor-pointer tracking-wide inline-block" htmlFor="password">{t("modals.enter-meta-token.body.password")}</label>
|
||||
{!isPasswordValid(value) && value.length > 0 && (
|
||||
<span className="text-orange-700 dark:text-orange-400 text-xs whitespace-normal min-w-0">{t("modals.enter-meta-token.body.password-too-short")}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md flex box-border h-9">
|
||||
<input className="grow px-1 py-[2px] outline-none bg-transparent" onChange={e => onChange({password: e.target.value, valid: isPasswordValid(e.target.value)})} value={value} type={showPassword ? "text" : "password"} name="password" id="password" placeholder={t("modals.enter-meta-token.body.password")} />
|
||||
<BsmButton className="shrink-0 m-1 rounded-md p-0.5 !bg-light-main-color-3 dark:!bg-main-color-3" icon={showPassword ? "eye-cross" : "eye"} withBar={false} onClick={() => setShowPassword(prev => !prev)} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -18,15 +18,10 @@ export function SettingRadioArray<T>({ id, items, selectedItemId, selectedItemVa
|
||||
return item.id === selectedItemId || item.value === selectedItemValue;
|
||||
}
|
||||
|
||||
const selectItem = (item: RadioItem<T>) => {
|
||||
if(item.disabled){ return; }
|
||||
onItemSelected?.(item);
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={id} className="w-full flex gap-1.5" style={{flexDirection: direction}}>
|
||||
{items.map(i => (
|
||||
<div onClick={() => selectItem(i)} key={i.id} className={`py-3 w-full flex ${i.disabled ? "cursor-not-allowed" : "cursor-pointer"} ${i.disabled && "brightness-75"} justify-between items-center rounded-md px-2 transition-colors duration-200 ${isSelected(i) ? "bg-light-main-color-3 dark:bg-main-color-3" : "bg-light-main-color-1 dark:bg-main-color-1"} ${i.className}`}>
|
||||
<div onClick={() => onItemSelected(i)} key={i.id} className={`py-3 w-full flex cursor-pointer justify-between items-center rounded-md px-2 transition-colors duration-200 ${isSelected(i) ? "bg-light-main-color-3 dark:bg-main-color-3" : "bg-light-main-color-1 dark:bg-main-color-1"} ${i.className}`}>
|
||||
<div className="flex items-center">
|
||||
<div className="h-5 rounded-full aspect-square border-2 border-gray-800 dark:border-white p-[3px] mr-2">
|
||||
<motion.span initial={{ scale: 0 }} animate={{ scale: isSelected(i) ? 1 : 0 }} className="h-full w-full block bg-gray-800 dark:bg-white rounded-full" />
|
||||
@@ -52,5 +47,4 @@ export interface RadioItem<T> {
|
||||
textIcon?: string;
|
||||
className?: string;
|
||||
value?: T;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ export function SettingsPage() {
|
||||
const themeSelected = useObservable(themeService.theme$, "os");
|
||||
const languageSelected = useObservable(i18nService.currentLanguage$, i18nService.getFallbackLanguage());
|
||||
const downloadStore = useObservable(bsDownloader.defaultStore$);
|
||||
|
||||
const [installationFolder, setInstallationFolder] = useState(null);
|
||||
const [showSupporters, setShowSupporters] = useState(false);
|
||||
const [mapDeepLinksEnabled, setMapDeepLinksEnabled] = useState(false);
|
||||
@@ -235,8 +236,8 @@ export function SettingsPage() {
|
||||
<SettingContainer id="choose-default-store" minorTitle="pages.settings.steam-and-oculus.download-platform.title" description="pages.settings.steam-and-oculus.download-platform.desc" className="mt-3">
|
||||
<SettingRadioArray items={[
|
||||
{ id: 1, text: "Steam", value: BsStore.STEAM, icon: <SteamIcon className="h-6 w-6 float-left"/> },
|
||||
{ id: 2, text: "Oculus Store (PC)", value: BsStore.OCULUS, icon: <OculusIcon className="h-6 w-6 float-left bg-white text-black rounded-full p-0.5"/>, disabled: true},
|
||||
{ id: 0, text: t("pages.settings.steam-and-oculus.download-platform.always-ask"), value: undefined, },
|
||||
{ id: 2, text: "Oculus Store (PC)", value: BsStore.OCULUS, icon: <OculusIcon className="h-6 w-6 float-left bg-white text-black rounded-full p-0.5"/>},
|
||||
{ id: 0, text: t("pages.settings.steam-and-oculus.download-platform.always-ask"), value: null, },
|
||||
]} selectedItemValue={downloadStore} onItemSelected={handleChangeBsStore}/>
|
||||
</SettingContainer>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export class BsDownloaderService extends AbstractBsDownloaderService {
|
||||
private readonly oculusDownloader: OculusDownloaderService;
|
||||
private readonly versionManager: BSVersionManagerService;
|
||||
|
||||
private readonly SELECTED_STORE_TO_DOWNLOAD_KET = "selectedStoreToDownload";
|
||||
private readonly SELECTED_STORE_TO_DOWNLOAD_KEY = "selectedStoreToDownload";
|
||||
|
||||
private constructor(){
|
||||
super();
|
||||
@@ -55,11 +55,11 @@ export class BsDownloaderService extends AbstractBsDownloaderService {
|
||||
this._isVerifying$.next(false);
|
||||
}
|
||||
|
||||
public get defaultStore(): BsStore | undefined { return this.config.get<BsStore>(this.SELECTED_STORE_TO_DOWNLOAD_KET); }
|
||||
public get defaultStore$(): Observable<BsStore | undefined> { return this.config.watch(this.SELECTED_STORE_TO_DOWNLOAD_KET); }
|
||||
public get defaultStore(): BsStore | undefined { return this.config.get<BsStore>(this.SELECTED_STORE_TO_DOWNLOAD_KEY); }
|
||||
public get defaultStore$(): Observable<BsStore | undefined> { return this.config.watch(this.SELECTED_STORE_TO_DOWNLOAD_KEY); }
|
||||
|
||||
public setDefaultStore(store: BsStore|undefined): void {
|
||||
this.config.set(this.SELECTED_STORE_TO_DOWNLOAD_KET, store);
|
||||
this.config.set(this.SELECTED_STORE_TO_DOWNLOAD_KEY, store);
|
||||
}
|
||||
|
||||
public async chooseStoreToDownloadFrom(): Promise<BsStore | undefined> {
|
||||
@@ -75,7 +75,7 @@ export class BsDownloaderService extends AbstractBsDownloaderService {
|
||||
public async downloadVersion(version: BSVersion, from?: BsStore): Promise<BSVersion> {
|
||||
|
||||
if(!from){
|
||||
from = this.defaultStore === BsStore.STEAM ? BsStore.STEAM : await this.chooseStoreToDownloadFrom();
|
||||
from = this.defaultStore ?? await this.chooseStoreToDownloadFrom();
|
||||
}
|
||||
|
||||
return this.getStoreDownloader(from).downloadBsVersion(version).then(() => {
|
||||
|
||||
@@ -6,7 +6,6 @@ import { ProgressBarService } from "../progress-bar.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
import { NotificationService } from "../notification.service";
|
||||
import { ModalExitCode, ModalService } from "../modale.service";
|
||||
import { LoginToMetaModal, MetaAuthMethod } from "renderer/components/modal/modal-types/bs-downgrade/login-to-meta-modal.component";
|
||||
import { DownloaderServiceInterface } from "./bs-store-downloader.interface";
|
||||
import { AbstractBsDownloaderService } from "./abstract-bs-downloader.service";
|
||||
import { DownloadInfo } from "main/services/bs-version-download/bs-steam-downloader.service";
|
||||
@@ -69,14 +68,6 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme
|
||||
);
|
||||
}
|
||||
|
||||
private tryAutoDownload(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
|
||||
const ignoreCode = [MetaAuthErrorCodes.NO_META_AUTH_TOKEN, OculusDownloaderErrorCodes.DOWNLOAD_CANCELLED];
|
||||
return this.handleDownload(
|
||||
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-auto-download", { args: downloadInfo }),
|
||||
ignoreCode
|
||||
);
|
||||
}
|
||||
|
||||
private startDownloadBsVersion(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
|
||||
const ignoreCode = [MetaAuthErrorCodes.META_LOGIN_WINDOW_CLOSED_BY_USER, OculusDownloaderErrorCodes.DOWNLOAD_CANCELLED];
|
||||
return this.handleDownload(
|
||||
@@ -87,38 +78,10 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme
|
||||
|
||||
private async doDownloadBsVersion(bsVersion: BSVersion, isVerification: boolean): Promise<BSVersion> {
|
||||
|
||||
let autoDownloadFailed = false;
|
||||
const autoDownloadFailed = false;
|
||||
|
||||
return (async () => {
|
||||
|
||||
const autoDownload = await lastValueFrom(this.tryAutoDownload({ bsVersion, isVerification })).then(() => true).catch((err: CustomError) => {
|
||||
const doNotRestartCodes: string[] = [
|
||||
OculusDownloaderErrorCodes.ALREADY_DOWNLOADING,
|
||||
OculusDownloaderErrorCodes.SOME_FILES_FAILED_TO_DOWNLOAD,
|
||||
OculusDownloaderErrorCodes.VERIFY_INTEGRITY_FAILED,
|
||||
OculusDownloaderErrorCodes.DOWNLOAD_CANCELLED
|
||||
];
|
||||
autoDownloadFailed = true;
|
||||
|
||||
if(doNotRestartCodes.includes(err?.code)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if(autoDownload){ return autoDownload; }
|
||||
|
||||
const {exitCode, data} = await this.modals.openModal(LoginToMetaModal)
|
||||
|
||||
if(exitCode !== ModalExitCode.COMPLETED){
|
||||
return false
|
||||
}
|
||||
|
||||
if(data.method === MetaAuthMethod.META){
|
||||
return lastValueFrom(this.startDownloadBsVersion({ bsVersion, isVerification, stay: data.stay })).then(() => true);
|
||||
}
|
||||
|
||||
const tokenRes = await this.modals.openModal(EnterMetaTokenModal);
|
||||
|
||||
if(tokenRes.exitCode !== ModalExitCode.COMPLETED){
|
||||
@@ -129,8 +92,6 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme
|
||||
|
||||
})().then(res => {
|
||||
|
||||
console.log("aaa", res);
|
||||
|
||||
if(autoDownloadFailed || !res){
|
||||
return bsVersion;
|
||||
}
|
||||
|
||||
@@ -30,10 +30,14 @@ export class ConfigurationService {
|
||||
public get<Type>(key: string | DefaultConfigKey): Type {
|
||||
const rawValue = (window.sessionStorage.getItem(key) ?? window.localStorage.getItem(key));
|
||||
const tryParse = tryit<Type>(() => JSON.parse(rawValue));
|
||||
if (!tryParse.result) {
|
||||
return defaultConfiguration[key as DefaultConfigKey];
|
||||
|
||||
const res = (tryParse.error ? rawValue : tryParse.result) as Type;
|
||||
|
||||
if(!res && Object.keys(defaultConfiguration).includes(key)){
|
||||
return defaultConfiguration[key as DefaultConfigKey] as Type;
|
||||
}
|
||||
return (tryParse.result ?? rawValue) as Type;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public set(key: string, value: unknown, persistant = true) {
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface BSVersion extends PartialBSVersion {
|
||||
}
|
||||
|
||||
export interface BSVersionMetadata {
|
||||
id: string;
|
||||
store: BsStore;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export function isOculusTokenValid(token: string, logger?: (...args: unknown[]) => void): boolean{
|
||||
|
||||
// Code taken from "https://github.com/ComputerElite/QuestAppVersionSwitcher" (TokenTools.cs)
|
||||
// Part of code taken from "https://github.com/ComputerElite/QuestAppVersionSwitcher" (TokenTools.cs)
|
||||
|
||||
logger?.("Checking if Oculus user token is valid");
|
||||
|
||||
@@ -12,12 +12,16 @@ export function isOculusTokenValid(token: string, logger?: (...args: unknown[])
|
||||
logger?.("Token contains %. Token most likely comes from an uri and won't work");
|
||||
return false;
|
||||
}
|
||||
if(!token.startsWith("OC")){
|
||||
logger?.("Token does not start with OC.");
|
||||
if(!token.startsWith("FRL")){
|
||||
logger?.("Tokens must start with 'FRL'.");
|
||||
return false;
|
||||
}
|
||||
if(token.includes("|")){
|
||||
logger?.("Token contains | which usually indicates an application token which is not valid for user tokens");
|
||||
logger?.("Token contains '|' which usually indicates an application token which is not valid for user tokens");
|
||||
return false;
|
||||
}
|
||||
if(token.includes(":")){
|
||||
logger?.("Token contains ':' wich can indicate that the user have copied the 'access_token' field with the token");
|
||||
return false;
|
||||
}
|
||||
if(token.match(/OC\d{15}/)){
|
||||
@@ -25,7 +29,7 @@ export function isOculusTokenValid(token: string, logger?: (...args: unknown[])
|
||||
return false;
|
||||
}
|
||||
|
||||
logger?.("Oculus user token is valid");
|
||||
logger?.("Oculus user token is valid.", `token lenght : ${token.length}`);
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user