mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
Compare commits
182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8b59cae05 | ||
|
|
45f2727415 | ||
|
|
7e44b3dedb | ||
|
|
0dea700479 | ||
|
|
56247eaf7b | ||
|
|
590a15e534 | ||
|
|
fb1cc9b82c | ||
|
|
7595f19af3 | ||
|
|
e978d84f5f | ||
|
|
0157d546e4 | ||
|
|
4f5dc51a68 | ||
|
|
938650dbde | ||
|
|
bae9e57fcd | ||
|
|
6a673f0c6b | ||
|
|
a962d839a8 | ||
|
|
173fb41e63 | ||
|
|
26cbeee5af | ||
|
|
f6b5263814 | ||
|
|
3b7ddd0170 | ||
|
|
600cbfe861 | ||
|
|
994f4568a4 | ||
|
|
1de973ebd0 | ||
|
|
4f6d3d7057 | ||
|
|
124e38c782 | ||
|
|
c9eed85a00 | ||
|
|
988c01f506 | ||
|
|
5450443022 | ||
|
|
590a1b354d | ||
|
|
2dcb629c2d | ||
|
|
96feaf8d42 | ||
|
|
4cb3258e17 | ||
|
|
9b9b3f73d0 | ||
|
|
de36965017 | ||
|
|
e9fddd2456 | ||
|
|
8b4791f1f4 | ||
|
|
5f9397f6db | ||
|
|
870e45991b | ||
|
|
38e94c92d7 | ||
|
|
93fbf7657d | ||
|
|
0fc6d69851 | ||
|
|
7f600a0cbf | ||
|
|
5bc424796a | ||
|
|
7f33b63bed | ||
|
|
57e0fb493f | ||
|
|
730ea4f2b9 | ||
|
|
948965dda5 | ||
|
|
8cfe5b4d34 | ||
|
|
a2e41b81a3 | ||
|
|
ee4639e041 | ||
|
|
6e6469d90f | ||
|
|
a53793a76b | ||
|
|
d046f1ed21 | ||
|
|
da1ac788fb | ||
|
|
1980560a2d | ||
|
|
7166f66a9e | ||
|
|
cc3fc10ddf | ||
|
|
15ecba1f6e | ||
|
|
2828640ed7 | ||
|
|
341903fc3e | ||
|
|
20c001914a | ||
|
|
77e9de704d | ||
|
|
34ec8467ec | ||
|
|
eeae140a41 | ||
|
|
e277af0e4b | ||
|
|
8a67492cf8 | ||
|
|
981dbceb93 | ||
|
|
aaee27732d | ||
|
|
e7acce2dfc | ||
|
|
56b15bf52a | ||
|
|
2f8fe67f9f | ||
|
|
b09e91a1cf | ||
|
|
9418c4acf7 | ||
|
|
0451bc55aa | ||
|
|
2840110a21 | ||
|
|
8c4eacf045 | ||
|
|
179785432d | ||
|
|
c3da39205f | ||
|
|
f071d1006b | ||
|
|
6da1832799 | ||
|
|
df0e124c3a | ||
|
|
25f1a72b48 | ||
|
|
2b5e76ffdd | ||
|
|
9d75e3ad6f | ||
|
|
ab5024ec4a | ||
|
|
bdaf4d6d9b | ||
|
|
6d53aaa631 | ||
|
|
e67d605949 | ||
|
|
b82840df3b | ||
|
|
42e0df29ee | ||
|
|
0745f5b401 | ||
|
|
1ab5b8bd00 | ||
|
|
dc93ef9134 | ||
|
|
943cfc2913 | ||
|
|
6b125aff27 | ||
|
|
3469b624d5 | ||
|
|
c498e331c7 | ||
|
|
c8165c10bf | ||
|
|
8af29abd92 | ||
|
|
5662ddd2be | ||
|
|
8f1e71010b | ||
|
|
71ff6c3c44 | ||
|
|
74cd60ff3d | ||
|
|
d09a441faa | ||
|
|
bef9ec30f9 | ||
|
|
3e33e1f4b3 | ||
|
|
c8485adac5 | ||
|
|
d623828fe5 | ||
|
|
0f5c7af703 | ||
|
|
b965d3aa0e | ||
|
|
1d6db5b76b | ||
|
|
0b896b8b7c | ||
|
|
93d12a9fb6 | ||
|
|
ce239e0e20 | ||
|
|
09af1d1f89 | ||
|
|
3b828ca0f4 | ||
|
|
4f4dd29e5f | ||
|
|
9189541c3a | ||
|
|
dcb6eb9ba6 | ||
|
|
e43365e568 | ||
|
|
9ee4e2e29b | ||
|
|
760030841a | ||
|
|
5fb08b39fa | ||
|
|
43558f8c0e | ||
|
|
554889fe7d | ||
|
|
25e1a15828 | ||
|
|
619090961b | ||
|
|
0ecd27d4d5 | ||
|
|
f585e343d9 | ||
|
|
0d68851cf4 | ||
|
|
36813d5f86 | ||
|
|
ad3b84b0ae | ||
|
|
9e020652c9 | ||
|
|
7af56cd7cc | ||
|
|
6dd454a982 | ||
|
|
fd80b85786 | ||
|
|
119af47d77 | ||
|
|
a419b9ae38 | ||
|
|
7d4d434164 | ||
|
|
5d304f9e13 | ||
|
|
a45e06efa3 | ||
|
|
e7292eb75e | ||
|
|
39f731352f | ||
|
|
9fa9f6d85a | ||
|
|
0a86ec89aa | ||
|
|
238d207590 | ||
|
|
98e2d2ec0d | ||
|
|
717dab5c90 | ||
|
|
d73a46aac2 | ||
|
|
9f9a4eba18 | ||
|
|
3bddd7e76b | ||
|
|
7cfc871be2 | ||
|
|
5705de7d7a | ||
|
|
b6fb29ca2d | ||
|
|
d094230614 | ||
|
|
742abb06ac | ||
|
|
e89f459c78 | ||
|
|
0dac4d5cf3 | ||
|
|
93fb26c89b | ||
|
|
446d6b75c0 | ||
|
|
8dd29c7461 | ||
|
|
0ad1a2e3fe | ||
|
|
3c03d5ce16 | ||
|
|
8d8b714c68 | ||
|
|
7d79048a25 | ||
|
|
4a3ba43dae | ||
|
|
dc413736e8 | ||
|
|
2d98addd02 | ||
|
|
b85e712d8c | ||
|
|
582c276e95 | ||
|
|
430b07eb89 | ||
|
|
b46f46bc45 | ||
|
|
fd41ec5070 | ||
|
|
25d0f77c1d | ||
|
|
c5b2a8242c | ||
|
|
8b7ce6b062 | ||
|
|
db58ff0ba3 | ||
|
|
e951e11e62 | ||
|
|
0b854eda7c | ||
|
|
670df826af | ||
|
|
912f9611ea | ||
|
|
d8254353b5 | ||
|
|
2edb96ba5b |
@@ -1,5 +1,3 @@
|
||||
MAIN_VITE_API_URL=API_URL
|
||||
MAIN_VITE_AUTH_URL=AUTH_URL
|
||||
MAIN_VITE_STEAMGRIDDB_API_KEY=YOUR_API_KEY
|
||||
MAIN_VITE_SENTRY_DSN=YOUR_SENTRY_DSN
|
||||
SENTRY_AUTH_TOKEN=
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us improve. Write in English, please.
|
||||
description: Create a report to help us improve. Write in English.
|
||||
title: "[BUG] Write a title for your bug"
|
||||
labels: ["bug"]
|
||||
body:
|
||||
@@ -61,3 +61,5 @@ body:
|
||||
required: true
|
||||
- label: I am aware that Hydra team does not offer any support or help regarding the downloaded games.
|
||||
required: true
|
||||
- label: I have read the [Frequently Asked Questions (FAQ)](https://github.com/hydralauncher/hydra/wiki/FAQ).
|
||||
required: true
|
||||
|
||||
12
.github/pull-request-template.md
vendored
Normal file
12
.github/pull-request-template.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- Please be sure to add one of the labels in the right hand side Labels option before creating a PR: [feature], [fix], [documentation],[translation]. This will allow Actions to automatically categorize PRs when generating Releases. -->
|
||||
|
||||
**When submitting this pull request, I confirm the following (please check the boxes):**
|
||||
|
||||
- [ ] I have read and understood the [Contributor Guidelines](https://github.com/hydralauncher/hydra?tab=readme-ov-file#ways-you-can-contribute).
|
||||
- [ ] I have checked that there are no duplicate pull requests related to this request.
|
||||
- [ ] I have considered, and confirm that this submission is valuable to others.
|
||||
- [ ] I accept that this submission may not be used and the pull request may be closed at the discretion of the maintainers.
|
||||
|
||||
**Fill in the PR content:**
|
||||
|
||||
-
|
||||
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -2,6 +2,9 @@ name: Build
|
||||
|
||||
on: pull_request
|
||||
|
||||
env:
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
@@ -17,7 +20,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.11.1
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -43,8 +46,9 @@ jobs:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
|
||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
|
||||
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
|
||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Windows
|
||||
@@ -54,8 +58,9 @@ jobs:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
|
||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
|
||||
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
|
||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create artifact
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.11.1
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.11.1
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -45,10 +45,10 @@ jobs:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
|
||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
|
||||
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
|
||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: yarn build:win
|
||||
@@ -56,10 +56,10 @@ jobs:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
|
||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
|
||||
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
|
||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
dist/*.pacman
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,5 +9,4 @@ out
|
||||
*.log*
|
||||
.env
|
||||
.vite
|
||||
sentry.properties
|
||||
ludusavi/
|
||||
24
README.md
24
README.md
@@ -13,19 +13,19 @@
|
||||
[](https://github.com/hydralauncher/hydra/actions)
|
||||
[](https://github.com/hydralauncher/hydra/releases)
|
||||
|
||||
[](./README.pt-BR.md)
|
||||
[](./docs/README.pt-BR.md)
|
||||
[](./README.md)
|
||||
[](./README.ru.md)
|
||||
[](./README.uk-UA.md)
|
||||
[](./README.be.md)
|
||||
[](./README.es.md)
|
||||
[](./README.fr.md)
|
||||
[](./README.de.md)
|
||||
[](./README.it.md)
|
||||
[](./README.cs.md)
|
||||
[](./README.da.md)
|
||||
[](./README.nb.md)
|
||||
[](./README.et.md)
|
||||
[](./docs/README.ru.md)
|
||||
[](./docs/README.uk-UA.md)
|
||||
[](./docs/README.be.md)
|
||||
[](./docs/README.es.md)
|
||||
[](./docs/README.fr.md)
|
||||
[](./docs/README.de.md)
|
||||
[](./docs/README.it.md)
|
||||
[](./docs/README.cs.md)
|
||||
[](./docs/README.da.md)
|
||||
[](./docs/README.nb.md)
|
||||
[](./docs/README.et.md)
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ extraResources:
|
||||
- hydra-download-manager
|
||||
- seeds
|
||||
- from: node_modules/create-desktop-shortcuts/src/windows.vbs
|
||||
- from: resources/achievement.wav
|
||||
files:
|
||||
- "!**/.vscode/*"
|
||||
- "!src/*"
|
||||
|
||||
@@ -6,16 +6,9 @@ import {
|
||||
externalizeDepsPlugin,
|
||||
} from "electron-vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
||||
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
|
||||
import svgr from "vite-plugin-svgr";
|
||||
|
||||
const sentryPlugin = sentryVitePlugin({
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
org: "hydra-launcher",
|
||||
project: "hydra-launcher",
|
||||
});
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
loadEnv(mode);
|
||||
|
||||
@@ -35,7 +28,7 @@ export default defineConfig(({ mode }) => {
|
||||
"@shared": resolve("src/shared"),
|
||||
},
|
||||
},
|
||||
plugins: [externalizeDepsPlugin(), swcPlugin(), sentryPlugin],
|
||||
plugins: [externalizeDepsPlugin(), swcPlugin()],
|
||||
},
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
@@ -51,7 +44,7 @@ export default defineConfig(({ mode }) => {
|
||||
"@shared": resolve("src/shared"),
|
||||
},
|
||||
},
|
||||
plugins: [svgr(), react(), vanillaExtractPlugin(), sentryPlugin],
|
||||
plugins: [svgr(), react(), vanillaExtractPlugin()],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydralauncher",
|
||||
"version": "3.0.1",
|
||||
"version": "3.0.8",
|
||||
"description": "Hydra",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Los Broxas",
|
||||
@@ -38,7 +38,6 @@
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@primer/octicons-react": "^19.9.0",
|
||||
"@reduxjs/toolkit": "^2.2.3",
|
||||
"@sentry/electron": "^5.1.0",
|
||||
"@vanilla-extract/css": "^1.14.2",
|
||||
"@vanilla-extract/dynamic": "^2.1.1",
|
||||
"@vanilla-extract/recipes": "^0.5.2",
|
||||
@@ -51,25 +50,25 @@
|
||||
"color.js": "^1.2.0",
|
||||
"create-desktop-shortcuts": "^1.11.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"dexie": "^4.0.8",
|
||||
"dexie": "^4.0.9",
|
||||
"electron-log": "^5.2.0",
|
||||
"electron-updater": "^6.3.9",
|
||||
"file-type": "^19.6.0",
|
||||
"flexsearch": "^0.7.43",
|
||||
"i18next": "^23.11.2",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
"icojs": "^0.19.4",
|
||||
"jsdom": "^24.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"knex": "^3.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lottie-react": "^2.4.0",
|
||||
"parse-torrent": "^11.0.17",
|
||||
"piscina": "^4.5.1",
|
||||
"piscina": "^4.7.0",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-loading-skeleton": "^3.4.0",
|
||||
"react-redux": "^9.1.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"sound-play": "^1.1.0",
|
||||
"sudo-prompt": "^9.2.1",
|
||||
"tar": "^7.4.3",
|
||||
"typeorm": "^0.3.20",
|
||||
@@ -79,12 +78,11 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@commitlint/cli": "^19.5.0",
|
||||
"@commitlint/config-conventional": "^19.5.0",
|
||||
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
||||
"@electron-toolkit/eslint-config-ts": "^2.0.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@sentry/vite-plugin": "^2.20.1",
|
||||
"@swc/core": "^1.4.16",
|
||||
"@types/auto-launch": "^5.0.5",
|
||||
"@types/color": "^3.0.6",
|
||||
@@ -96,6 +94,7 @@
|
||||
"@types/parse-torrent": "^5.8.7",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/sound-play": "^1.1.3",
|
||||
"@types/user-agents": "^1.0.4",
|
||||
"@vanilla-extract/vite-plugin": "^4.0.7",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
@@ -110,6 +109,7 @@
|
||||
"prettier": "^3.2.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass-embedded": "^1.80.6",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.12",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
libtorrent
|
||||
cx_Freeze
|
||||
cx_Freeze == 7.2.3
|
||||
cx_Logging; sys_platform == 'win32'
|
||||
pywin32; sys_platform == 'win32'
|
||||
psutil
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
381
src/locales/bg/translation.json
Normal file
381
src/locales/bg/translation.json
Normal file
@@ -0,0 +1,381 @@
|
||||
{
|
||||
"language_name": "Български",
|
||||
"app": {
|
||||
"successfully_signed_in": "Успешно вписване"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Препоръчани",
|
||||
"surprise_me": "Изненадай ме",
|
||||
"no_results": "Не са намерени резултати",
|
||||
"start_typing": "Търсене...",
|
||||
"hot": "Актуално сега",
|
||||
"weekly": "📅 Най-доброто от седмицата",
|
||||
"achievements": "🏆 Игри, които да победите"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Каталог",
|
||||
"downloads": "Изтегляния",
|
||||
"settings": "Настройки",
|
||||
"my_library": "Моята библиотека",
|
||||
"downloading_metadata": "{{title}} (Сваляне на метаданни…)",
|
||||
"paused": "{{title}} (Пауза)",
|
||||
"downloading": "{{title}} ({{percentage}} - Изтегляне…)",
|
||||
"filter": "Търсене по име",
|
||||
"home": "Начало",
|
||||
"queued": "{{title}} (Опашка)",
|
||||
"game_has_no_executable": "Играта няма избран изпълним файл",
|
||||
"sign_in": "Вписване",
|
||||
"friends": "Приятели",
|
||||
"need_help": "Имате нужда от помощ??"
|
||||
},
|
||||
"header": {
|
||||
"search": "Търсене",
|
||||
"home": "Начало",
|
||||
"catalogue": "Каталог",
|
||||
"downloads": "Изтегляния",
|
||||
"search_results": "Резултати от търсене",
|
||||
"settings": "Настройки",
|
||||
"version_available_install": "Версия {{version}} е налична. Кликни тук, за да рестартирате и инсталирате.",
|
||||
"version_available_download": "Версия {{version}} е налична. Кликни тук за изтегляне."
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Няма изтегляния в ход",
|
||||
"downloading_metadata": "Сваляне на {{title}} метадата…",
|
||||
"downloading": "Изтегляне на {{title}}… ({{percentage}} готово) - Остават {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Изтегляне на {{title}}… ({{percentage}} готово) - Изчисляване на оставащо време…",
|
||||
"checking_files": "Проверка на {{title}} файловете… ({{percentage}} готово)"
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "Следваща страница",
|
||||
"previous_page": "Предишна страница"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Варианти за изтегляне",
|
||||
"download_options_zero": "Няма варианти за изтегляне",
|
||||
"download_options_one": "{{count}} варианти за изтегляне",
|
||||
"download_options_other": "{{count}} варианти за изтегляне",
|
||||
"updated_at": "Обновено на {{updated_at}}",
|
||||
"install": "Инсталирай",
|
||||
"resume": "Продължи",
|
||||
"pause": "Пауза",
|
||||
"cancel": "Отказ",
|
||||
"remove": "Премахни",
|
||||
"space_left_on_disk": "{{space}} място на диска",
|
||||
"eta": "Заклчение {{eta}}",
|
||||
"calculating_eta": "Калкулиране на оставащо време…",
|
||||
"downloading_metadata": "Изтегляне на метадата…",
|
||||
"filter": "Филтрирай repacks",
|
||||
"requirements": "Системни изисквания",
|
||||
"minimum": "Минимални",
|
||||
"recommended": "Препоръчителни",
|
||||
"paused": "Паузирано",
|
||||
"release_date": "Издадено на {{date}}",
|
||||
"publisher": "Публикувано от {{publisher}}",
|
||||
"hours": "часове",
|
||||
"minutes": "минути",
|
||||
"amount_hours": "{{amount}} часа",
|
||||
"amount_minutes": "{{amount}} минути",
|
||||
"accuracy": "{{accuracy}}% точност",
|
||||
"add_to_library": "Добави в библиотеката",
|
||||
"remove_from_library": "Премахни от библиотеката",
|
||||
"no_downloads": "Няма налични изтегляния",
|
||||
"play_time": "Игрално време {{amount}}",
|
||||
"last_time_played": "Последно пускане {{period}}",
|
||||
"not_played_yet": "Не сте играли {{title}} все още",
|
||||
"next_suggestion": "Следващо предложение",
|
||||
"play": "Пускане",
|
||||
"deleting": "Изтриване на инсталация…",
|
||||
"close": "Затвори",
|
||||
"playing_now": "Играй сега",
|
||||
"change": "Промяна",
|
||||
"repacks_modal_description": "Избери repack който искаш да изтеглиш",
|
||||
"select_folder_hint": "За да промените стандартната папка отидете в <0>Настройки</0>",
|
||||
"download_now": "Изтегли сега",
|
||||
"no_shop_details": "Не може да се извлекат данни за магазина.",
|
||||
"download_options": "Опции за сваляне",
|
||||
"download_path": "Път за сваляне",
|
||||
"previous_screenshot": "Предишна снимка",
|
||||
"next_screenshot": "Следваща снимка",
|
||||
"screenshot": "Снимка {{number}}",
|
||||
"open_screenshot": "Отвори снимки {{number}}",
|
||||
"download_settings": "Настройки за сваляне",
|
||||
"downloader": "Downloader",
|
||||
"select_executable": "Избери",
|
||||
"no_executable_selected": "Няма избран стартиращ файл",
|
||||
"open_folder": "Отвори папка",
|
||||
"open_download_location": "Виж свалените файлове",
|
||||
"create_shortcut": "Пряк път на Десктопа",
|
||||
"remove_files": "Премахни файловете",
|
||||
"remove_from_library_title": "Сигурен ли си?",
|
||||
"remove_from_library_description": "Това ще премахне {{game}} от Библиотеката",
|
||||
"options": "Опции",
|
||||
"executable_section_title": "Стартиращ файл",
|
||||
"executable_section_description": "Пътят на файла, който ще се изпълни, когато се щракне върху \"Пускане\"",
|
||||
"downloads_secion_title": "Свалени",
|
||||
"downloads_section_description": "Вижте актуализации или други версии на тази игра",
|
||||
"danger_zone_section_title": "Опасна зона",
|
||||
"danger_zone_section_description": "Премахнете тази игра от библиотеката си или от файловете, изтеглени от Hydra",
|
||||
"download_in_progress": "Изтегляне в ход",
|
||||
"download_paused": "Изтеглянето е паузирано",
|
||||
"last_downloaded_option": "Опция от последно изтегляне",
|
||||
"create_shortcut_success": "Прекият път е създаден успешно",
|
||||
"create_shortcut_error": "Грешка при създаването на пряк път",
|
||||
"nsfw_content_title": "Тази игра съдържа неподходящо съдържание",
|
||||
"nsfw_content_description": "{{title}} съдържа съдържание, което може да не е подходящо за всички възрасти. Сигурни ли сте, че искате да продължите?",
|
||||
"allow_nsfw_content": "Продължи",
|
||||
"refuse_nsfw_content": "Назад",
|
||||
"stats": "Статистики",
|
||||
"download_count": "Сваляния",
|
||||
"player_count": "Активни играчи",
|
||||
"download_error": "Тази опция за изтегляне не е налична",
|
||||
"download": "Свали",
|
||||
"executable_path_in_use": "Изпълнимият файл вече се използва от \"{{game}}\"",
|
||||
"warning": "Внимание:",
|
||||
"hydra_needs_to_remain_open": "за това изтегляне, Hydra трябва да остане отворена, когато е завършено. Ако Hydra се затвори преди завършването, ще загубите напредъка си..",
|
||||
"achievements": "Постижения",
|
||||
"achievements_count": "Постижения {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Запазване в облака",
|
||||
"cloud_save_description": "Запазете напредъка си в облака и продължете да играете на всяко устройство",
|
||||
"backups": "Резервни копия",
|
||||
"install_backup": "Инсталирай",
|
||||
"delete_backup": "Изтрий",
|
||||
"create_backup": "Ново копие",
|
||||
"last_backup_date": "Последно копие от {{date}}",
|
||||
"no_backup_preview": "Не бяха намерени запазени игри за това заглавие",
|
||||
"restoring_backup": "Възстановяване на резервно копие ({{progress}} готово)…",
|
||||
"uploading_backup": "Качване на резервно копие…",
|
||||
"no_backups": "Все още не сте създали резервни копия за тази игра",
|
||||
"backup_uploaded": "Качено резервно копие",
|
||||
"backup_deleted": "Изтрито резервно копие",
|
||||
"backup_restored": "Възстановен бекъп",
|
||||
"see_all_achievements": "Вижте всички постижения",
|
||||
"sign_in_to_see_achievements": "Влезте, за да видите постиженията",
|
||||
"mapping_method_automatic": "Автоматично",
|
||||
"mapping_method_manual": "Ръчно",
|
||||
"mapping_method_label": "Метод на картографиране",
|
||||
"files_automatically_mapped": "Автоматично картографиране на файлове",
|
||||
"no_backups_created": "Не са създадени резервни копия за тази игра",
|
||||
"manage_files": "Управление на файлове",
|
||||
"loading_save_preview": "Търсене на запазени игри…",
|
||||
"wine_prefix": "Wine Префикс",
|
||||
"wine_prefix_description": "Wine prefix използван за тази игра",
|
||||
"no_download_option_info": "Няма налични данни",
|
||||
"backup_deletion_failed": "Неуспешно изтриване на резервно копие",
|
||||
"max_number_of_artifacts_reached": "Достигнат максимален брой резервни копия за тази игра",
|
||||
"achievements_not_sync": "Постиженията не са синхронизирани",
|
||||
"manage_files_description": "Управлявайте кои файлове ще бъдат архивирани и възстановени",
|
||||
"select_folder": "Избери папка",
|
||||
"backup_from": "Резервно копие от {{date}}",
|
||||
"custom_backup_location_set": "Задаване на персонализирано местоположение за архивиране"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Активирай Hydra",
|
||||
"installation_id": "Идентификатор на инсталацията:",
|
||||
"enter_activation_code": "Въведете кода за активиране",
|
||||
"message": "Ако не знаете къде да попитате за това, значи не трябва да го имате..",
|
||||
"activate": "Активирай",
|
||||
"loading": "Зареждане…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Продължи",
|
||||
"pause": "Пауза",
|
||||
"eta": "Conclusion {{eta}}",
|
||||
"paused": "Паузирано",
|
||||
"verifying": "Проверка…",
|
||||
"completed": "Готово",
|
||||
"removed": "Не е изтеглен",
|
||||
"cancel": "Отказ",
|
||||
"filter": "Филтриране на изтеглени игри",
|
||||
"remove": "Премахни",
|
||||
"downloading_metadata": "Изтегляне на метаданни…",
|
||||
"deleting": "Изтриване на инсталатора…",
|
||||
"delete": "Премахване на инсталатора",
|
||||
"delete_modal_title": "Сигурени ли сте?",
|
||||
"delete_modal_description": "Това ще премахне всички инсталационни файлове от компютъра ви.",
|
||||
"install": "Инсталирай",
|
||||
"download_in_progress": "В процес на изпълнение",
|
||||
"queued_downloads": "Изтеглени файлове в опашката",
|
||||
"downloads_completed": "Приключени",
|
||||
"queued": "В опашка",
|
||||
"no_downloads_title": "Толкова е празно",
|
||||
"no_downloads_description": "Все още не сте изтеглили нищо с Hydra, но никога не е късно да започнете...",
|
||||
"checking_files": "Проверка на файлове…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Инсталационен път",
|
||||
"change": "Актуализиране",
|
||||
"notifications": "Известия",
|
||||
"enable_download_notifications": "Когато изтеглянето е завършено",
|
||||
"enable_repack_list_notifications": "Когато се добави нов repack",
|
||||
"real_debrid_api_token_label": "Real-Debrid API токен",
|
||||
"quit_app_instead_hiding": "Не скривайте Hydra при затваряне",
|
||||
"launch_with_system": "Стартиране на Hydra при стартиране на системата",
|
||||
"general": "Общ",
|
||||
"behavior": "Поведение",
|
||||
"download_sources": "Източници за изтегляне",
|
||||
"language": "Език",
|
||||
"real_debrid_api_token": "API Токен",
|
||||
"enable_real_debrid": "Включи Real-Debrid",
|
||||
"real_debrid_description": "Real-Debrid е неограничен даунлоудър, който ви позволява бързо да изтегляте файлове, ограничени само от скоростта на интернет..",
|
||||
"real_debrid_invalid_token": "Невалиден API токен",
|
||||
"real_debrid_api_token_hint": "Вземете своя API токен <0>тук</0>",
|
||||
"real_debrid_free_account_error": "Акаунтът \"{{username}}\" е безплатен акаунт. Моля абонирай се за Real-Debrid",
|
||||
"real_debrid_linked_message": "Акаунтът \"{{username}}\" е свързан",
|
||||
"save_changes": "Запази промените",
|
||||
"changes_saved": "Промените са успешно запазни",
|
||||
"download_sources_description": "Hydra ще извлича връзките за изтегляне от тези източници. URL адресът на източника трябва да е директна връзка към .json файл, съдържащ връзките за изтегляне.",
|
||||
"validate_download_source": "Валидиране",
|
||||
"remove_download_source": "Премахни",
|
||||
"add_download_source": "Добави източник",
|
||||
"download_count_zero": "Няма опции за сваляне",
|
||||
"download_count_one": "{{countFormatted}} опции за сваляне",
|
||||
"download_count_other": "{{countFormatted}} опции за сваляне",
|
||||
"download_source_url": "URL адрес на източника за изтегляне",
|
||||
"add_download_source_description": "Вмъкнете URL адреса на файла .json",
|
||||
"download_source_up_to_date": "Актуален",
|
||||
"download_source_errored": "Сгрешен",
|
||||
"sync_download_sources": "Синхронизирай източниците",
|
||||
"removed_download_source": "Източника за сваляне е премахнат",
|
||||
"added_download_source": "Добавен източник за сваляне",
|
||||
"download_sources_synced": "Всички източници за сваляне са синхронизирани",
|
||||
"insert_valid_json_url": "Добавете ваиден JSON линк",
|
||||
"found_download_option_zero": "Няма намерени опции за сваляне",
|
||||
"found_download_option_one": "Намерени {{countFormatted}} опции за сваляне",
|
||||
"found_download_option_other": "Намерени {{countFormatted}} опции за сваляне",
|
||||
"import": "Внеси",
|
||||
"public": "Публичен",
|
||||
"private": "Личен",
|
||||
"friends_only": "Само за приятели",
|
||||
"privacy": "Поверителност",
|
||||
"profile_visibility": "Видимост на профила",
|
||||
"profile_visibility_description": "Изберете кой може да вижда вашия профил и библиотека",
|
||||
"required_field": "Това поле е задължително",
|
||||
"source_already_exists": "Този източник вече е добавен",
|
||||
"must_be_valid_url": "Източникът трябва да е валиден URL адрес.",
|
||||
"blocked_users": "Блокирани потребители",
|
||||
"user_unblocked": "Потребителят е бил деблокиран",
|
||||
"enable_achievement_notifications": "Когато е отключено постижение",
|
||||
"launch_minimized": "Стартиране на Hydra минимизирано",
|
||||
"disable_nsfw_alert": "Деактивиране на предупреждението NSFW"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Изтеглянето е завършено",
|
||||
"game_ready_to_install": "{{title}} е готово за инсталиране",
|
||||
"repack_list_updated": "Repack лист е обновен",
|
||||
"repack_count_one": "{{count}} repack е добавен",
|
||||
"repack_count_other": "{{count}} repacks добавени",
|
||||
"new_update_available": "Версия {{version}} е налична",
|
||||
"restart_to_install_update": "Рестартирайте Hydra, за да инсталирате актуализацията",
|
||||
"notification_achievement_unlocked_title": "Отключено постижение за {{game}}",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} и други {{count}} са отклщчени"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Отвори Hydra",
|
||||
"quit": "Изход"
|
||||
},
|
||||
"game_card": {
|
||||
"no_downloads": "Няма налични изтегляния"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Не инсталирани програми",
|
||||
"description": "Wine или Lutris изпълними файлове не бяха открити на вашата система",
|
||||
"instructions": "Проверете правилния начин за инсталиране на някоя от тях на вашата дистрибуция на Linux, за да може играта да работи нормално"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Бутон за затваряне"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Превключване на видимостта на паролата"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} часове",
|
||||
"amount_minutes": "{{amount}} минути",
|
||||
"last_time_played": "Последно играно {{period}}",
|
||||
"activity": "Скорошна активност",
|
||||
"library": "Библиотека",
|
||||
"total_play_time": "Общо време за игра: {{amount}}",
|
||||
"no_recent_activity_title": "Хмм… няма нищо тук",
|
||||
"no_recent_activity_description": "Не сте играли игри напоследък. Време е да промените това.!",
|
||||
"display_name": "Показване на името",
|
||||
"saving": "Запазване",
|
||||
"save": "Запис",
|
||||
"edit_profile": "Редактиране на профила",
|
||||
"saved_successfully": "Запазено успешно",
|
||||
"try_again": "Моля, опитайте пак",
|
||||
"sign_out_modal_title": "Сигурни ли сте?",
|
||||
"cancel": "Отказ",
|
||||
"successfully_signed_out": "Успешно се отписахте",
|
||||
"sign_out": "Отписване",
|
||||
"playing_for": "В игра от {{amount}}",
|
||||
"sign_out_modal_text": "Вашата библиотека е свързана с текущата ви сметка. Когато се отпишете, библиотеката ви вече няма да е видима и напредъкът няма да бъде запазен. Продължете с отписването?",
|
||||
"add_friends": "Добави приятели",
|
||||
"add": "Добави",
|
||||
"friend_code": "Приятелски код",
|
||||
"see_profile": "Виж профила",
|
||||
"sending": "Изпращане",
|
||||
"friend_request_sent": "Изпратена покана за приятелство",
|
||||
"friends": "Приятели",
|
||||
"friends_list": "Списък с приятели",
|
||||
"user_not_found": "Не е намерен потребител",
|
||||
"block_user": "Блокирай потребител",
|
||||
"add_friend": "Добави приятел",
|
||||
"request_sent": "Изпратена покана",
|
||||
"request_received": "Получена покана",
|
||||
"accept_request": "Приеми поканата",
|
||||
"ignore_request": "Игнирирай поканата",
|
||||
"cancel_request": "Откажи поканата",
|
||||
"undo_friendship": "Отмяна на приятелството",
|
||||
"request_accepted": "Поканата е приета",
|
||||
"user_blocked_successfully": "Потребителят е блокиран успешно",
|
||||
"user_block_modal_text": "Това ще блокира {{displayName}}",
|
||||
"blocked_users": "Блокирани потребители",
|
||||
"unblock": "Отблокирай",
|
||||
"no_friends_added": "Не сте добавили приятели",
|
||||
"pending": "Чакащи",
|
||||
"no_pending_invites": "Нямате чакащи покани",
|
||||
"no_blocked_users": "Нямате блокирани потребители",
|
||||
"friend_code_copied": "Приятелския код е копиран",
|
||||
"undo_friendship_modal_text": "Това ще отмени приятелството ви с {{displayName}}",
|
||||
"privacy_hint": "За да настроите кой може да вижда това, отидете в <0>Настройки</0>",
|
||||
"locked_profile": "Този профил е личен",
|
||||
"image_process_failure": "Грешка при обработката на изображението",
|
||||
"required_field": "Това поле е задължително",
|
||||
"displayname_min_length": "Името трябва да е дълго поне 3 символа",
|
||||
"displayname_max_length": "Името трябва да е с дължина не повече от 50 символа.",
|
||||
"report_profile": "Докладвай този профил",
|
||||
"report_reason": "Защо докладвате този профил?",
|
||||
"report_description": "Допълнителна информация",
|
||||
"report_description_placeholder": "Допълнителна информация",
|
||||
"report": "Докладвай",
|
||||
"report_reason_hate": "Омразна реч",
|
||||
"report_reason_sexual_content": "Сексуално съдържание",
|
||||
"report_reason_violence": "Насилия",
|
||||
"report_reason_spam": "Спам",
|
||||
"report_reason_other": "Друго",
|
||||
"profile_reported": "Профилът е докладван",
|
||||
"your_friend_code": "Вашия приятелски код:",
|
||||
"upload_banner": "Качи банер",
|
||||
"uploading_banner": "Качване на банер…",
|
||||
"background_image_updated": "Обновено фоново изображение"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Постижението е отключено",
|
||||
"user_achievements": "Постиженията на {{displayName}} ",
|
||||
"your_achievements": "Вашите Постижения",
|
||||
"unlocked_at": "Отключено на:",
|
||||
"subscription_needed": "Необходим е абонамент за Hydra Cloud, за да видите това съдържание",
|
||||
"new_achievements_unlocked": "Отключени {{achievementCount}} нови постижения от {{gameCount}} игра",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} постижения",
|
||||
"achievements_unlocked_for_game": "Отключени {{achievementCount}} нови постижения за {{gameTitle}}"
|
||||
},
|
||||
"tour": {
|
||||
"subscription_tour_title": "Hydra Cloud Абонамент",
|
||||
"subscribe_now": "Абонирай се сега",
|
||||
"cloud_saving": "Запазване в облака",
|
||||
"cloud_achievements": "Запазете постиженията си в облака",
|
||||
"animated_profile_picture": "Анимирана профилна снимка",
|
||||
"premium_support": "Премиум поддръжка",
|
||||
"show_and_compare_achievements": "Показвайте и сравнявайте постиженията си с тези на други потребители",
|
||||
"animated_profile_banner": "Анимиран профилен банер"
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,11 @@
|
||||
"home": {
|
||||
"featured": "Doporučené",
|
||||
"surprise_me": "Překvap mě",
|
||||
"no_results": "Výsledek nenalezen"
|
||||
"no_results": "Výsledek nenalezen",
|
||||
"start_typing": "Začni psát pro vyhledávání...",
|
||||
"hot": "Teď populární",
|
||||
"weekly": "📅 Nejlepší hry týdne",
|
||||
"achievements": "🏆 Hry k překonání"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Katalog",
|
||||
@@ -20,7 +24,9 @@
|
||||
"home": "Domov",
|
||||
"queued": "{{title}} (V řadě)",
|
||||
"game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor",
|
||||
"sign_in": "Přihlásit se"
|
||||
"sign_in": "Přihlásit se",
|
||||
"friends": "Přátelé",
|
||||
"need_help": "Potřebujete pomoc?"
|
||||
},
|
||||
"header": {
|
||||
"search": "Vyhledat hry",
|
||||
@@ -113,7 +119,54 @@
|
||||
"download_paused": "Stahování pozastaveno",
|
||||
"last_downloaded_option": "Poslední stažená možnost",
|
||||
"create_shortcut_success": "Zástupce vytvořen úspěšně",
|
||||
"create_shortcut_error": "Chyba při pokusu vytvořit zástupce"
|
||||
"create_shortcut_error": "Chyba při pokusu vytvořit zástupce",
|
||||
"nsfw_content_title": "Tahle hra obsahuje nevhodný obsah",
|
||||
"nsfw_content_description": "{{title}} obsahuje obsah, který by nemusel být vhodný pro všechny věkové skupiny. Jste si jisti, že chcete pokračovat?",
|
||||
"allow_nsfw_content": "Pokračovat",
|
||||
"refuse_nsfw_content": "Jít zpět",
|
||||
"stats": "Statistiky",
|
||||
"download_count": "Stažení",
|
||||
"player_count": "Aktivní hráči",
|
||||
"download_error": "Tahle možnost stažení není dostupná",
|
||||
"download": "Stáhnout",
|
||||
"executable_path_in_use": "Spustitelný soubor již používá \"{{game}}\"",
|
||||
"warning": "Varování",
|
||||
"hydra_needs_to_remain_open": "Pro tohle stažení, musí Hydra zůstat otevřená až do konce stahování. Pokud Hydru zavřete dříve, postup stahování bude ztracen.",
|
||||
"achievements": "Achievementy",
|
||||
"achievements_count": "Achievementy {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Uložení v cloudu",
|
||||
"cloud_save_description": "Uložte si svůj postup v cloud a pokračujte v hraní na jakémkoliv zářízení",
|
||||
"backups": "Zálohy",
|
||||
"install_backup": "Nainstalovat",
|
||||
"delete_backup": "Smazat",
|
||||
"create_backup": "Vytvořit zálohu",
|
||||
"last_backup_date": "Poslední záloha vytvořena {{date}}",
|
||||
"no_backup_preview": "Žádné zálohy nebyly nalezeny pro tuhle hru",
|
||||
"restoring_backup": "Obnovuji zálohu ({{progress}} hotovo)...",
|
||||
"uploading_backup": "Nahrávání zálohy...",
|
||||
"no_backups": "Nemáte zatím vytvořeny žádné zálohy pro tuto hru",
|
||||
"backup_uploaded": "Záloha nahrána",
|
||||
"backup_deleted": "Záloha odstraněna",
|
||||
"backup_restored": "Záloha obnovena",
|
||||
"see_all_achievements": "Zobrazit všechny achievementy",
|
||||
"sign_in_to_see_achievements": "Musíte se přihlásit pro zobrazení achievementů",
|
||||
"mapping_method_automatic": "Automaticky",
|
||||
"mapping_method_manual": "Manuálně",
|
||||
"mapping_method_label": "Metoda mapování",
|
||||
"files_automatically_mapped": "Soubory automaticky zmapovány",
|
||||
"no_backups_created": "Žádné zálohy nebyly vytvořeny pro tuto hru",
|
||||
"manage_files": "Spravovat soubory",
|
||||
"loading_save_preview": "Hledání uložených her...",
|
||||
"wine_prefix": "Wine Prefix",
|
||||
"wine_prefix_description": "Wine Prefix použit pro spuštění této hry",
|
||||
"no_download_option_info": "Žádné informace nejsou dostupny",
|
||||
"backup_deletion_failed": "Nepovedlo se odstranit zálohu",
|
||||
"max_number_of_artifacts_reached": "Dosáhli jste maximálního počtu záloh pro tuto hru",
|
||||
"achievements_not_sync": "Vaše achievementy nejsou synchronizovány",
|
||||
"manage_files_description": "Spravovat, které soubory budou zálohovány a obnoveny",
|
||||
"select_folder": "Vybrat složku",
|
||||
"backup_from": "Zálohy z {{date}}",
|
||||
"custom_backup_location_set": "Vlastní umístění záloh nastaveno"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Aktivovat hydru",
|
||||
@@ -189,7 +242,21 @@
|
||||
"found_download_option_zero": "Nenalezena žádná možnost stahování",
|
||||
"found_download_option_one": "Nalezena {{countFormatted}} možnost stahování",
|
||||
"found_download_option_other": "Nalezeny {{countFormatted}} možnosti stahování",
|
||||
"import": "Importovat"
|
||||
"import": "Importovat",
|
||||
"public": "Veřejné",
|
||||
"private": "Soukromé",
|
||||
"friends_only": "Pouze přátelé",
|
||||
"privacy": "Soukromí",
|
||||
"profile_visibility": "Viditelnost profilu",
|
||||
"profile_visibility_description": "Vyberte si, kdo může vidět váš profil a knihovnu",
|
||||
"required_field": "Toto pole je povinné",
|
||||
"source_already_exists": "Tento zdroj byl již přidán",
|
||||
"must_be_valid_url": "Zdroj musí být platký odkaz URL",
|
||||
"blocked_users": "Zablokovaní uživatelé",
|
||||
"user_unblocked": "Uživatel byl odblokován",
|
||||
"enable_achievement_notifications": "Když je odemknut achievement",
|
||||
"launch_minimized": "Spustit v minimalizovaném režimu",
|
||||
"disable_nsfw_alert": "Deaktivovat upozornění na nevhodný obsah"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Stahování dokončeno",
|
||||
@@ -198,7 +265,9 @@
|
||||
"repack_count_one": "{{count}} repack přidán",
|
||||
"repack_count_other": "{{count}} repacky přidány",
|
||||
"new_update_available": "Version {{version}} je dostupná",
|
||||
"restart_to_install_update": "Restartuj Hydru pro aktualizaci"
|
||||
"restart_to_install_update": "Restartuj Hydru pro aktualizaci",
|
||||
"notification_achievement_unlocked_title": "Achievement pro {{game}} byl odemknut",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} a dalších {{count}} byly odemknuty"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Otevřít Hydru",
|
||||
@@ -266,6 +335,47 @@
|
||||
"no_pending_invites": "Nemáte žádné příchozí žádosti",
|
||||
"no_blocked_users": "Nemáte nikoho zablokovaného",
|
||||
"friend_code_copied": "Kód přítele zkopírován",
|
||||
"undo_friendship_modal_text": "Tímto zrušíte své přátelství s {{displayName}}"
|
||||
"undo_friendship_modal_text": "Tímto zrušíte své přátelství s {{displayName}}",
|
||||
"privacy_hint": "Pro změnu toho, kdo tohle může vidět, jděte do <0>Nastavení</0>",
|
||||
"locked_profile": "Tento profil je soukromý",
|
||||
"image_process_failure": "Nastala chyba při zpracování obrázku",
|
||||
"required_field": "Toto pole je povinné",
|
||||
"displayname_min_length": "Uživatelské jméno musí být minimálně 3 znaky dlouhé",
|
||||
"displayname_max_length": "Uživatelské jméno musí být maximálně 50 znaků dlouhé",
|
||||
"report_profile": "Nahlásit profil",
|
||||
"report_reason": "Proč nahlašujete tento profil?",
|
||||
"report_description": "Přídavné informace",
|
||||
"report_description_placeholder": "Přídavné informace",
|
||||
"report": "Nahlásit",
|
||||
"report_reason_hate": "Nenávistné projevy",
|
||||
"report_reason_sexual_content": "Sexuální obsah",
|
||||
"report_reason_violence": "Násilí",
|
||||
"report_reason_spam": "Spam",
|
||||
"report_reason_other": "Ostatní",
|
||||
"profile_reported": "Profil nahlášen",
|
||||
"your_friend_code": "Tvůj kód přítele:",
|
||||
"upload_banner": "Nahrát banner profilu",
|
||||
"uploading_banner": "Nahrávání banneru",
|
||||
"background_image_updated": "Obrázek pozadí byl změněn"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Achievement odemčen",
|
||||
"user_achievements": "Achievementy uživatele {{displayName}}",
|
||||
"your_achievements": "Vaše achievementy",
|
||||
"unlocked_at": "Odemčeno:",
|
||||
"subscription_needed": "Je vyžadováno předplatné Hydra Cloud pro zobrazení tohoto obsahu",
|
||||
"new_achievements_unlocked": "Odemčeno {{achievementCount}} nových achievementů z {{gameCount}} her",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementů",
|
||||
"achievements_unlocked_for_game": "Odemčeno {{achievementCount}} nových achievementů pro {{gameTitle}}"
|
||||
},
|
||||
"tour": {
|
||||
"subscription_tour_title": "Předplatné Hydra Cloud",
|
||||
"subscribe_now": "Připojit se",
|
||||
"cloud_saving": "Ukládání v cloudu",
|
||||
"cloud_achievements": "Ukládejte vaše achievementy do cloudu",
|
||||
"animated_profile_picture": "Animované profilové obrázky",
|
||||
"premium_support": "Prémiová podpora",
|
||||
"show_and_compare_achievements": "Zobraz a porovnej achievementy s ostatními uživateli",
|
||||
"animated_profile_banner": "Animovaný banner na profilu"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"queued": "{{title}} (Queued)",
|
||||
"game_has_no_executable": "Game has no executable selected",
|
||||
"sign_in": "Sign in",
|
||||
"friends": "Friends"
|
||||
"friends": "Friends",
|
||||
"need_help": "Need help?"
|
||||
},
|
||||
"header": {
|
||||
"search": "Search games",
|
||||
@@ -104,6 +105,7 @@
|
||||
"open_folder": "Open folder",
|
||||
"open_download_location": "See downloaded files",
|
||||
"create_shortcut": "Create desktop shortcut",
|
||||
"clear": "Clear",
|
||||
"remove_files": "Remove files",
|
||||
"remove_from_library_title": "Are you sure?",
|
||||
"remove_from_library_description": "This will remove {{game}} from your library",
|
||||
@@ -130,7 +132,7 @@
|
||||
"download": "Download",
|
||||
"executable_path_in_use": "Executable already in use by \"{{game}}\"",
|
||||
"warning": "Warning:",
|
||||
"hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util its conclusion. In case Hydra closes before the conclusion, you will lose your progress.",
|
||||
"hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.",
|
||||
"achievements": "Achievements",
|
||||
"achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Cloud save",
|
||||
@@ -165,7 +167,8 @@
|
||||
"manage_files_description": "Manage which files will be backed up and restored",
|
||||
"select_folder": "Select folder",
|
||||
"backup_from": "Backup from {{date}}",
|
||||
"custom_backup_location_set": "Custom backup location set"
|
||||
"custom_backup_location_set": "Custom backup location set",
|
||||
"no_directory_selected": "No directory selected"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activate Hydra",
|
||||
@@ -215,7 +218,7 @@
|
||||
"language": "Language",
|
||||
"real_debrid_api_token": "API Token",
|
||||
"enable_real_debrid": "Enable Real-Debrid",
|
||||
"real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to download files instantly and at the best of your Internet speed.",
|
||||
"real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.",
|
||||
"real_debrid_invalid_token": "Invalid API token",
|
||||
"real_debrid_api_token_hint": "You can get your API token <0>here</0>",
|
||||
"real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid",
|
||||
@@ -230,7 +233,7 @@
|
||||
"download_count_one": "{{countFormatted}} download option",
|
||||
"download_count_other": "{{countFormatted}} download options",
|
||||
"download_source_url": "Download source URL",
|
||||
"add_download_source_description": "Insert the URL containing the .json file",
|
||||
"add_download_source_description": "Insert the URL of the .json file",
|
||||
"download_source_up_to_date": "Up-to-date",
|
||||
"download_source_errored": "Errored",
|
||||
"sync_download_sources": "Sync sources",
|
||||
@@ -249,11 +252,13 @@
|
||||
"profile_visibility": "Profile visibility",
|
||||
"profile_visibility_description": "Choose who can see your profile and library",
|
||||
"required_field": "This field is required",
|
||||
"source_already_exists": "This source has been already added",
|
||||
"source_already_exists": "This source has already been added",
|
||||
"must_be_valid_url": "The source must be a valid URL",
|
||||
"blocked_users": "Blocked users",
|
||||
"user_unblocked": "User has been unblocked",
|
||||
"enable_achievement_notifications": "When an achievement in unlocked"
|
||||
"enable_achievement_notifications": "When an achievement is unlocked",
|
||||
"launch_minimized": "Launch Hydra minimized",
|
||||
"disable_nsfw_alert": "Disable NSFW alert"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download complete",
|
||||
@@ -288,7 +293,7 @@
|
||||
"amount_hours": "{{amount}} hours",
|
||||
"amount_minutes": "{{amount}} minutes",
|
||||
"last_time_played": "Last played {{period}}",
|
||||
"activity": "Recent activity",
|
||||
"activity": "Recent Activity",
|
||||
"library": "Library",
|
||||
"total_play_time": "Total playtime: {{amount}}",
|
||||
"no_recent_activity_title": "Hmmm… nothing here",
|
||||
@@ -327,7 +332,7 @@
|
||||
"user_block_modal_text": "This will block {{displayName}}",
|
||||
"blocked_users": "Blocked users",
|
||||
"unblock": "Unblock",
|
||||
"no_friends_added": "You still don't have added friends",
|
||||
"no_friends_added": "You have no added friends",
|
||||
"pending": "Pending",
|
||||
"no_pending_invites": "You have no pending invites",
|
||||
"no_blocked_users": "You have no blocked users",
|
||||
@@ -361,7 +366,9 @@
|
||||
"your_achievements": "Your Achievements",
|
||||
"unlocked_at": "Unlocked at:",
|
||||
"subscription_needed": "A Hydra Cloud subscription is required to see this content",
|
||||
"new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games"
|
||||
"new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements",
|
||||
"achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}"
|
||||
},
|
||||
"tour": {
|
||||
"subscription_tour_title": "Hydra Cloud Subscription",
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"no_results": "Sin resultados encontrados",
|
||||
"start_typing": "Empieza a escribir para buscar...",
|
||||
"hot": "Popular Ahora",
|
||||
"weekly": "📅 Mejores juegos de la semana"
|
||||
"weekly": "📅 Mejores juegos de la semana",
|
||||
"achievements": "🏆 Juegos para completar"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Catálogo",
|
||||
@@ -24,7 +25,8 @@
|
||||
"queued": "{{title}} (En cola)",
|
||||
"game_has_no_executable": "El juego no tiene un ejecutable seleccionado",
|
||||
"sign_in": "Iniciar sesión",
|
||||
"friends": "Amigos"
|
||||
"friends": "Amigos",
|
||||
"need_help": "¿Necesitas ayuda?"
|
||||
},
|
||||
"header": {
|
||||
"search": "Buscar juegos",
|
||||
@@ -98,7 +100,7 @@
|
||||
"open_screenshot": "Abrir captura {{number}}",
|
||||
"download_settings": "Ajustes de descarga",
|
||||
"downloader": "Método de descarga",
|
||||
"select_executable": "Seleccionar ejecutable",
|
||||
"select_executable": "Seleccionar",
|
||||
"no_executable_selected": "No se seleccionó un ejecutable",
|
||||
"open_folder": "Abrir carpeta",
|
||||
"open_download_location": "Ver archivos descargados",
|
||||
@@ -160,7 +162,13 @@
|
||||
"no_download_option_info": "Sin información disponible",
|
||||
"backup_deletion_failed": "La eliminación de la copia de seguridad falló",
|
||||
"max_number_of_artifacts_reached": "Número máximo de copias de seguridad de este juego alcanzadas",
|
||||
"achievements_not_sync": "Tus logros no están sincronizadas"
|
||||
"achievements_not_sync": "Tus logros no están sincronizados",
|
||||
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
|
||||
"select_folder": "Seleccionar carpeta",
|
||||
"backup_from": "Copia de seguridad de {{date}}",
|
||||
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad",
|
||||
"clear": "Limpiar",
|
||||
"no_directory_selected": "No se seleccionó un directório"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activar Hydra",
|
||||
@@ -248,7 +256,9 @@
|
||||
"must_be_valid_url": "La fuente debe ser una URL válida.",
|
||||
"blocked_users": "Usuarios bloqueados",
|
||||
"user_unblocked": "El usuario ha sido desbloqueado",
|
||||
"enable_achievement_notifications": "Cuando un logro se desbloquea"
|
||||
"enable_achievement_notifications": "Cuando un logro se desbloquea",
|
||||
"launch_minimized": "Iniciar Hydra minimizado",
|
||||
"disable_nsfw_alert": "Desactivar alerta NSFW"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Descarga completada",
|
||||
@@ -347,15 +357,18 @@
|
||||
"profile_reported": "Perfil reportado",
|
||||
"your_friend_code": "Tu código de amigo:",
|
||||
"upload_banner": "Subir un banner",
|
||||
"uploading_banner": "Subiendo banner…"
|
||||
"uploading_banner": "Subiendo banner…",
|
||||
"background_image_updated": "Imagen de fondo actualizada"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Logro desbloqueado",
|
||||
"user_achievements": "Logros de {{displayName}}",
|
||||
"your_achievements": "Tus Logros",
|
||||
"unlocked_at": "Desbloqueado el:",
|
||||
"subscription_needed": "Se necesita una suscripción a Hydra Cloud se necesita para ver este contenido",
|
||||
"new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos"
|
||||
"subscription_needed": "Se necesita una suscripción a Hydra Cloud necesita para ver este contenido",
|
||||
"new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} logros",
|
||||
"achievements_unlocked_for_game": "Se han desbloqueado {{achievementCount}} nuevos logros de {{gameTitle}}"
|
||||
},
|
||||
"tour": {
|
||||
"subscription_tour_title": "Suscripción Hydra Cloud",
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"no_results": "Tulemusi ei leitud",
|
||||
"start_typing": "Alusta otsimiseks kirjutamist...",
|
||||
"hot": "Praegu kuum",
|
||||
"weekly": "📅 Nädala top mängud"
|
||||
"weekly": "📅 Nädala top mängud",
|
||||
"achievements": "🏆 Mängud, mida läbida"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Kataloog",
|
||||
@@ -160,95 +161,11 @@
|
||||
"no_download_option_info": "Info pole saadaval",
|
||||
"backup_deletion_failed": "Varunduse kustutamine ebaõnnestus",
|
||||
"max_number_of_artifacts_reached": "Selle mängu varunduste maksimaalne arv on saavutatud",
|
||||
"achievements_not_sync": "Sinu saavutused pole sünkroniseeritud"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Aktiveeri Hydra",
|
||||
"installation_id": "Installatsiooni ID:",
|
||||
"enter_activation_code": "Sisesta oma aktiveerimiskood",
|
||||
"message": "Kui sa ei tea, kust seda küsida, siis sa ei peaks seda omama.",
|
||||
"activate": "Aktiveeri",
|
||||
"loading": "Laadimine…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Jätka",
|
||||
"pause": "Peata",
|
||||
"eta": "Lõpp {{eta}}",
|
||||
"paused": "Peatatud",
|
||||
"verifying": "Kontrollimine…",
|
||||
"completed": "Lõpetatud",
|
||||
"removed": "Pole alla laaditud",
|
||||
"cancel": "Tühista",
|
||||
"filter": "Filtreeri allalaaditud mänge",
|
||||
"remove": "Eemalda",
|
||||
"downloading_metadata": "Metaandmete allalaadimine…",
|
||||
"deleting": "Installeri kustutamine…",
|
||||
"delete": "Eemalda installer",
|
||||
"delete_modal_title": "Oled sa kindel?",
|
||||
"delete_modal_description": "See eemaldab kõik installifailid sinu arvutist",
|
||||
"install": "Installi",
|
||||
"download_in_progress": "Töös",
|
||||
"queued_downloads": "Järjekorras allalaadimised",
|
||||
"downloads_completed": "Lõpetatud",
|
||||
"queued": "Järjekorras",
|
||||
"no_downloads_title": "Nii tühi",
|
||||
"no_downloads_description": "Sa pole veel Hydraga midagi alla laadinud, aga pole kunagi hilja alustada.",
|
||||
"checking_files": "Failide kontrollimine…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Allalaadimiste tee",
|
||||
"change": "Uuenda",
|
||||
"notifications": "Teavitused",
|
||||
"enable_download_notifications": "Kui allalaadimine on lõpetatud",
|
||||
"enable_repack_list_notifications": "Kui uus repack on lisatud",
|
||||
"real_debrid_api_token_label": "Real-Debrid API võti",
|
||||
"quit_app_instead_hiding": "Ära peida Hydrat sulgemisel",
|
||||
"launch_with_system": "Käivita Hydra süsteemi käivitamisel",
|
||||
"general": "Üldine",
|
||||
"behavior": "Käitumine",
|
||||
"download_sources": "Allalaadimise allikad",
|
||||
"language": "Keel",
|
||||
"real_debrid_api_token": "API Võti",
|
||||
"enable_real_debrid": "Luba Real-Debrid",
|
||||
"real_debrid_description": "Real-Debrid on piiranguteta allalaadija, mis võimaldab sul faile alla laadida koheselt ja sinu internetiühenduse parima kiirusega.",
|
||||
"real_debrid_invalid_token": "Vigane API võti",
|
||||
"real_debrid_api_token_hint": "Sa saad oma API võtme <0>siit</0>",
|
||||
"real_debrid_free_account_error": "Konto \"{{username}}\" on tasuta konto. Palun telli Real-Debrid",
|
||||
"real_debrid_linked_message": "Konto \"{{username}}\" ühendatud",
|
||||
"save_changes": "Salvesta muudatused",
|
||||
"changes_saved": "Muudatused edukalt salvestatud",
|
||||
"download_sources_description": "Hydra laeb allalaadimise lingid nendest allikatest. Allika URL peab olema otsene link .json failile, mis sisaldab allalaadimise linke.",
|
||||
"validate_download_source": "Valideeri",
|
||||
"remove_download_source": "Eemalda",
|
||||
"add_download_source": "Lisa allikas",
|
||||
"download_count_zero": "Allalaadimise valikuid pole",
|
||||
"download_count_one": "{{countFormatted}} allalaadimise valik",
|
||||
"download_count_other": "{{countFormatted}} allalaadimise valikut",
|
||||
"download_source_url": "Allalaadimise allika URL",
|
||||
"add_download_source_description": "Sisesta URL, mis sisaldab .json faili",
|
||||
"download_source_up_to_date": "Ajakohane",
|
||||
"download_source_errored": "Vigane",
|
||||
"sync_download_sources": "Sünkroniseeri allikad",
|
||||
"removed_download_source": "Allalaadimise allikas eemaldatud",
|
||||
"added_download_source": "Allalaadimise allikas lisatud",
|
||||
"download_sources_synced": "Kõik allalaadimise allikad on sünkroniseeritud",
|
||||
"insert_valid_json_url": "Sisesta kehtiv JSON url",
|
||||
"found_download_option_zero": "Allalaadimise valikuid ei leitud",
|
||||
"found_download_option_one": "Leitud {{countFormatted}} allalaadimise valik",
|
||||
"found_download_option_other": "Leitud {{countFormatted}} allalaadimise valikut",
|
||||
"import": "Impordi",
|
||||
"public": "Avalik",
|
||||
"private": "Privaatne",
|
||||
"friends_only": "Ainult sõpradele",
|
||||
"privacy": "Privaatsus",
|
||||
"profile_visibility": "Profiili nähtavus",
|
||||
"profile_visibility_description": "Vali, kes saavad näha sinu profiili ja kogu",
|
||||
"required_field": "See väli on kohustuslik",
|
||||
"source_already_exists": "See allikas on juba lisatud",
|
||||
"must_be_valid_url": "Allikas peab olema kehtiv URL",
|
||||
"blocked_users": "Blokeeritud kasutajad",
|
||||
"user_unblocked": "Kasutaja blokeering on eemaldatud",
|
||||
"enable_achievement_notifications": "Kui saavutus avatakse"
|
||||
"achievements_not_sync": "Sinu saavutused pole sünkroniseeritud",
|
||||
"manage_files_description": "Hallake, millised failid varundatakse ja taastatakse",
|
||||
"select_folder": "Vali kaust",
|
||||
"backup_from": "Varundamine kuupäevast {{date}}",
|
||||
"custom_backup_location_set": "Kohandatud varundamise asukoht määratud"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Aktiveeri Hydra",
|
||||
@@ -395,7 +312,7 @@
|
||||
"sending": "Saatmine",
|
||||
"friend_request_sent": "Sõbrakutse saadetud",
|
||||
"friends": "Sõbrad",
|
||||
"friends_list": "Sõbrade nimekiri",
|
||||
"friends_list": "Sõprade nimekiri",
|
||||
"user_not_found": "Kasutajat ei leitud",
|
||||
"block_user": "Blokeeri kasutaja",
|
||||
"add_friend": "Lisa sõbraks",
|
||||
@@ -435,7 +352,8 @@
|
||||
"profile_reported": "Profiilist teatatud",
|
||||
"your_friend_code": "Sinu sõbrakood:",
|
||||
"upload_banner": "Lae üles bänner",
|
||||
"uploading_banner": "Bänneri üleslaadimine…"
|
||||
"uploading_banner": "Bänneri üleslaadimine…",
|
||||
"background_image_updated": "Bänner uuendatud"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Saavutus avatud",
|
||||
|
||||
@@ -57,14 +57,14 @@
|
||||
"remove_from_library": "Supprimer de la bibliothèque",
|
||||
"no_downloads": "Aucun téléchargement disponible",
|
||||
"next_suggestion": "Suggestion suivante",
|
||||
"play_time": "Joué pour {{montant}}",
|
||||
"play_time": "Joué pour {{amount}}",
|
||||
"install": "Installer",
|
||||
"play": "Jouer",
|
||||
"not_played_yet": "Vous n'avez pas encore joué à {{title}}",
|
||||
"close": "Fermer",
|
||||
"deleting": "Suppression du programme d'installation…",
|
||||
"playing_now": "Jeu en cours",
|
||||
"last_time_played": "Dernièrement joué {{période}}"
|
||||
"last_time_played": "Dernièrement joué {{period}}"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activer Hydra",
|
||||
|
||||
@@ -24,6 +24,7 @@ import kk from "./kk/translation.json";
|
||||
import cs from "./cs/translation.json";
|
||||
import nb from "./nb/translation.json";
|
||||
import et from "./et/translation.json";
|
||||
import bg from "./bg/translation.json";
|
||||
|
||||
export default {
|
||||
"pt-BR": ptBR,
|
||||
@@ -48,6 +49,7 @@ export default {
|
||||
fa,
|
||||
ro,
|
||||
ca,
|
||||
bg,
|
||||
kk,
|
||||
cs,
|
||||
nb,
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"queued": "{{title}} (Na fila)",
|
||||
"game_has_no_executable": "Jogo não possui executável selecionado",
|
||||
"sign_in": "Login",
|
||||
"friends": "Amigos"
|
||||
"friends": "Amigos",
|
||||
"need_help": "Precisa de ajuda?"
|
||||
},
|
||||
"header": {
|
||||
"search": "Buscar jogos",
|
||||
@@ -130,7 +131,7 @@
|
||||
"achievements": "Conquistas",
|
||||
"achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})",
|
||||
"cloud_save": "Salvamento em nuvem",
|
||||
"cloud_save_description": "Matenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo",
|
||||
"cloud_save_description": "Mantenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo",
|
||||
"backups": "Backups",
|
||||
"install_backup": "Restaurar",
|
||||
"delete_backup": "Apagar",
|
||||
@@ -161,7 +162,9 @@
|
||||
"backup_from": "Backup de {{date}}",
|
||||
"custom_backup_location_set": "Localização customizada selecionada",
|
||||
"select_folder": "Selecione a pasta",
|
||||
"manage_files_description": "Gerencie quais arquivos serão feitos backup"
|
||||
"manage_files_description": "Gerencie quais arquivos serão feitos backup",
|
||||
"clear": "Limpar",
|
||||
"no_directory_selected": "Nenhum diretório selecionado"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Ativação",
|
||||
@@ -190,7 +193,7 @@
|
||||
"install": "Instalar",
|
||||
"download_in_progress": "Baixando agora",
|
||||
"queued_downloads": "Na fila",
|
||||
"downloads_completed": "Completo",
|
||||
"downloads_completed": "Concluído",
|
||||
"queued": "Na fila",
|
||||
"no_downloads_title": "Nada por aqui…",
|
||||
"no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
|
||||
@@ -249,7 +252,9 @@
|
||||
"must_be_valid_url": "A fonte deve ser uma URL válida",
|
||||
"blocked_users": "Usuários bloqueados",
|
||||
"user_unblocked": "Usuário desbloqueado",
|
||||
"enable_achievement_notifications": "Quando uma conquista é desbloqueada"
|
||||
"enable_achievement_notifications": "Quando uma conquista é desbloqueada",
|
||||
"launch_minimized": "Iniciar o Hydra minimizado",
|
||||
"disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download concluído",
|
||||
@@ -359,7 +364,9 @@
|
||||
"user_achievements": "Conquistas de {{displayName}}",
|
||||
"unlocked_at": "Desbloqueado em:",
|
||||
"subscription_needed": "Você precisa de uma assinatura Hydra Cloud para visualizar este conteúdo",
|
||||
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos"
|
||||
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas",
|
||||
"achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}"
|
||||
},
|
||||
"tour": {
|
||||
"subscription_tour_title": "Assinatura Hydra Cloud",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"featured": "Рекомендованное",
|
||||
"surprise_me": "Удиви меня",
|
||||
"no_results": "Ничего не найдено",
|
||||
"hot": "Сейчас жарко",
|
||||
"hot": "Сейчас в топе",
|
||||
"start_typing": "Начинаю вводить текст для поиска...",
|
||||
"weekly": "📅 Лучшие игры недели"
|
||||
},
|
||||
@@ -24,7 +24,8 @@
|
||||
"queued": "{{title}} (В очереди)",
|
||||
"game_has_no_executable": "Файл запуска игры не выбран",
|
||||
"sign_in": "Войти",
|
||||
"friends": "Друзья"
|
||||
"friends": "Друзья",
|
||||
"need_help": "Нужна помощь?"
|
||||
},
|
||||
"header": {
|
||||
"search": "Поиск",
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
"home": {
|
||||
"featured": "特色推荐",
|
||||
"surprise_me": "向我推荐",
|
||||
"no_results": "没有找到结果"
|
||||
"no_results": "没有找到结果",
|
||||
"start_typing": "键入以开始搜素...",
|
||||
"hot": "当下热门",
|
||||
"weekly": "📅本周热门游戏",
|
||||
"achievements": "🏆尝试击败"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "游戏目录",
|
||||
@@ -20,7 +24,8 @@
|
||||
"home": "主页",
|
||||
"queued": "{{title}} (已加入下载队列)",
|
||||
"game_has_no_executable": "未选择游戏的可执行文件",
|
||||
"sign_in": "登入"
|
||||
"sign_in": "登入",
|
||||
"friends": "好友"
|
||||
},
|
||||
"header": {
|
||||
"search": "搜索游戏",
|
||||
@@ -36,17 +41,18 @@
|
||||
"no_downloads_in_progress": "没有正在进行的下载",
|
||||
"downloading_metadata": "正在下载{{title}}的元数据…",
|
||||
"downloading": "正在下载{{title}}… ({{percentage}}完成) - 剩余时间{{eta}} - 速度{{speed}}",
|
||||
"calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间..."
|
||||
"calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间...",
|
||||
"checking_files": "正在校验 {{title}} 的文件... ({{percentage}} 已完成)"
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "下一页",
|
||||
"previous_page": "上一页"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "打开下载选项",
|
||||
"download_options_zero": "无下载选项",
|
||||
"download_options_one": "{{count}}个下载选项",
|
||||
"download_options_other": "{{count}}个下载选项",
|
||||
"open_download_options": "打开下载菜单",
|
||||
"download_options_zero": "无可下载项",
|
||||
"download_options_one": "{{count}}个可下载项",
|
||||
"download_options_other": "{{count}}个可下载项",
|
||||
"updated_at": "更新于{{updated_at}}",
|
||||
"install": "安装",
|
||||
"resume": "恢复",
|
||||
@@ -55,11 +61,13 @@
|
||||
"remove": "移除",
|
||||
"space_left_on_disk": "磁盘剩余空间{{space}}",
|
||||
"eta": "预计完成时间{{eta}}",
|
||||
"calculating_eta": "正在计算剩余时间…",
|
||||
"downloading_metadata": "正在下载元数据…",
|
||||
"filter": "筛选重打包",
|
||||
"requirements": "配置要求",
|
||||
"minimum": "最低要求",
|
||||
"recommended": "推荐要求",
|
||||
"paused": "已暂停",
|
||||
"release_date": "发布于{{date}}",
|
||||
"publisher": "发行商{{publisher}}",
|
||||
"hours": "小时",
|
||||
@@ -80,15 +88,18 @@
|
||||
"playing_now": "正在游戏中",
|
||||
"change": "更改",
|
||||
"repacks_modal_description": "选择您想要下载的重打包",
|
||||
"select_folder_hint": "要更改默认文件夹,请访问",
|
||||
"select_folder_hint": "要更改默认文件夹,请访问<0>设置</0>",
|
||||
"download_now": "立即下载",
|
||||
"no_shop_details": "无法检索商店详细信息.",
|
||||
"download_options": "下载选项",
|
||||
"download_path": "下载路径",
|
||||
"previous_screenshot": "上一张截图",
|
||||
"next_screenshot": "下一张截图",
|
||||
"screenshot": "截图 {{number}}",
|
||||
"open_screenshot": "打开截图 {{number}}",
|
||||
"download_settings": "下载设置",
|
||||
"downloader": "下载器",
|
||||
"select_executable": "选择",
|
||||
"select_executable": "选择可执行文件",
|
||||
"no_executable_selected": "没有可执行文件被指定",
|
||||
"open_folder": "打开目录",
|
||||
"open_download_location": "查看已下载的文件",
|
||||
@@ -107,7 +118,54 @@
|
||||
"download_paused": "下载暂停",
|
||||
"last_downloaded_option": "上次下载的选项",
|
||||
"create_shortcut_success": "成功创建快捷方式",
|
||||
"create_shortcut_error": "创建快捷方式出错"
|
||||
"create_shortcut_error": "创建快捷方式出错",
|
||||
"nsfw_content_title": "本游戏包含不适合展示的内容",
|
||||
"nsfw_content_description": "{{title}}包含可能不适合所有年龄段的内容。您确定要继续吗?",
|
||||
"allow_nsfw_content": "继续",
|
||||
"refuse_nsfw_content": "返回",
|
||||
"stats": "统计数据",
|
||||
"download_count": "下载量",
|
||||
"player_count": "活跃玩家",
|
||||
"download_error": "此下载选项不可用",
|
||||
"download": "下载",
|
||||
"executable_path_in_use": "可执行文件已经被以下游戏 \"{{game}}\" 使用",
|
||||
"warning": "警告:",
|
||||
"hydra_needs_to_remain_open": "对于此次下载,Hydra必须保持开启直至其完成。若海德拉在完成前关闭,您的进度将丢失。",
|
||||
"achievements": "成就",
|
||||
"achievements_count": "成就 {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "云存档",
|
||||
"cloud_save_description": "将您的进度保存在云端,便可在任何设备上继续游戏。",
|
||||
"backups": "备份",
|
||||
"install_backup": "安装",
|
||||
"delete_backup": "删除",
|
||||
"create_backup": "新备份",
|
||||
"last_backup_date": "最后一次备份于{{date}}",
|
||||
"no_backup_preview": "未找到此游戏标题的存档",
|
||||
"restoring_backup": "正在恢复备份({{progress}}已完成)…",
|
||||
"uploading_backup": "上传备份中…",
|
||||
"no_backups": "您尚未为这款游戏创建任何备份",
|
||||
"backup_uploaded": "备份已上传",
|
||||
"backup_deleted": "备份已删除",
|
||||
"backup_restored": "备份已恢复",
|
||||
"see_all_achievements": "查看所有成就",
|
||||
"sign_in_to_see_achievements": "登入以查看所有成就",
|
||||
"mapping_method_automatic": "自动",
|
||||
"mapping_method_manual": "常规",
|
||||
"mapping_method_label": "索引类型",
|
||||
"files_automatically_mapped": "文件已自动索引",
|
||||
"no_backups_created": "没有为此游戏创建过备份",
|
||||
"manage_files": "管理文件",
|
||||
"loading_save_preview": "正在查找要保存的游戏…",
|
||||
"wine_prefix": "Wine 前置",
|
||||
"wine_prefix_description": "运行该游戏所用的 Wine 前置",
|
||||
"no_download_option_info": "无可用信息",
|
||||
"backup_deletion_failed": "删除备份失败",
|
||||
"max_number_of_artifacts_reached": "已达到该游戏备份上限",
|
||||
"achievements_not_sync": "你的成就未同步",
|
||||
"manage_files_description": "管理哪些文件要备份和恢复",
|
||||
"select_folder": "选择文件夹",
|
||||
"backup_from": "{{date}} 时备份",
|
||||
"custom_backup_location_set": "自定义备份文件位置"
|
||||
},
|
||||
"activation": {
|
||||
"title": "激活 Hydra",
|
||||
@@ -124,6 +182,7 @@
|
||||
"paused": "已暂停",
|
||||
"verifying": "正在验证…",
|
||||
"completed": "已完成",
|
||||
"removed": "未下载",
|
||||
"cancel": "取消",
|
||||
"filter": "筛选已下载游戏",
|
||||
"remove": "移除",
|
||||
@@ -138,7 +197,8 @@
|
||||
"downloads_completed": "已完成",
|
||||
"queued": "下载列表",
|
||||
"no_downloads_title": "空空如也",
|
||||
"no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。"
|
||||
"no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。",
|
||||
"checking_files": "正在校验文件…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "下载路径",
|
||||
@@ -181,14 +241,49 @@
|
||||
"found_download_option_zero": "未找到下载选项",
|
||||
"found_download_option_one": "找到 {{countFormatted}} 个下载选项",
|
||||
"found_download_option_other": "找到 {{countFormatted}} 个下载选项",
|
||||
"import": "导入"
|
||||
"import": "导入",
|
||||
"public": "公开",
|
||||
"private": "私密",
|
||||
"friends_only": "仅限朋友",
|
||||
"privacy": "隐私",
|
||||
"profile_visibility": "资料可见性",
|
||||
"profile_visibility_description": "选择谁可以查看您的个人资料和资料库",
|
||||
"required_field": "该字段为必填字段",
|
||||
"source_already_exists": "已添加此来源",
|
||||
"must_be_valid_url": "来源必须是有效的 URL",
|
||||
"blocked_users": "已屏蔽用户",
|
||||
"user_unblocked": "用户已经被屏蔽",
|
||||
"enable_achievement_notifications": "当成就解锁时"
|
||||
},
|
||||
"modal": {
|
||||
"close": "关闭按钮"
|
||||
"notifications": {
|
||||
"download_complete": "下载完成",
|
||||
"game_ready_to_install": "{{title}} 已准备就绪",
|
||||
"repack_list_updated": "重打包列表已更新",
|
||||
"repack_count_one": "{{count}} 重打包已添加",
|
||||
"repack_count_other": "{{count}} 重打包已添加",
|
||||
"new_update_available": "版本 {{version}} 可用",
|
||||
"restart_to_install_update": "重启 Hydra 以安装更新",
|
||||
"notification_achievement_unlocked_title": "{{game}} 的成绩已解锁",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} 和其他 {{count}} 已解锁"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "打开 Hydra",
|
||||
"quit": "退出"
|
||||
},
|
||||
"game_card": {
|
||||
"no_downloads": "无可用下载选项"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "程序未安装",
|
||||
"description": "您的系统中找不到 Wine 或 Lutris 的可执行文件",
|
||||
"instructions": "请检查在 Linux 发行版上安装这些软件的正确方法,以便游戏能够正常运行"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "切换密码可见性"
|
||||
},
|
||||
"modal": {
|
||||
"close": "关闭按钮"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} 小时",
|
||||
"amount_minutes": "{{amount}} 分钟",
|
||||
@@ -208,7 +303,74 @@
|
||||
"cancel": "取消",
|
||||
"successfully_signed_out": "登出成功",
|
||||
"sign_out": "登出",
|
||||
"playing_for": "Playing for {{amount}}",
|
||||
"sign_out_modal_text": "您的资料库与您当前的账户相关联。注销后,您的资料库将不再可见,任何进度也不会保存。继续退出吗?"
|
||||
"playing_for": "已经玩了{{amount}}",
|
||||
"sign_out_modal_text": "您的资料库与您当前的账户相关联。注销后,您的资料库将不再可见,任何进度也不会保存。继续退出吗?",
|
||||
"add_friends": "添加好友",
|
||||
"add": "添加",
|
||||
"friend_code": "好友代码",
|
||||
"see_profile": "查看资料",
|
||||
"sending": "发送中",
|
||||
"friend_request_sent": "好友请求已发送",
|
||||
"friends": "好友",
|
||||
"friends_list": "好友列表",
|
||||
"user_not_found": "未找到此用户",
|
||||
"block_user": "屏蔽此用户",
|
||||
"add_friend": "添加好友",
|
||||
"request_sent": "请求已发送",
|
||||
"request_received": "已收到请求",
|
||||
"accept_request": "同意申请",
|
||||
"ignore_request": "忽略申请",
|
||||
"cancel_request": "取消申请",
|
||||
"undo_friendship": "解除好友关系",
|
||||
"request_accepted": "请求已通过",
|
||||
"user_blocked_successfully": "成功屏蔽此用户",
|
||||
"user_block_modal_text": "这将会屏蔽 {{displayName}}",
|
||||
"blocked_users": "黑名单用户",
|
||||
"unblock": "解除屏蔽",
|
||||
"no_friends_added": "你还没有添加过好友",
|
||||
"pending": "待处理",
|
||||
"no_pending_invites": "您没有待处理的邀请",
|
||||
"no_blocked_users": "你没有已经拉人黑名单的用户",
|
||||
"friend_code_copied": "好友代码已复制",
|
||||
"undo_friendship_modal_text": "这将使你与 {{displayName}} 解除好友关系",
|
||||
"privacy_hint": "要调整谁可以看到你的个人资料,可以去<0>设置</0>中修改",
|
||||
"locked_profile": "此个人资料是私密的",
|
||||
"image_process_failure": "处理图片时发生错误",
|
||||
"required_field": "此字段为必填项",
|
||||
"displayname_min_length": "显示名称最少必须为3个字符。",
|
||||
"displayname_max_length": "显示名称最多必须为50个字符",
|
||||
"report_profile": "举报此资料",
|
||||
"report_reason": "为什么你要举报此资料?",
|
||||
"report_description": "额外信息",
|
||||
"report_description_placeholder": "额外信息",
|
||||
"report": "举报",
|
||||
"report_reason_hate": "Hate speech",
|
||||
"report_reason_sexual_content": "色情内容",
|
||||
"report_reason_violence": "暴力",
|
||||
"report_reason_spam": "骚扰",
|
||||
"report_reason_other": "其他",
|
||||
"profile_reported": "个人资料已举报",
|
||||
"your_friend_code": "你的好友代码:",
|
||||
"upload_banner": "上传横幅",
|
||||
"uploading_banner": "上传横幅中…",
|
||||
"background_image_updated": "背景图片已更新"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "成就已解锁",
|
||||
"user_achievements": "{{displayName}}的成就",
|
||||
"your_achievements": "你的成就",
|
||||
"unlocked_at": "解锁于:",
|
||||
"subscription_needed": "需要订阅 Hydra Cloud 才能看到此内容",
|
||||
"new_achievements_unlocked": "从 {{gameCount}} 游戏中解锁 {{achievementCount}} 新成就"
|
||||
},
|
||||
"tour": {
|
||||
"subscription_tour_title": "Hydra 云订阅",
|
||||
"subscribe_now": "现在订购",
|
||||
"cloud_saving": "云存档",
|
||||
"cloud_achievements": "将你的成就保存至云端",
|
||||
"animated_profile_picture": "动画头像",
|
||||
"premium_support": "高级技术支持",
|
||||
"show_and_compare_achievements": "展示并与其他用户比较您的成就",
|
||||
"animated_profile_banner": "动态个人简介横幅"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ export const LUDUSAVI_MANIFEST_URL = "https://cdn.losbroxas.org/manifest.yaml";
|
||||
|
||||
export const defaultDownloadsPath = app.getPath("downloads");
|
||||
|
||||
export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging");
|
||||
|
||||
export const databaseDirectory = path.join(app.getPath("appData"), "hydra");
|
||||
export const databasePath = path.join(
|
||||
databaseDirectory,
|
||||
import.meta.env.MAIN_VITE_API_URL.includes("staging")
|
||||
? "hydra_test.db"
|
||||
: "hydra.db"
|
||||
isStaging ? "hydra_test.db" : "hydra.db"
|
||||
);
|
||||
|
||||
export const logsPath = path.join(app.getPath("appData"), "hydra", "logs");
|
||||
@@ -19,6 +19,10 @@ export const seedsPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, "seeds")
|
||||
: path.join(__dirname, "..", "..", "seeds");
|
||||
|
||||
export const achievementSoundPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, "achievement.wav")
|
||||
: path.join(__dirname, "..", "..", "resources", "achievement.wav");
|
||||
|
||||
export const backupsPath = path.join(app.getPath("userData"), "Backups");
|
||||
|
||||
export const appVersion = app.getVersion();
|
||||
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
|
||||
|
||||
@@ -35,6 +35,12 @@ export class UserPreferences {
|
||||
@Column("boolean", { default: false })
|
||||
runAtStartup: boolean;
|
||||
|
||||
@Column("boolean", { default: false })
|
||||
startMinimized: boolean;
|
||||
|
||||
@Column("boolean", { default: false })
|
||||
disableNsfwAlert: boolean;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import * as Sentry from "@sentry/electron/main";
|
||||
|
||||
import { userAuthRepository } from "@main/repository";
|
||||
import { registerEvent } from "../register-event";
|
||||
@@ -10,8 +9,6 @@ const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
if (!auth) return null;
|
||||
const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload;
|
||||
|
||||
Sentry.setContext("sessionId", payload.sessionId);
|
||||
|
||||
return payload.sessionId;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import * as Sentry from "@sentry/electron/main";
|
||||
import {
|
||||
DownloadManager,
|
||||
HydraApi,
|
||||
@@ -29,9 +28,6 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
gamesPlaytime.clear();
|
||||
});
|
||||
|
||||
/* Removes user from Sentry */
|
||||
Sentry.setUser(null);
|
||||
|
||||
/* Cancels any ongoing downloads */
|
||||
DownloadManager.cancelDownload();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const { autoUpdater } = updater;
|
||||
const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
autoUpdater.removeAllListeners();
|
||||
if (app.isPackaged) {
|
||||
autoUpdater.quitAndInstall(true, true);
|
||||
autoUpdater.quitAndInstall(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import type { HowLongToBeatCategory } from "@types";
|
||||
import { getHowLongToBeatGame, searchHowLongToBeat } from "@main/services";
|
||||
import type { GameShop, HowLongToBeatCategory } from "@types";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import { formatName } from "@shared";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const getHowLongToBeat = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
title: string
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
): Promise<HowLongToBeatCategory[] | null> => {
|
||||
const response = await searchHowLongToBeat(title);
|
||||
|
||||
const game = response.data.find((game) => {
|
||||
return formatName(game.game_name) === formatName(title);
|
||||
const params = new URLSearchParams({
|
||||
objectId,
|
||||
shop,
|
||||
});
|
||||
|
||||
if (!game) return null;
|
||||
const howLongToBeat = await getHowLongToBeatGame(String(game.game_id));
|
||||
|
||||
return howLongToBeat;
|
||||
return HydraApi.get(`/games/how-long-to-beat?${params.toString()}`, null, {
|
||||
needsAuth: false,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getHowLongToBeat", getHowLongToBeat);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { appVersion, defaultDownloadsPath } from "@main/constants";
|
||||
import { appVersion, defaultDownloadsPath, isStaging } from "@main/constants";
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import "./catalogue/get-catalogue";
|
||||
@@ -72,5 +72,6 @@ import "./misc/show-item-in-folder";
|
||||
|
||||
ipcMain.handle("ping", () => "pong");
|
||||
ipcMain.handle("getVersion", () => appVersion);
|
||||
ipcMain.handle("isStaging", () => isStaging);
|
||||
ipcMain.handle("isPortableVersion", () => isPortableVersion());
|
||||
ipcMain.handle("getDefaultDownloadsPath", () => defaultDownloadsPath);
|
||||
|
||||
@@ -24,6 +24,7 @@ const createGameShortcut = async (
|
||||
const options = {
|
||||
filePath,
|
||||
name: removeSymbolsFromName(game.title),
|
||||
outputPath: app.getPath("desktop"),
|
||||
};
|
||||
|
||||
return createDesktopShortcut({
|
||||
|
||||
@@ -5,9 +5,9 @@ import { registerEvent } from "../register-event";
|
||||
const selectGameWinePrefix = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
id: number,
|
||||
winePrefixPath: string
|
||||
winePrefixPath: string | null
|
||||
) => {
|
||||
return gameRepository.update({ id }, { winePrefixPath });
|
||||
return gameRepository.update({ id }, { winePrefixPath: winePrefixPath });
|
||||
};
|
||||
|
||||
registerEvent("selectGameWinePrefix", selectGameWinePrefix);
|
||||
|
||||
@@ -6,14 +6,18 @@ import { parseExecutablePath } from "../helpers/parse-executable-path";
|
||||
const updateExecutablePath = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
id: number,
|
||||
executablePath: string
|
||||
executablePath: string | null
|
||||
) => {
|
||||
const parsedPath = executablePath
|
||||
? parseExecutablePath(executablePath)
|
||||
: null;
|
||||
|
||||
return gameRepository.update(
|
||||
{
|
||||
id,
|
||||
},
|
||||
{
|
||||
executablePath: parseExecutablePath(executablePath),
|
||||
executablePath: parsedPath,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { shell } from "electron";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { userAuthRepository } from "@main/repository";
|
||||
import {
|
||||
userAuthRepository,
|
||||
userPreferencesRepository,
|
||||
} from "@main/repository";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const userAuth = await userAuthRepository.findOne({ where: { id: 1 } });
|
||||
const [userAuth, userPreferences] = await Promise.all([
|
||||
userAuthRepository.findOne({ where: { id: 1 } }),
|
||||
userPreferencesRepository.findOne({ where: { id: 1 } }),
|
||||
]);
|
||||
|
||||
if (!userAuth) {
|
||||
return;
|
||||
@@ -16,6 +22,7 @@ const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
|
||||
const params = new URLSearchParams({
|
||||
token: paymentToken,
|
||||
lng: userPreferences?.language || "en",
|
||||
});
|
||||
|
||||
shell.openExternal(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
import type { StartGameDownloadPayload } from "@types";
|
||||
import { DownloadManager, HydraApi, logger } from "@main/services";
|
||||
|
||||
|
||||
@@ -16,15 +16,16 @@ const windowsStartupPath = path.join(
|
||||
|
||||
const autoLaunch = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
enabled: boolean
|
||||
autoLaunchProps: { enabled: boolean; minimized: boolean }
|
||||
) => {
|
||||
if (!app.isPackaged) return;
|
||||
|
||||
const appLauncher = new AutoLaunch({
|
||||
name: app.getName(),
|
||||
isHidden: autoLaunchProps.minimized,
|
||||
});
|
||||
|
||||
if (enabled) {
|
||||
if (autoLaunchProps.enabled) {
|
||||
appLauncher.enable().catch((err) => {
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { app, BrowserWindow, net, protocol } from "electron";
|
||||
import { init } from "@sentry/electron/main";
|
||||
import updater from "electron-updater";
|
||||
import i18n from "i18next";
|
||||
import path from "node:path";
|
||||
@@ -26,12 +25,6 @@ autoUpdater.logger = logger;
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) app.quit();
|
||||
|
||||
if (import.meta.env.MAIN_VITE_SENTRY_DSN) {
|
||||
init({
|
||||
dsn: import.meta.env.MAIN_VITE_SENTRY_DSN,
|
||||
});
|
||||
}
|
||||
|
||||
app.commandLine.appendSwitch("--no-sandbox");
|
||||
|
||||
i18n.init({
|
||||
@@ -101,8 +94,10 @@ app.whenReady().then(async () => {
|
||||
i18n.changeLanguage(userPreferences.language);
|
||||
}
|
||||
|
||||
WindowManager.createMainWindow();
|
||||
WindowManager.createNotificationWindow();
|
||||
if (!process.argv.includes("--hidden")) {
|
||||
WindowManager.createMainWindow();
|
||||
}
|
||||
|
||||
WindowManager.createSystemTray(userPreferences?.language || "en");
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ import { AddAchievementNotificationPreference } from "./migrations/2024101301290
|
||||
import { CreateUserSubscription } from "./migrations/20241015235142_create_user_subscription";
|
||||
import { AddBackgroundImageUrl } from "./migrations/20241016100249_add_background_image_url";
|
||||
import { AddWinePrefixToGame } from "./migrations/20241019081648_add_wine_prefix_to_game";
|
||||
|
||||
import { AddStartMinimizedColumn } from "./migrations/20241030171454_add_start_minimized_column";
|
||||
import { AddDisableNsfwAlertColumn } from "./migrations/20241106053733_add_disable_nsfw_alert_column";
|
||||
export type HydraMigration = Knex.Migration & { name: string };
|
||||
|
||||
class MigrationSource implements Knex.MigrationSource<HydraMigration> {
|
||||
@@ -27,6 +28,8 @@ class MigrationSource implements Knex.MigrationSource<HydraMigration> {
|
||||
CreateUserSubscription,
|
||||
AddBackgroundImageUrl,
|
||||
AddWinePrefixToGame,
|
||||
AddStartMinimizedColumn,
|
||||
AddDisableNsfwAlertColumn,
|
||||
]);
|
||||
}
|
||||
getMigrationName(migration: HydraMigration): string {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { HydraMigration } from "@main/knex-client";
|
||||
import type { Knex } from "knex";
|
||||
|
||||
export const AddStartMinimizedColumn: HydraMigration = {
|
||||
name: "AddStartMinimizedColumn",
|
||||
up: (knex: Knex) => {
|
||||
return knex.schema.alterTable("user_preferences", (table) => {
|
||||
return table.boolean("startMinimized").notNullable().defaultTo(0);
|
||||
});
|
||||
},
|
||||
|
||||
down: async (knex: Knex) => {
|
||||
return knex.schema.alterTable("user_preferences", (table) => {
|
||||
return table.dropColumn("startMinimized");
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { HydraMigration } from "@main/knex-client";
|
||||
import type { Knex } from "knex";
|
||||
|
||||
export const AddDisableNsfwAlertColumn: HydraMigration = {
|
||||
name: "AddDisableNsfwAlertColumn",
|
||||
up: (knex: Knex) => {
|
||||
return knex.schema.alterTable("user_preferences", (table) => {
|
||||
return table.boolean("disableNsfwAlert").notNullable().defaultTo(0);
|
||||
});
|
||||
},
|
||||
|
||||
down: async (knex: Knex) => {
|
||||
return knex.schema.alterTable("user_preferences", (table) => {
|
||||
return table.dropColumn("disableNsfwAlert");
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -13,7 +13,7 @@ import type { AchievementFile, UnlockedAchievement } from "@types";
|
||||
import { achievementsLogger } from "../logger";
|
||||
import { Cracker } from "@shared";
|
||||
import { IsNull, Not } from "typeorm";
|
||||
import { WindowManager } from "../window-manager";
|
||||
import { publishCombinedNewAchievementNotification } from "../notifications";
|
||||
|
||||
const fileStats: Map<string, number> = new Map();
|
||||
const fltFiles: Map<string, Set<string>> = new Map();
|
||||
@@ -219,7 +219,6 @@ export class AchievementWatcherManager {
|
||||
const games = await gameRepository.find({
|
||||
where: {
|
||||
isDeleted: false,
|
||||
winePrefixPath: Not(IsNull()),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -242,12 +241,21 @@ export class AchievementWatcherManager {
|
||||
? await this.preSearchAchievementsWindows()
|
||||
: await this.preSearchAchievementsWithWine();
|
||||
|
||||
WindowManager.notificationWindow?.webContents.send(
|
||||
"on-combined-achievements-unlocked",
|
||||
newAchievementsCount.filter((achievements) => achievements).length,
|
||||
newAchievementsCount.reduce((acc, val) => acc + val, 0)
|
||||
const totalNewGamesWithAchievements = newAchievementsCount.filter(
|
||||
(achievements) => achievements
|
||||
).length;
|
||||
const totalNewAchievements = newAchievementsCount.reduce(
|
||||
(acc, val) => acc + val,
|
||||
0
|
||||
);
|
||||
|
||||
if (totalNewAchievements > 0) {
|
||||
publishCombinedNewAchievementNotification(
|
||||
totalNewAchievements,
|
||||
totalNewGamesWithAchievements
|
||||
);
|
||||
}
|
||||
|
||||
this.hasFinishedMergingWithRemote = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,11 +79,11 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(publicDocuments, "Steam", "CODEX"),
|
||||
fileLocation: ["achievements.ini"],
|
||||
fileLocation: ["<objectId>", "achievements.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(appData, "Steam", "CODEX"),
|
||||
fileLocation: ["achievements.ini"],
|
||||
fileLocation: ["<objectId>", "achievements.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -92,7 +92,7 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(publicDocuments, "Steam", "RUNE"),
|
||||
fileLocation: ["achievements.ini"],
|
||||
fileLocation: ["<objectId>", "achievements.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -101,11 +101,11 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(publicDocuments, "OnlineFix"),
|
||||
fileLocation: ["Stats", "Achievements.ini"],
|
||||
fileLocation: ["<objectId>", "Stats", "Achievements.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(publicDocuments, "OnlineFix"),
|
||||
fileLocation: ["Achievements.ini"],
|
||||
fileLocation: ["<objectId>", "Achievements.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -114,11 +114,11 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(appData, "Goldberg SteamEmu Saves"),
|
||||
fileLocation: ["achievements.json"],
|
||||
fileLocation: ["<objectId>", "achievements.json"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(appData, "GSE Saves"),
|
||||
fileLocation: ["achievements.json"],
|
||||
fileLocation: ["<objectId>", "achievements.json"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -131,19 +131,19 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(programData, "RLD!"),
|
||||
fileLocation: ["achievements.ini"],
|
||||
fileLocation: ["<objectId>", "achievements.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(programData, "Steam", "Player"),
|
||||
fileLocation: ["stats", "achievements.ini"],
|
||||
fileLocation: ["<objectId>", "stats", "achievements.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(programData, "Steam", "RLD!"),
|
||||
fileLocation: ["stats", "achievements.ini"],
|
||||
fileLocation: ["<objectId>", "stats", "achievements.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(programData, "Steam", "dodi"),
|
||||
fileLocation: ["stats", "achievements.ini"],
|
||||
fileLocation: ["<objectId>", "stats", "achievements.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -152,11 +152,16 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(appData, "EMPRESS", "remote"),
|
||||
fileLocation: ["achievements.json"],
|
||||
fileLocation: ["<objectId>", "achievements.json"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(publicDocuments, "EMPRESS", "remote"),
|
||||
fileLocation: ["achievements.json"],
|
||||
folderPath: path.join(publicDocuments, "EMPRESS"),
|
||||
fileLocation: [
|
||||
"<objectId>",
|
||||
"remote",
|
||||
"<objectId>",
|
||||
"achievements.json",
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -165,15 +170,15 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(documents, "SKIDROW"),
|
||||
fileLocation: ["SteamEmu", "UserStats", "achiev.ini"],
|
||||
fileLocation: ["<objectId>", "SteamEmu", "UserStats", "achiev.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(documents, "Player"),
|
||||
fileLocation: ["SteamEmu", "UserStats", "achiev.ini"],
|
||||
fileLocation: ["<objectId>", "SteamEmu", "UserStats", "achiev.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(localAppData, "SKIDROW"),
|
||||
fileLocation: ["SteamEmu", "UserStats", "achiev.ini"],
|
||||
fileLocation: ["<objectId>", "SteamEmu", "UserStats", "achiev.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -182,7 +187,7 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(appData, "CreamAPI"),
|
||||
fileLocation: ["stats", "CreamAPI.Achievements.cfg"],
|
||||
fileLocation: ["<objectId>", "stats", "CreamAPI.Achievements.cfg"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -191,7 +196,7 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(appData, "SmartSteamEmu"),
|
||||
fileLocation: ["User", "Achievements.ini"],
|
||||
fileLocation: ["<objectId>", "User", "Achievements.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -213,11 +218,11 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(appData, "RLE"),
|
||||
fileLocation: ["achievements.ini"],
|
||||
fileLocation: ["<objectId>", "achievements.ini"],
|
||||
},
|
||||
{
|
||||
folderPath: path.join(appData, "RLE"),
|
||||
fileLocation: ["Achievements.ini"],
|
||||
fileLocation: ["<objectId>", "Achievements.ini"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -226,7 +231,7 @@ const getPathFromCracker = (cracker: Cracker) => {
|
||||
return [
|
||||
{
|
||||
folderPath: path.join(appData, ".1911"),
|
||||
fileLocation: ["achievement"],
|
||||
fileLocation: ["<objectId>", "achievement"],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -253,8 +258,7 @@ export const findAchievementFiles = (game: Game) => {
|
||||
const filePath = path.join(
|
||||
game.winePrefixPath ?? "",
|
||||
folderPath,
|
||||
objectId,
|
||||
...fileLocation
|
||||
...mapFileLocationWithObjectId(fileLocation, objectId)
|
||||
);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
@@ -303,6 +307,15 @@ export const findAchievementFileInExecutableDirectory = (
|
||||
];
|
||||
};
|
||||
|
||||
const mapFileLocationWithObjectId = (
|
||||
fileLocation: string[],
|
||||
objectId: string
|
||||
) => {
|
||||
return fileLocation.map((location) =>
|
||||
location.replace("<objectId>", objectId)
|
||||
);
|
||||
};
|
||||
|
||||
export const findAllAchievementFiles = () => {
|
||||
const gameAchievementFiles = new Map<string, AchievementFile[]>();
|
||||
|
||||
@@ -315,7 +328,10 @@ export const findAllAchievementFiles = () => {
|
||||
const objectIds = fs.readdirSync(folderPath);
|
||||
|
||||
for (const objectId of objectIds) {
|
||||
const filePath = path.join(folderPath, objectId, ...fileLocation);
|
||||
const filePath = path.join(
|
||||
folderPath,
|
||||
...mapFileLocationWithObjectId(fileLocation, objectId)
|
||||
);
|
||||
|
||||
if (!fs.existsSync(filePath)) continue;
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ import { HydraApi } from "../hydra-api";
|
||||
import { getUnlockedAchievements } from "@main/events/user/get-unlocked-achievements";
|
||||
import { Game } from "@main/entity";
|
||||
import { achievementsLogger } from "../logger";
|
||||
import { SubscriptionRequiredError } from "@shared";
|
||||
import { publishNewAchievementNotification } from "../notifications";
|
||||
|
||||
const saveAchievementsOnLocal = async (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
achievements: any[],
|
||||
achievements: UnlockedAchievement[],
|
||||
sendUpdateEvent: boolean
|
||||
) => {
|
||||
return gameAchievementRepository
|
||||
@@ -83,6 +83,8 @@ export const mergeAchievements = async (
|
||||
};
|
||||
});
|
||||
|
||||
const mergedLocalAchievements = unlockedAchievements.concat(newAchievements);
|
||||
|
||||
if (
|
||||
newAchievements.length &&
|
||||
publishNotification &&
|
||||
@@ -100,7 +102,7 @@ export const mergeAchievements = async (
|
||||
);
|
||||
});
|
||||
})
|
||||
.filter((achievement) => achievement)
|
||||
.filter((achievement) => Boolean(achievement))
|
||||
.map((achievement) => {
|
||||
return {
|
||||
displayName: achievement!.displayName,
|
||||
@@ -108,25 +110,20 @@ export const mergeAchievements = async (
|
||||
};
|
||||
});
|
||||
|
||||
WindowManager.notificationWindow?.webContents.send(
|
||||
"on-achievement-unlocked",
|
||||
game.objectID,
|
||||
game.shop,
|
||||
achievementsInfo
|
||||
);
|
||||
publishNewAchievementNotification({
|
||||
achievements: achievementsInfo,
|
||||
unlockedAchievementCount: mergedLocalAchievements.length,
|
||||
totalAchievementCount: achievementsData.length,
|
||||
gameTitle: game.title,
|
||||
gameIcon: game.iconUrl,
|
||||
});
|
||||
}
|
||||
|
||||
const mergedLocalAchievements = unlockedAchievements.concat(newAchievements);
|
||||
|
||||
if (game.remoteId) {
|
||||
await HydraApi.put(
|
||||
"/profile/games/achievements",
|
||||
{
|
||||
id: game.remoteId,
|
||||
achievements: mergedLocalAchievements,
|
||||
},
|
||||
{ needsSubscription: true }
|
||||
)
|
||||
await HydraApi.put("/profile/games/achievements", {
|
||||
id: game.remoteId,
|
||||
achievements: mergedLocalAchievements,
|
||||
})
|
||||
.then((response) => {
|
||||
return saveAchievementsOnLocal(
|
||||
response.objectId,
|
||||
@@ -136,9 +133,7 @@ export const mergeAchievements = async (
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!(err instanceof SubscriptionRequiredError)) {
|
||||
achievementsLogger.error(err);
|
||||
}
|
||||
achievementsLogger.error(err);
|
||||
|
||||
return saveAchievementsOnLocal(
|
||||
game.objectID,
|
||||
|
||||
@@ -65,6 +65,11 @@ export const parseAchievementFile = (
|
||||
return processCreamAPI(parsed);
|
||||
}
|
||||
|
||||
if (type === Cracker.empress) {
|
||||
const parsed = jsonParse(filePath);
|
||||
return processGoldberg(parsed);
|
||||
}
|
||||
|
||||
if (type === Cracker.razor1911) {
|
||||
return processRazor1911(filePath);
|
||||
}
|
||||
@@ -118,7 +123,7 @@ const jsonParse = (filePath: string) => {
|
||||
const processRazor1911 = (filePath: string): UnlockedAchievement[] => {
|
||||
try {
|
||||
const fileContent = readFileSync(filePath, "utf-8");
|
||||
achievementsLogger.log("processing file", filePath, fileContent);
|
||||
|
||||
const lines =
|
||||
fileContent.charCodeAt(0) === 0xfeff
|
||||
? fileContent.slice(1).split(/[\r\n]+/)
|
||||
@@ -136,7 +141,7 @@ const processRazor1911 = (filePath: string): UnlockedAchievement[] => {
|
||||
});
|
||||
}
|
||||
}
|
||||
achievementsLogger.log("processing file", achievements);
|
||||
|
||||
return achievements;
|
||||
} catch (err) {
|
||||
achievementsLogger.error(`Error processing ${filePath}`, err);
|
||||
|
||||
@@ -174,8 +174,10 @@ export class PythonInstance {
|
||||
.then((response) => response.data);
|
||||
}
|
||||
|
||||
private static async handleRpcError(_error: unknown) {
|
||||
await this.rpc.get("/healthcheck").catch(() => {
|
||||
private static async handleRpcError(error: unknown) {
|
||||
logger.error(error);
|
||||
|
||||
return this.rpc.get("/healthcheck").catch(() => {
|
||||
logger.error(
|
||||
"RPC healthcheck failed. Killing process and starting again"
|
||||
);
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { requestWebPage } from "@main/helpers";
|
||||
import type {
|
||||
HowLongToBeatCategory,
|
||||
HowLongToBeatSearchResponse,
|
||||
} from "@types";
|
||||
import { formatName } from "@shared";
|
||||
import { logger } from "./logger";
|
||||
import UserAgent from "user-agents";
|
||||
|
||||
const state = {
|
||||
apiKey: null as string | null,
|
||||
};
|
||||
|
||||
const getHowLongToBeatSearchApiKey = async () => {
|
||||
const userAgent = new UserAgent();
|
||||
|
||||
const document = await requestWebPage("https://howlongtobeat.com/");
|
||||
const scripts = Array.from(document.querySelectorAll("script"));
|
||||
|
||||
const appScript = scripts.find((script) =>
|
||||
script.src.startsWith("/_next/static/chunks/pages/_app")
|
||||
);
|
||||
|
||||
if (!appScript) return null;
|
||||
|
||||
const response = await axios.get(
|
||||
`https://howlongtobeat.com${appScript.src}`,
|
||||
{
|
||||
headers: {
|
||||
"User-Agent": userAgent.toString(),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const results = /fetch\("\/api\/search\/"\.concat\("(.*?)"\)/gm.exec(
|
||||
response.data
|
||||
);
|
||||
|
||||
if (!results) return null;
|
||||
|
||||
return results[1];
|
||||
};
|
||||
|
||||
export const searchHowLongToBeat = async (gameName: string) => {
|
||||
state.apiKey = state.apiKey ?? (await getHowLongToBeatSearchApiKey());
|
||||
if (!state.apiKey) return { data: [] };
|
||||
|
||||
const userAgent = new UserAgent();
|
||||
|
||||
const response = await axios
|
||||
.post(
|
||||
`https://howlongtobeat.com/api/search/${state.apiKey}`,
|
||||
{
|
||||
searchType: "games",
|
||||
searchTerms: formatName(gameName).split(" "),
|
||||
searchPage: 1,
|
||||
size: 20,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"User-Agent": userAgent.toString(),
|
||||
Referer: "https://howlongtobeat.com/",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((error) => {
|
||||
logger.error("Error searching HowLongToBeat:", error?.response?.status);
|
||||
return { data: { data: [] } };
|
||||
});
|
||||
|
||||
return response.data as HowLongToBeatSearchResponse;
|
||||
};
|
||||
|
||||
const parseListItems = ($lis: Element[]) => {
|
||||
return $lis.map(($li) => {
|
||||
const title = $li.querySelector("h4")?.textContent;
|
||||
const [, accuracyClassName] = Array.from(($li as HTMLElement).classList);
|
||||
|
||||
const accuracy = accuracyClassName.split("time_").at(1);
|
||||
|
||||
return {
|
||||
title: title ?? "",
|
||||
duration: $li.querySelector("h5")?.textContent ?? "",
|
||||
accuracy: accuracy ?? "",
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const getHowLongToBeatGame = async (
|
||||
id: string
|
||||
): Promise<HowLongToBeatCategory[]> => {
|
||||
const document = await requestWebPage(`https://howlongtobeat.com/game/${id}`);
|
||||
|
||||
const $ul = document.querySelector(".shadow_shadow ul");
|
||||
if (!$ul) return [];
|
||||
|
||||
const $lis = Array.from($ul.children);
|
||||
|
||||
const [$firstLi] = $lis;
|
||||
|
||||
if ($firstLi.tagName === "DIV") {
|
||||
const $pcData = $lis.find(($li) => $li.textContent?.includes("PC"));
|
||||
return parseListItems(Array.from($pcData?.querySelectorAll("li") ?? []));
|
||||
}
|
||||
|
||||
return parseListItems($lis);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { UserNotLoggedInError, SubscriptionRequiredError } from "@shared";
|
||||
import { omit } from "lodash-es";
|
||||
import { appVersion } from "@main/constants";
|
||||
import { getUserData } from "./user/get-user-data";
|
||||
import { isFuture, isToday } from "date-fns";
|
||||
|
||||
interface HydraApiOptions {
|
||||
needsAuth?: boolean;
|
||||
@@ -45,10 +46,8 @@ export class HydraApi {
|
||||
}
|
||||
|
||||
private static hasActiveSubscription() {
|
||||
return (
|
||||
this.userAuth.subscription?.expiresAt &&
|
||||
this.userAuth.subscription.expiresAt > new Date()
|
||||
);
|
||||
const expiresAt = this.userAuth.subscription?.expiresAt;
|
||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
||||
}
|
||||
|
||||
static async handleExternalAuth(uri: string) {
|
||||
@@ -112,6 +111,8 @@ export class HydraApi {
|
||||
expirationTimestamp: 0,
|
||||
subscription: null,
|
||||
};
|
||||
|
||||
this.post("/auth/logout", {}, { needsAuth: false }).catch(() => {});
|
||||
}
|
||||
|
||||
static async setupApi() {
|
||||
@@ -152,21 +153,26 @@ export class HydraApi {
|
||||
(error) => {
|
||||
logger.error(" ---- RESPONSE ERROR -----");
|
||||
const { config } = error;
|
||||
const data = JSON.parse(config.data);
|
||||
|
||||
logger.error(
|
||||
config.method,
|
||||
config.baseURL,
|
||||
config.url,
|
||||
config.headers,
|
||||
config.data
|
||||
omit(config.headers, ["accessToken", "refreshToken"]),
|
||||
Array.isArray(data)
|
||||
? data
|
||||
: omit(data, ["accessToken", "refreshToken"])
|
||||
);
|
||||
if (error.response) {
|
||||
logger.error(
|
||||
"Response",
|
||||
"Response error:",
|
||||
error.response.status,
|
||||
error.response.data
|
||||
);
|
||||
} else if (error.request) {
|
||||
logger.error("Request", error.request);
|
||||
const errorData = error.toJSON();
|
||||
logger.error("Request error:", errorData.message);
|
||||
} else {
|
||||
logger.error("Error", error.message);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ export * from "./steam-250";
|
||||
export * from "./steam-grid";
|
||||
export * from "./window-manager";
|
||||
export * from "./download";
|
||||
export * from "./how-long-to-beat";
|
||||
export * from "./process-watcher";
|
||||
export * from "./main-loop";
|
||||
export * from "./hydra-api";
|
||||
|
||||
@@ -24,6 +24,7 @@ export class Ludusavi {
|
||||
workerData: {
|
||||
binaryPath: this.binaryPath,
|
||||
},
|
||||
maxThreads: 1,
|
||||
});
|
||||
|
||||
static async getConfig() {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { Notification, nativeImage } from "electron";
|
||||
import { t } from "i18next";
|
||||
import { parseICO } from "icojs";
|
||||
import trayIcon from "@resources/tray-icon.png?asset";
|
||||
import { Game } from "@main/entity";
|
||||
import { gameRepository, userPreferencesRepository } from "@main/repository";
|
||||
|
||||
const getGameIconNativeImage = async (gameId: number) => {
|
||||
try {
|
||||
const game = await gameRepository.findOne({
|
||||
where: {
|
||||
id: gameId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!game?.iconUrl) return undefined;
|
||||
|
||||
const images = await parseICO(
|
||||
Buffer.from(game.iconUrl.split("base64,")[1], "base64")
|
||||
);
|
||||
|
||||
const highResIcon = images.find((image) => image.width >= 128);
|
||||
if (!highResIcon) return undefined;
|
||||
|
||||
return nativeImage.createFromBuffer(Buffer.from(highResIcon.buffer));
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const publishDownloadCompleteNotification = async (game: Game) => {
|
||||
const userPreferences = await userPreferencesRepository.findOne({
|
||||
where: { id: 1 },
|
||||
});
|
||||
|
||||
const icon = await getGameIconNativeImage(game.id);
|
||||
|
||||
if (userPreferences?.downloadNotificationsEnabled) {
|
||||
new Notification({
|
||||
title: t("download_complete", {
|
||||
ns: "notifications",
|
||||
}),
|
||||
body: t("game_ready_to_install", {
|
||||
ns: "notifications",
|
||||
title: game.title,
|
||||
}),
|
||||
icon,
|
||||
}).show();
|
||||
}
|
||||
};
|
||||
|
||||
export const publishNotificationUpdateReadyToInstall = async (
|
||||
version: string
|
||||
) => {
|
||||
new Notification({
|
||||
title: t("new_update_available", {
|
||||
ns: "notifications",
|
||||
version,
|
||||
}),
|
||||
body: t("restart_to_install_update", {
|
||||
ns: "notifications",
|
||||
}),
|
||||
icon: trayIcon,
|
||||
}).show();
|
||||
};
|
||||
|
||||
export const publishNewFriendRequestNotification = async () => {};
|
||||
146
src/main/services/notifications/index.ts
Normal file
146
src/main/services/notifications/index.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { Notification, app } from "electron";
|
||||
import { t } from "i18next";
|
||||
import trayIcon from "@resources/tray-icon.png?asset";
|
||||
import { Game } from "@main/entity";
|
||||
import { userPreferencesRepository } from "@main/repository";
|
||||
import fs from "node:fs";
|
||||
import axios from "axios";
|
||||
import path from "node:path";
|
||||
import sound from "sound-play";
|
||||
import { achievementSoundPath } from "@main/constants";
|
||||
import icon from "@resources/icon.png?asset";
|
||||
import { NotificationOptions, toXmlString } from "./xml";
|
||||
import { logger } from "../logger";
|
||||
|
||||
async function downloadImage(url: string | null) {
|
||||
if (!url) return undefined;
|
||||
if (!url.startsWith("http")) return undefined;
|
||||
|
||||
const fileName = url.split("/").pop()!;
|
||||
const outputPath = path.join(app.getPath("temp"), fileName);
|
||||
const writer = fs.createWriteStream(outputPath);
|
||||
|
||||
const response = await axios.get(url, {
|
||||
responseType: "stream",
|
||||
});
|
||||
|
||||
response.data.pipe(writer);
|
||||
|
||||
return new Promise<string | undefined>((resolve) => {
|
||||
writer.on("finish", () => {
|
||||
resolve(outputPath);
|
||||
});
|
||||
writer.on("error", () => {
|
||||
logger.error("Failed to download image", { url });
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const publishDownloadCompleteNotification = async (game: Game) => {
|
||||
const userPreferences = await userPreferencesRepository.findOne({
|
||||
where: { id: 1 },
|
||||
});
|
||||
|
||||
if (userPreferences?.downloadNotificationsEnabled) {
|
||||
new Notification({
|
||||
title: t("download_complete", {
|
||||
ns: "notifications",
|
||||
}),
|
||||
body: t("game_ready_to_install", {
|
||||
ns: "notifications",
|
||||
title: game.title,
|
||||
}),
|
||||
icon: await downloadImage(game.iconUrl),
|
||||
}).show();
|
||||
}
|
||||
};
|
||||
|
||||
export const publishNotificationUpdateReadyToInstall = async (
|
||||
version: string
|
||||
) => {
|
||||
new Notification({
|
||||
title: t("new_update_available", {
|
||||
ns: "notifications",
|
||||
version,
|
||||
}),
|
||||
body: t("restart_to_install_update", {
|
||||
ns: "notifications",
|
||||
}),
|
||||
icon: trayIcon,
|
||||
}).show();
|
||||
};
|
||||
|
||||
export const publishNewFriendRequestNotification = async () => {};
|
||||
|
||||
export const publishCombinedNewAchievementNotification = async (
|
||||
achievementCount,
|
||||
gameCount
|
||||
) => {
|
||||
const options: NotificationOptions = {
|
||||
title: t("achievement_unlocked", { ns: "achievement" }),
|
||||
body: t("new_achievements_unlocked", {
|
||||
ns: "achievement",
|
||||
gameCount,
|
||||
achievementCount,
|
||||
}),
|
||||
icon,
|
||||
silent: true,
|
||||
};
|
||||
|
||||
new Notification({
|
||||
...options,
|
||||
toastXml: toXmlString(options),
|
||||
}).show();
|
||||
|
||||
if (process.platform !== "linux") {
|
||||
sound.play(achievementSoundPath);
|
||||
}
|
||||
};
|
||||
|
||||
export const publishNewAchievementNotification = async (info: {
|
||||
achievements: { displayName: string; iconUrl: string }[];
|
||||
unlockedAchievementCount: number;
|
||||
totalAchievementCount: number;
|
||||
gameTitle: string;
|
||||
gameIcon: string | null;
|
||||
}) => {
|
||||
const partialOptions =
|
||||
info.achievements.length > 1
|
||||
? {
|
||||
title: t("achievements_unlocked_for_game", {
|
||||
ns: "achievement",
|
||||
gameTitle: info.gameTitle,
|
||||
achievementCount: info.achievements.length,
|
||||
}),
|
||||
body: info.achievements.map((a) => a.displayName).join(", "),
|
||||
icon: (await downloadImage(info.gameIcon)) ?? icon,
|
||||
}
|
||||
: {
|
||||
title: t("achievement_unlocked", { ns: "achievement" }),
|
||||
body: info.achievements[0].displayName,
|
||||
icon: (await downloadImage(info.achievements[0].iconUrl)) ?? icon,
|
||||
};
|
||||
|
||||
const options: NotificationOptions = {
|
||||
...partialOptions,
|
||||
silent: true,
|
||||
progress: {
|
||||
value: info.unlockedAchievementCount / info.totalAchievementCount,
|
||||
valueOverride: t("achievement_progress", {
|
||||
ns: "achievement",
|
||||
unlockedCount: info.unlockedAchievementCount,
|
||||
totalCount: info.totalAchievementCount,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
new Notification({
|
||||
...options,
|
||||
toastXml: toXmlString(options),
|
||||
}).show();
|
||||
|
||||
if (process.platform !== "linux") {
|
||||
sound.play(achievementSoundPath);
|
||||
}
|
||||
};
|
||||
79
src/main/services/notifications/xml.ts
Normal file
79
src/main/services/notifications/xml.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
export interface NotificationOptions {
|
||||
title: string;
|
||||
body?: string;
|
||||
icon: string;
|
||||
duration?: "short" | "long";
|
||||
silent?: boolean;
|
||||
progress?: {
|
||||
status?: string;
|
||||
value: number;
|
||||
valueOverride: string;
|
||||
};
|
||||
}
|
||||
|
||||
function escape(string: string) {
|
||||
return string.replace(/[<>&'"]/g, (match) => {
|
||||
switch (match) {
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "&":
|
||||
return "&";
|
||||
case "'":
|
||||
return "'";
|
||||
case '"':
|
||||
return """;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addAttributeOrTrim(name: string, value: string) {
|
||||
return value ? `${name}="${value}" ` : "";
|
||||
}
|
||||
|
||||
export function toXmlString(options: NotificationOptions) {
|
||||
let template =
|
||||
"<toast " +
|
||||
`displayTimestamp="${new Date().toISOString()}" ` +
|
||||
`scenario="default" ` +
|
||||
`duration="${options.duration ?? "short"}" ` +
|
||||
`activationType="protocol" ` +
|
||||
">";
|
||||
|
||||
//Visual
|
||||
template += `<visual><binding template="ToastGeneric">`;
|
||||
if (options.icon)
|
||||
template += `<image placement="appLogoOverride" src="${options.icon}" hint-crop="none"/>`;
|
||||
template +=
|
||||
`<text><![CDATA[${options.title}]]></text>` +
|
||||
`<text><![CDATA[${options.body}]]></text>`;
|
||||
|
||||
//Progress bar
|
||||
if (options.progress) {
|
||||
template +=
|
||||
"<progress " +
|
||||
`value="${options.progress.value}" ` +
|
||||
`status="" ` +
|
||||
addAttributeOrTrim(
|
||||
"valueStringOverride",
|
||||
escape(options.progress.valueOverride)
|
||||
) +
|
||||
"/>";
|
||||
}
|
||||
template += "</binding></visual>";
|
||||
|
||||
//Actions
|
||||
template += "<actions>";
|
||||
template += "</actions>";
|
||||
|
||||
//Audio
|
||||
template += "<audio " + `silent="true" ` + `loop="false" ` + "/>";
|
||||
|
||||
//EOF
|
||||
template += "</toast>";
|
||||
|
||||
return template;
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
userAuthRepository,
|
||||
userSubscriptionRepository,
|
||||
} from "@main/repository";
|
||||
import * as Sentry from "@sentry/electron/main";
|
||||
import { UserNotLoggedInError } from "@shared";
|
||||
import { logger } from "../logger";
|
||||
|
||||
@@ -39,15 +38,13 @@ export const getUserData = () => {
|
||||
await userSubscriptionRepository.delete({ id: 1 });
|
||||
}
|
||||
|
||||
Sentry.setUser({ id: me.id, username: me.username });
|
||||
|
||||
return me;
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err instanceof UserNotLoggedInError) {
|
||||
return null;
|
||||
}
|
||||
logger.error("Failed to get logged user", err);
|
||||
logger.error("Failed to get logged user");
|
||||
const loggedUser = await userAuthRepository.findOne({
|
||||
where: { id: 1 },
|
||||
relations: { subscription: true },
|
||||
@@ -59,6 +56,7 @@ export const getUserData = () => {
|
||||
id: loggedUser.userId,
|
||||
username: "",
|
||||
bio: "",
|
||||
email: null,
|
||||
profileVisibility: "PUBLIC" as ProfileVisibility,
|
||||
subscription: loggedUser.subscription
|
||||
? {
|
||||
|
||||
@@ -20,7 +20,6 @@ import UserAgent from "user-agents";
|
||||
|
||||
export class WindowManager {
|
||||
public static mainWindow: Electron.BrowserWindow | null = null;
|
||||
public static notificationWindow: Electron.BrowserWindow | null = null;
|
||||
|
||||
private static loadMainWindowURL(hash = "") {
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
@@ -39,21 +38,6 @@ export class WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static loadNotificationWindowURL() {
|
||||
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
|
||||
this.notificationWindow?.loadURL(
|
||||
`${process.env["ELECTRON_RENDERER_URL"]}#/achievement-notification`
|
||||
);
|
||||
} else {
|
||||
this.notificationWindow?.loadFile(
|
||||
path.join(__dirname, "../renderer/index.html"),
|
||||
{
|
||||
hash: "achievement-notification",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static createMainWindow() {
|
||||
if (this.mainWindow) return;
|
||||
|
||||
@@ -63,7 +47,7 @@ export class WindowManager {
|
||||
minWidth: 1024,
|
||||
minHeight: 540,
|
||||
backgroundColor: "#1c1c1c",
|
||||
titleBarStyle: process.platform === "win32" ? "hidden" : "default",
|
||||
titleBarStyle: process.platform === "linux" ? "default" : "hidden",
|
||||
...(process.platform === "linux" ? { icon } : {}),
|
||||
trafficLightPosition: { x: 16, y: 16 },
|
||||
titleBarOverlay: {
|
||||
@@ -101,6 +85,14 @@ export class WindowManager {
|
||||
return callback(details);
|
||||
}
|
||||
|
||||
if (details.url.includes("featurebase")) {
|
||||
return callback(details);
|
||||
}
|
||||
|
||||
if (details.url.includes("chatwoot")) {
|
||||
return callback(details);
|
||||
}
|
||||
|
||||
const headers = {
|
||||
"access-control-allow-origin": ["*"],
|
||||
"access-control-allow-methods": ["GET, POST, PUT, DELETE, OPTIONS"],
|
||||
@@ -151,32 +143,6 @@ export class WindowManager {
|
||||
});
|
||||
}
|
||||
|
||||
public static createNotificationWindow() {
|
||||
this.notificationWindow = new BrowserWindow({
|
||||
transparent: true,
|
||||
maximizable: false,
|
||||
autoHideMenuBar: true,
|
||||
minimizable: false,
|
||||
focusable: false,
|
||||
skipTaskbar: true,
|
||||
frame: false,
|
||||
width: 350,
|
||||
height: 104,
|
||||
x: 0,
|
||||
y: 0,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, "../preload/index.mjs"),
|
||||
sandbox: false,
|
||||
},
|
||||
});
|
||||
this.notificationWindow.setIgnoreMouseEvents(true);
|
||||
// this.notificationWindow.setVisibleOnAllWorkspaces(true, {
|
||||
// visibleOnFullScreen: true,
|
||||
// });
|
||||
this.notificationWindow.setAlwaysOnTop(true, "screen-saver", 1);
|
||||
this.loadNotificationWindowURL();
|
||||
}
|
||||
|
||||
public static openAuthWindow() {
|
||||
if (this.mainWindow) {
|
||||
const authWindow = new BrowserWindow({
|
||||
@@ -310,14 +276,15 @@ export class WindowManager {
|
||||
if (process.platform !== "darwin") {
|
||||
tray.addListener("click", () => {
|
||||
if (this.mainWindow) {
|
||||
if (WindowManager.mainWindow?.isMinimized())
|
||||
WindowManager.mainWindow.restore();
|
||||
|
||||
WindowManager.mainWindow?.focus();
|
||||
return;
|
||||
if (
|
||||
WindowManager.mainWindow?.isMinimized() ||
|
||||
!WindowManager.mainWindow?.isVisible()
|
||||
) {
|
||||
WindowManager.mainWindow?.show();
|
||||
}
|
||||
} else {
|
||||
this.createMainWindow();
|
||||
}
|
||||
|
||||
this.createMainWindow();
|
||||
});
|
||||
|
||||
tray.addListener("right-click", showContextMenu);
|
||||
|
||||
2
src/main/vite-env.d.ts
vendored
2
src/main/vite-env.d.ts
vendored
@@ -3,8 +3,8 @@
|
||||
interface ImportMetaEnv {
|
||||
readonly MAIN_VITE_STEAMGRIDDB_API_KEY: string;
|
||||
readonly MAIN_VITE_API_URL: string;
|
||||
readonly MAIN_VITE_ANALYTICS_API_URL: string;
|
||||
readonly MAIN_VITE_AUTH_URL: string;
|
||||
readonly MAIN_VITE_SENTRY_DSN: string;
|
||||
readonly MAIN_VITE_CHECKOUT_URL: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,25 @@ export const backupGame = ({
|
||||
preview?: boolean;
|
||||
winePrefix?: string;
|
||||
}) => {
|
||||
const args = ["backup", title, "--api", "--force"];
|
||||
return new Promise((resolve, reject) => {
|
||||
const args = ["backup", title, "--api", "--force"];
|
||||
|
||||
if (preview) args.push("--preview");
|
||||
if (backupPath) args.push("--path", backupPath);
|
||||
if (winePrefix) args.push("--wine-prefix", winePrefix);
|
||||
if (preview) args.push("--preview");
|
||||
if (backupPath) args.push("--path", backupPath);
|
||||
if (winePrefix) args.push("--wine-prefix", winePrefix);
|
||||
|
||||
const result = cp.execFileSync(binaryPath, args);
|
||||
cp.execFile(
|
||||
binaryPath,
|
||||
args,
|
||||
(err: cp.ExecFileException | null, stdout: string) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
return JSON.parse(result.toString("utf-8")) as LudusaviBackup;
|
||||
return resolve(JSON.parse(stdout) as LudusaviBackup);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const restoreBackup = (backupPath: string) => {
|
||||
|
||||
@@ -42,8 +42,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
getGameShopDetails: (objectId: string, shop: GameShop, language: string) =>
|
||||
ipcRenderer.invoke("getGameShopDetails", objectId, shop, language),
|
||||
getRandomGame: () => ipcRenderer.invoke("getRandomGame"),
|
||||
getHowLongToBeat: (title: string) =>
|
||||
ipcRenderer.invoke("getHowLongToBeat", title),
|
||||
getHowLongToBeat: (objectId: string, shop: GameShop) =>
|
||||
ipcRenderer.invoke("getHowLongToBeat", objectId, shop),
|
||||
getGames: (take?: number, skip?: number) =>
|
||||
ipcRenderer.invoke("getGames", take, skip),
|
||||
searchGameRepacks: (query: string) =>
|
||||
@@ -51,35 +51,6 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
getGameStats: (objectId: string, shop: GameShop) =>
|
||||
ipcRenderer.invoke("getGameStats", objectId, shop),
|
||||
getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"),
|
||||
onAchievementUnlocked: (
|
||||
cb: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
achievements?: { displayName: string; iconUrl: string }[]
|
||||
) => void
|
||||
) => {
|
||||
const listener = (
|
||||
_event: Electron.IpcRendererEvent,
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
achievements?: { displayName: string; iconUrl: string }[]
|
||||
) => cb(objectId, shop, achievements);
|
||||
ipcRenderer.on("on-achievement-unlocked", listener);
|
||||
return () =>
|
||||
ipcRenderer.removeListener("on-achievement-unlocked", listener);
|
||||
},
|
||||
onCombinedAchievementsUnlocked: (
|
||||
cb: (gameCount: number, achievementsCount: number) => void
|
||||
) => {
|
||||
const listener = (
|
||||
_event: Electron.IpcRendererEvent,
|
||||
gameCount: number,
|
||||
achievementCount: number
|
||||
) => cb(gameCount, achievementCount);
|
||||
ipcRenderer.on("on-combined-achievements-unlocked", listener);
|
||||
return () =>
|
||||
ipcRenderer.removeListener("on-combined-achievements-unlocked", listener);
|
||||
},
|
||||
onUpdateAchievements: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
@@ -101,7 +72,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"),
|
||||
updateUserPreferences: (preferences: UserPreferences) =>
|
||||
ipcRenderer.invoke("updateUserPreferences", preferences),
|
||||
autoLaunch: (enabled: boolean) => ipcRenderer.invoke("autoLaunch", enabled),
|
||||
autoLaunch: (autoLaunchProps: { enabled: boolean; minimized: boolean }) =>
|
||||
ipcRenderer.invoke("autoLaunch", autoLaunchProps),
|
||||
authenticateRealDebrid: (apiToken: string) =>
|
||||
ipcRenderer.invoke("authenticateRealDebrid", apiToken),
|
||||
|
||||
@@ -115,9 +87,9 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
ipcRenderer.invoke("addGameToLibrary", objectId, title, shop),
|
||||
createGameShortcut: (id: number) =>
|
||||
ipcRenderer.invoke("createGameShortcut", id),
|
||||
updateExecutablePath: (id: number, executablePath: string) =>
|
||||
updateExecutablePath: (id: number, executablePath: string | null) =>
|
||||
ipcRenderer.invoke("updateExecutablePath", id, executablePath),
|
||||
selectGameWinePrefix: (id: number, winePrefixPath: string) =>
|
||||
selectGameWinePrefix: (id: number, winePrefixPath: string | null) =>
|
||||
ipcRenderer.invoke("selectGameWinePrefix", id, winePrefixPath),
|
||||
verifyExecutablePathInUse: (executablePath: string) =>
|
||||
ipcRenderer.invoke("verifyExecutablePathInUse", executablePath),
|
||||
@@ -226,6 +198,7 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
ping: () => ipcRenderer.invoke("ping"),
|
||||
getVersion: () => ipcRenderer.invoke("getVersion"),
|
||||
getDefaultDownloadsPath: () => ipcRenderer.invoke("getDefaultDownloadsPath"),
|
||||
isStaging: () => ipcRenderer.invoke("isStaging"),
|
||||
isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"),
|
||||
openExternal: (src: string) => ipcRenderer.invoke("openExternal", src),
|
||||
openCheckout: () => ipcRenderer.invoke("openCheckout"),
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>Hydra</title>
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: local: *; media-src 'self' local: data: *;"
|
||||
content="default-src 'self' 'unsafe-inline' * data: local:;"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -126,3 +126,9 @@ export const titleBar = style({
|
||||
zIndex: "4",
|
||||
borderBottom: `1px solid ${vars.color.border}`,
|
||||
} as ComplexStyleRule);
|
||||
|
||||
export const cloudText = style({
|
||||
background: "linear-gradient(270deg, #16B195 50%, #3E62C0 100%)",
|
||||
backgroundClip: "text",
|
||||
color: "transparent",
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useContext, useEffect, useRef } from "react";
|
||||
|
||||
import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components";
|
||||
|
||||
@@ -29,11 +29,6 @@ import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
||||
import { downloadSourcesWorker } from "./workers";
|
||||
import { repacksContext } from "./context";
|
||||
import { logger } from "./logger";
|
||||
import { SubscriptionTourModal } from "./pages/shared-modals/subscription-tour-modal";
|
||||
|
||||
interface TourModals {
|
||||
subscriptionModal?: boolean;
|
||||
}
|
||||
|
||||
export interface AppProps {
|
||||
children: React.ReactNode;
|
||||
@@ -59,8 +54,13 @@ export function App() {
|
||||
hideFriendsModal,
|
||||
} = useUserDetails();
|
||||
|
||||
const { userDetails, fetchUserDetails, updateUserDetails, clearUserDetails } =
|
||||
useUserDetails();
|
||||
const {
|
||||
userDetails,
|
||||
hasActiveSubscription,
|
||||
fetchUserDetails,
|
||||
updateUserDetails,
|
||||
clearUserDetails,
|
||||
} = useUserDetails();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -77,9 +77,6 @@ export function App() {
|
||||
|
||||
const { showSuccessToast } = useToast();
|
||||
|
||||
const [showSubscritionTourModal, setShowSubscritionTourModal] =
|
||||
useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([window.electron.getUserPreferences(), updateLibrary()]).then(
|
||||
([preferences]) => {
|
||||
@@ -117,24 +114,23 @@ export function App() {
|
||||
dispatch(setProfileBackground(profileBackground));
|
||||
}
|
||||
|
||||
fetchUserDetails().then((response) => {
|
||||
if (response) {
|
||||
updateUserDetails(response);
|
||||
syncFriendRequests();
|
||||
}
|
||||
});
|
||||
fetchUserDetails()
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
updateUserDetails(response);
|
||||
syncFriendRequests();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (document.getElementById("external-resources")) return;
|
||||
|
||||
const $script = document.createElement("script");
|
||||
$script.id = "external-resources";
|
||||
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}?t=${Date.now()}`;
|
||||
document.head.appendChild($script);
|
||||
});
|
||||
}, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const tourModalsString = window.localStorage.getItem("tourModals") || "{}";
|
||||
|
||||
const tourModals = JSON.parse(tourModalsString) as TourModals;
|
||||
|
||||
if (!tourModals.subscriptionModal) {
|
||||
setShowSubscritionTourModal(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onSignIn = useCallback(() => {
|
||||
fetchUserDetails().then((response) => {
|
||||
if (response) {
|
||||
@@ -222,7 +218,7 @@ export function App() {
|
||||
|
||||
useEffect(() => {
|
||||
new MutationObserver(() => {
|
||||
const modal = document.body.querySelector("[role=dialog]");
|
||||
const modal = document.body.querySelector("[data-hydra-dialog]");
|
||||
|
||||
dispatch(toggleDraggingDisabled(Boolean(modal)));
|
||||
}).observe(document.body, {
|
||||
@@ -280,14 +276,6 @@ export function App() {
|
||||
});
|
||||
}, [indexRepacks]);
|
||||
|
||||
const handleCloseSubscriptionTourModal = () => {
|
||||
setShowSubscritionTourModal(false);
|
||||
window.localStorage.setItem(
|
||||
"tourModals",
|
||||
JSON.stringify({ subscriptionModal: true } as TourModals)
|
||||
);
|
||||
};
|
||||
|
||||
const handleToastClose = useCallback(() => {
|
||||
dispatch(closeToast());
|
||||
}, [dispatch]);
|
||||
@@ -296,7 +284,12 @@ export function App() {
|
||||
<>
|
||||
{window.electron.platform === "win32" && (
|
||||
<div className={styles.titleBar}>
|
||||
<h4>Hydra</h4>
|
||||
<h4>
|
||||
Hydra
|
||||
{hasActiveSubscription && (
|
||||
<span className={styles.cloudText}> Cloud</span>
|
||||
)}
|
||||
</h4>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -307,11 +300,6 @@ export function App() {
|
||||
onClose={handleToastClose}
|
||||
/>
|
||||
|
||||
<SubscriptionTourModal
|
||||
visible={showSubscritionTourModal && false}
|
||||
onClose={handleCloseSubscriptionTourModal}
|
||||
/>
|
||||
|
||||
{userDetails && (
|
||||
<UserFriendModal
|
||||
visible={isFriendsModalVisible}
|
||||
|
||||
BIN
src/renderer/src/assets/icons/cloud-animated.gif
Normal file
BIN
src/renderer/src/assets/icons/cloud-animated.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
src/renderer/src/assets/icons/flame-animated.gif
Normal file
BIN
src/renderer/src/assets/icons/flame-animated.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
BIN
src/renderer/src/assets/icons/flame-static.png
Normal file
BIN
src/renderer/src/assets/icons/flame-static.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
src/renderer/src/assets/icons/stars-animated.gif
Normal file
BIN
src/renderer/src/assets/icons/stars-animated.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
@@ -1,725 +0,0 @@
|
||||
{
|
||||
"v": "5.12.1",
|
||||
"fr": 30,
|
||||
"ip": 0,
|
||||
"op": 60,
|
||||
"w": 400,
|
||||
"h": 400,
|
||||
"nm": "Cloud",
|
||||
"ddd": 0,
|
||||
"assets": [],
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 2,
|
||||
"ty": 4,
|
||||
"nm": "Layer 6",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [322.789, 202.565, 0],
|
||||
"to": [-1.5, -0.167, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 30,
|
||||
"s": [313.789, 201.565, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [-1.5, -0.167, 0]
|
||||
},
|
||||
{ "t": 60, "s": [322.789, 202.565, 0] }
|
||||
],
|
||||
"ix": 2,
|
||||
"l": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, -38.564],
|
||||
[38.564, 0],
|
||||
[0, 38.564],
|
||||
[-38.564, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 38.564],
|
||||
[-38.564, 0],
|
||||
[0, -38.564],
|
||||
[38.564, 0]
|
||||
],
|
||||
"v": [
|
||||
[69.827, 0],
|
||||
[0, 69.827],
|
||||
[-69.827, 0],
|
||||
[0, -69.827]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.839215686275, 0.854901960784, 0.933333333333, 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": [0, 0], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"ct": 1,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 3,
|
||||
"ty": 4,
|
||||
"nm": "Layer 5",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [243.704, 202.565, 0],
|
||||
"to": [-1.667, 0, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 30,
|
||||
"s": [233.704, 202.565, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [-1.667, 0, 0]
|
||||
},
|
||||
{ "t": 60, "s": [243.704, 202.565, 0] }
|
||||
],
|
||||
"ix": 2,
|
||||
"l": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, -38.564],
|
||||
[38.564, 0],
|
||||
[0, 38.564],
|
||||
[-38.564, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 38.564],
|
||||
[-38.564, 0],
|
||||
[0, -38.564],
|
||||
[38.564, 0]
|
||||
],
|
||||
"v": [
|
||||
[69.827, 0],
|
||||
[0, 69.827],
|
||||
[-69.827, 0],
|
||||
[0, -69.827]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.839215686275, 0.854901960784, 0.933333333333, 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": [0, 0], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"ct": 1,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 4,
|
||||
"ty": 4,
|
||||
"nm": "Layer 4",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [260.681, 151.053, 0],
|
||||
"to": [1.333, -1.333, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 30,
|
||||
"s": [268.681, 143.053, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [1.333, -1.333, 0]
|
||||
},
|
||||
{ "t": 60, "s": [260.681, 151.053, 0] }
|
||||
],
|
||||
"ix": 2,
|
||||
"l": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, -38.564],
|
||||
[38.564, 0],
|
||||
[0, 38.564],
|
||||
[-38.564, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 38.564],
|
||||
[-38.564, 0],
|
||||
[0, -38.564],
|
||||
[38.564, 0]
|
||||
],
|
||||
"v": [
|
||||
[69.827, 0],
|
||||
[0, 69.827],
|
||||
[-69.827, 0],
|
||||
[0, -69.827]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.839215686275, 0.854901960784, 0.933333333333, 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": [0, 0], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"ct": 1,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 5,
|
||||
"ty": 4,
|
||||
"nm": "Layer 3",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [162.135, 206.563, 0],
|
||||
"to": [-0.833, -0.167, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 30,
|
||||
"s": [157.135, 205.563, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [-0.833, -0.167, 0]
|
||||
},
|
||||
{ "t": 60, "s": [162.135, 206.563, 0] }
|
||||
],
|
||||
"ix": 2,
|
||||
"l": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, -36.66],
|
||||
[36.66, 0],
|
||||
[0, 36.66],
|
||||
[-36.66, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 36.66],
|
||||
[-36.66, 0],
|
||||
[0, -36.66],
|
||||
[36.66, 0]
|
||||
],
|
||||
"v": [
|
||||
[66.378, 0],
|
||||
[0, 66.378],
|
||||
[-66.378, 0],
|
||||
[0, -66.378]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.839215686275, 0.854901960784, 0.933333333333, 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": [0, 0], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"ct": 1,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 6,
|
||||
"ty": 4,
|
||||
"nm": "Layer 2",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [180.178, 132.225, 0],
|
||||
"to": [-0.5, -2.333, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 30,
|
||||
"s": [177.178, 118.225, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [-0.5, -2.333, 0]
|
||||
},
|
||||
{ "t": 60, "s": [180.178, 132.225, 0] }
|
||||
],
|
||||
"ix": 2,
|
||||
"l": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, -50.068],
|
||||
[50.068, 0],
|
||||
[0, 50.068],
|
||||
[-50.068, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 50.068],
|
||||
[-50.068, 0],
|
||||
[0, -50.068],
|
||||
[50.068, 0]
|
||||
],
|
||||
"v": [
|
||||
[90.655, 0],
|
||||
[0, 90.655],
|
||||
[-90.655, 0],
|
||||
[0, -90.655]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.839215686275, 0.854901960784, 0.933333333333, 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": [0, 0], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"ct": 1,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 7,
|
||||
"ty": 4,
|
||||
"nm": "Layer 1",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [95.756, 208.288, 0],
|
||||
"to": [-1.167, 0, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.667, "y": 1 },
|
||||
"o": { "x": 0.333, "y": 0 },
|
||||
"t": 30,
|
||||
"s": [88.756, 208.288, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [-1.167, 0, 0]
|
||||
},
|
||||
{ "t": 60, "s": [95.756, 208.288, 0] }
|
||||
],
|
||||
"ix": 2,
|
||||
"l": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, -35.403],
|
||||
[35.403, 0],
|
||||
[0, 35.403],
|
||||
[-35.403, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 35.403],
|
||||
[-35.403, 0],
|
||||
[0, -35.403],
|
||||
[35.403, 0]
|
||||
],
|
||||
"v": [
|
||||
[64.103, 0],
|
||||
[0, 64.103],
|
||||
[-64.103, 0],
|
||||
[0, -64.103]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.839215686275, 0.854901960784, 0.933333333333, 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": [0, 0], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"ct": 1,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 8,
|
||||
"ty": 3,
|
||||
"nm": "Null 1",
|
||||
"parent": 6,
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 0, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [19.822, 67.775, 0], "ix": 2, "l": 2 },
|
||||
"a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"ip": 0,
|
||||
"op": 270,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
],
|
||||
"markers": [],
|
||||
"props": {}
|
||||
}
|
||||
@@ -1,843 +0,0 @@
|
||||
{
|
||||
"v": "4.8.0",
|
||||
"meta": { "g": "LottieFiles AE 3.5.6", "a": "", "k": "", "d": "", "tc": "" },
|
||||
"fr": 60,
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"w": 714,
|
||||
"h": 678,
|
||||
"nm": "Pre-comp 1",
|
||||
"ddd": 0,
|
||||
"assets": [
|
||||
{
|
||||
"id": "comp_0",
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 1,
|
||||
"ty": 4,
|
||||
"nm": "centro",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.214, "y": 1 },
|
||||
"o": { "x": 0.462, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [450, 907, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{ "t": 30, "s": [450, 1513, 0] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [-348, -169, 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, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-348, -420],
|
||||
[-348, -30]
|
||||
],
|
||||
"c": false
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.854901960784, 0.858823529412, 0.882352941176, 1],
|
||||
"ix": 3
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 4 },
|
||||
"w": { "a": 0, "k": 77, "ix": 5 },
|
||||
"lc": 2,
|
||||
"lj": 1,
|
||||
"ml": 4,
|
||||
"bm": 0,
|
||||
"nm": "Stroke 1",
|
||||
"mn": "ADBE Vector Graphic - Stroke",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": { "a": 0, "k": [-348, -164], "ix": 2 },
|
||||
"a": { "a": 0, "k": [-348, -156], "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": "Shape 1",
|
||||
"np": 3,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 2,
|
||||
"ty": 4,
|
||||
"nm": "esquerdo",
|
||||
"parent": 1,
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.298], "y": [1] },
|
||||
"o": { "x": [0.448], "y": [0] },
|
||||
"t": 6,
|
||||
"s": [43.5]
|
||||
},
|
||||
{ "t": 36, "s": [-1] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [-2, 84, 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, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-178, -102],
|
||||
[-2, 84]
|
||||
],
|
||||
"c": false
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.854901960784, 0.858823529412, 0.882352941176, 1],
|
||||
"ix": 3
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 4 },
|
||||
"w": { "a": 0, "k": 77, "ix": 5 },
|
||||
"lc": 2,
|
||||
"lj": 1,
|
||||
"ml": 4,
|
||||
"bm": 0,
|
||||
"nm": "Stroke 1",
|
||||
"mn": "ADBE Vector Graphic - Stroke",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": { "a": 0, "k": [0, 0], "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": "Shape 1",
|
||||
"np": 3,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tm",
|
||||
"s": { "a": 0, "k": 8, "ix": 1 },
|
||||
"e": { "a": 0, "k": 100, "ix": 2 },
|
||||
"o": { "a": 0, "k": 0, "ix": 3 },
|
||||
"m": 1,
|
||||
"ix": 2,
|
||||
"nm": "Trim Paths 1",
|
||||
"mn": "ADBE Vector Filter - Trim",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 3,
|
||||
"ty": 4,
|
||||
"nm": "direito",
|
||||
"parent": 1,
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.265], "y": [1] },
|
||||
"o": { "x": [0.53], "y": [0] },
|
||||
"t": 6,
|
||||
"s": [-43.5]
|
||||
},
|
||||
{ "t": 36, "s": [1] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [-2, 84, 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, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-178, -102],
|
||||
[-2, 84]
|
||||
],
|
||||
"c": false
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tm",
|
||||
"s": { "a": 0, "k": 8, "ix": 1 },
|
||||
"e": { "a": 0, "k": 100, "ix": 2 },
|
||||
"o": { "a": 0, "k": 0, "ix": 3 },
|
||||
"m": 1,
|
||||
"ix": 2,
|
||||
"nm": "Trim Paths 1",
|
||||
"mn": "ADBE Vector Filter - Trim",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.854901960784, 0.858823529412, 0.882352941176, 1],
|
||||
"ix": 3
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 4 },
|
||||
"w": { "a": 0, "k": 77, "ix": 5 },
|
||||
"lc": 2,
|
||||
"lj": 1,
|
||||
"ml": 4,
|
||||
"bm": 0,
|
||||
"nm": "Stroke 1",
|
||||
"mn": "ADBE Vector Graphic - Stroke",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": { "a": 0, "k": [0, 0], "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": "Shape 1",
|
||||
"np": 4,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "comp_1",
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 1,
|
||||
"ty": 4,
|
||||
"nm": "centro",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.569, "y": 1 },
|
||||
"o": { "x": 0.809, "y": 0 },
|
||||
"t": 0,
|
||||
"s": [450, 391, 0],
|
||||
"to": [0, 0, 0],
|
||||
"ti": [0, 0, 0]
|
||||
},
|
||||
{ "t": 30, "s": [450, 997, 0] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [-348, -169, 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, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-348, -420],
|
||||
[-348, -30]
|
||||
],
|
||||
"c": false
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.854901960784, 0.858823529412, 0.882352941176, 1],
|
||||
"ix": 3
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 4 },
|
||||
"w": { "a": 0, "k": 77, "ix": 5 },
|
||||
"lc": 2,
|
||||
"lj": 1,
|
||||
"ml": 4,
|
||||
"bm": 0,
|
||||
"nm": "Stroke 1",
|
||||
"mn": "ADBE Vector Graphic - Stroke",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": { "a": 0, "k": [-348, -164], "ix": 2 },
|
||||
"a": { "a": 0, "k": [-348, -156], "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": "Shape 1",
|
||||
"np": 3,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 2,
|
||||
"ty": 4,
|
||||
"nm": "esquerdo",
|
||||
"parent": 1,
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.552], "y": [1] },
|
||||
"o": { "x": [0.702], "y": [0] },
|
||||
"t": 0,
|
||||
"s": [-1]
|
||||
},
|
||||
{ "t": 30, "s": [43.5] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [-2, 84, 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, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-178, -102],
|
||||
[-2, 84]
|
||||
],
|
||||
"c": false
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.854901960784, 0.858823529412, 0.882352941176, 1],
|
||||
"ix": 3
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 4 },
|
||||
"w": { "a": 0, "k": 77, "ix": 5 },
|
||||
"lc": 2,
|
||||
"lj": 1,
|
||||
"ml": 4,
|
||||
"bm": 0,
|
||||
"nm": "Stroke 1",
|
||||
"mn": "ADBE Vector Graphic - Stroke",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": { "a": 0, "k": [0, 0], "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": "Shape 1",
|
||||
"np": 3,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tm",
|
||||
"s": { "a": 0, "k": 8, "ix": 1 },
|
||||
"e": { "a": 0, "k": 100, "ix": 2 },
|
||||
"o": { "a": 0, "k": 0, "ix": 3 },
|
||||
"m": 1,
|
||||
"ix": 2,
|
||||
"nm": "Trim Paths 1",
|
||||
"mn": "ADBE Vector Filter - Trim",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 3,
|
||||
"ty": 4,
|
||||
"nm": "direito",
|
||||
"parent": 1,
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.47], "y": [1] },
|
||||
"o": { "x": [0.735], "y": [0] },
|
||||
"t": 0,
|
||||
"s": [1]
|
||||
},
|
||||
{ "t": 30, "s": [-43.5] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [-2, 84, 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, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-178, -102],
|
||||
[-2, 84]
|
||||
],
|
||||
"c": false
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tm",
|
||||
"s": { "a": 0, "k": 8, "ix": 1 },
|
||||
"e": { "a": 0, "k": 100, "ix": 2 },
|
||||
"o": { "a": 0, "k": 0, "ix": 3 },
|
||||
"m": 1,
|
||||
"ix": 2,
|
||||
"nm": "Trim Paths 1",
|
||||
"mn": "ADBE Vector Filter - Trim",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.854901960784, 0.858823529412, 0.882352941176, 1],
|
||||
"ix": 3
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 4 },
|
||||
"w": { "a": 0, "k": 77, "ix": 5 },
|
||||
"lc": 2,
|
||||
"lj": 1,
|
||||
"ml": 4,
|
||||
"bm": 0,
|
||||
"nm": "Stroke 1",
|
||||
"mn": "ADBE Vector Graphic - Stroke",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": { "a": 0, "k": [0, 0], "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": "Shape 1",
|
||||
"np": 4,
|
||||
"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": "seta 2",
|
||||
"refId": "comp_0",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [357, -247, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [450, 960, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6 }
|
||||
},
|
||||
"ao": 0,
|
||||
"w": 900,
|
||||
"h": 1920,
|
||||
"ip": 30,
|
||||
"op": 120,
|
||||
"st": 30,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 2,
|
||||
"ty": 0,
|
||||
"nm": "seta",
|
||||
"refId": "comp_1",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [357, 258, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [450, 345, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6 }
|
||||
},
|
||||
"ao": 0,
|
||||
"w": 900,
|
||||
"h": 690,
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 3,
|
||||
"ty": 4,
|
||||
"nm": "base Outlines",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [357, 548.713, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [357.81, 129.934, 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, 0],
|
||||
[0, 50.043],
|
||||
[0, 0],
|
||||
[-21.158, 0],
|
||||
[0, -21.447],
|
||||
[0, 0],
|
||||
[-7.049, 0],
|
||||
[0, 0],
|
||||
[0, 7.149],
|
||||
[0, 0],
|
||||
[-21.158, 0],
|
||||
[0, -21.447],
|
||||
[0, 0],
|
||||
[49.368, 0]
|
||||
],
|
||||
"o": [
|
||||
[-49.369, 0],
|
||||
[0, 0],
|
||||
[0, -21.447],
|
||||
[21.158, 0],
|
||||
[0, 0],
|
||||
[0, 7.145],
|
||||
[0, 0],
|
||||
[7.053, 0],
|
||||
[0, 0],
|
||||
[0, -21.447],
|
||||
[21.158, 0],
|
||||
[0, 0],
|
||||
[0, 50.043],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-268.169, 129.445],
|
||||
[-357.559, 38.834],
|
||||
[-357.559, -90.61],
|
||||
[-319.249, -129.445],
|
||||
[-280.939, -90.61],
|
||||
[-280.939, 38.834],
|
||||
[-268.169, 51.778],
|
||||
[268.169, 51.778],
|
||||
[280.939, 38.834],
|
||||
[280.939, -90.61],
|
||||
[319.249, -129.445],
|
||||
[357.559, -90.61],
|
||||
[357.559, 38.834],
|
||||
[268.169, 129.445]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.865977448108, 0.86824388691, 0.890449075138, 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": [357.809, 129.695], "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": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
],
|
||||
"markers": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,928 +0,0 @@
|
||||
{
|
||||
"v": "4.8.0",
|
||||
"meta": { "g": "LottieFiles AE 3.5.6", "a": "", "k": "", "d": "", "tc": "" },
|
||||
"fr": 60,
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"w": 300,
|
||||
"h": 300,
|
||||
"nm": "Comp 1",
|
||||
"ddd": 0,
|
||||
"assets": [
|
||||
{
|
||||
"id": "comp_0",
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 1,
|
||||
"ty": 5,
|
||||
"nm": "3",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.055], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 0,
|
||||
"s": [0]
|
||||
},
|
||||
{
|
||||
"i": { "x": [0.055], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 30,
|
||||
"s": [8]
|
||||
},
|
||||
{ "t": 60, "s": [0] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [930, 525, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [16.605, -23.904, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [170, 170, 100], "ix": 6 }
|
||||
},
|
||||
"ao": 0,
|
||||
"hasMask": true,
|
||||
"masksProperties": [
|
||||
{
|
||||
"inv": false,
|
||||
"mode": "a",
|
||||
"pt": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[14.987, -34.426],
|
||||
[9.105, -30.309],
|
||||
[9.987, -22.073],
|
||||
[17.487, -16.779],
|
||||
[24.105, -23.544],
|
||||
[22.193, -30.603]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 1
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 3 },
|
||||
"x": { "a": 0, "k": 0, "ix": 4 },
|
||||
"nm": "Mask 1"
|
||||
}
|
||||
],
|
||||
"ef": [
|
||||
{
|
||||
"ty": 21,
|
||||
"nm": "Fill",
|
||||
"np": 9,
|
||||
"mn": "ADBE Fill",
|
||||
"ix": 1,
|
||||
"en": 1,
|
||||
"ef": [
|
||||
{
|
||||
"ty": 10,
|
||||
"nm": "Fill Mask",
|
||||
"mn": "ADBE Fill-0001",
|
||||
"ix": 1,
|
||||
"v": { "a": 0, "k": 0, "ix": 1 }
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "All Masks",
|
||||
"mn": "ADBE Fill-0007",
|
||||
"ix": 2,
|
||||
"v": { "a": 0, "k": 0, "ix": 2 }
|
||||
},
|
||||
{
|
||||
"ty": 2,
|
||||
"nm": "Color",
|
||||
"mn": "ADBE Fill-0002",
|
||||
"ix": 3,
|
||||
"v": {
|
||||
"a": 0,
|
||||
"k": [0.992156863213, 0.880375564098, 0.128396704793, 1],
|
||||
"ix": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "Invert",
|
||||
"mn": "ADBE Fill-0006",
|
||||
"ix": 4,
|
||||
"v": { "a": 0, "k": 0, "ix": 4 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Horizontal Feather",
|
||||
"mn": "ADBE Fill-0003",
|
||||
"ix": 5,
|
||||
"v": { "a": 0, "k": 0, "ix": 5 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Vertical Feather",
|
||||
"mn": "ADBE Fill-0004",
|
||||
"ix": 6,
|
||||
"v": { "a": 0, "k": 0, "ix": 6 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Opacity",
|
||||
"mn": "ADBE Fill-0005",
|
||||
"ix": 7,
|
||||
"v": { "a": 0, "k": 1, "ix": 7 }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"t": {
|
||||
"d": {
|
||||
"k": [
|
||||
{
|
||||
"s": {
|
||||
"s": 40,
|
||||
"f": "SegoeUIEmoji",
|
||||
"t": "✨",
|
||||
"j": 0,
|
||||
"tr": 0,
|
||||
"lh": 48,
|
||||
"ls": 0,
|
||||
"fc": [1, 1, 1]
|
||||
},
|
||||
"t": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"p": {},
|
||||
"m": { "g": 1, "a": { "a": 0, "k": [0, 0], "ix": 2 } },
|
||||
"a": []
|
||||
},
|
||||
"ip": 0,
|
||||
"op": 123,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 2,
|
||||
"ty": 5,
|
||||
"nm": "2",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.055], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 0,
|
||||
"s": [0]
|
||||
},
|
||||
{
|
||||
"i": { "x": [0.055], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 30,
|
||||
"s": [-8]
|
||||
},
|
||||
{ "t": 60, "s": [0] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [960, 540, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [31.912, -13.397, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [170, 170, 100], "ix": 6 }
|
||||
},
|
||||
"ao": 0,
|
||||
"hasMask": true,
|
||||
"masksProperties": [
|
||||
{
|
||||
"inv": false,
|
||||
"mode": "a",
|
||||
"pt": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[31.31, -34.72],
|
||||
[24.546, -22.514],
|
||||
[16.605, -16.485],
|
||||
[17.046, -11.338],
|
||||
[21.163, -7.073],
|
||||
[27.487, -0.309],
|
||||
[33.663, 10.133],
|
||||
[47.634, -1.926],
|
||||
[51.31, -12.073]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 1
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 3 },
|
||||
"x": { "a": 0, "k": 0, "ix": 4 },
|
||||
"nm": "Mask 1"
|
||||
}
|
||||
],
|
||||
"ef": [
|
||||
{
|
||||
"ty": 21,
|
||||
"nm": "Fill",
|
||||
"np": 9,
|
||||
"mn": "ADBE Fill",
|
||||
"ix": 1,
|
||||
"en": 1,
|
||||
"ef": [
|
||||
{
|
||||
"ty": 10,
|
||||
"nm": "Fill Mask",
|
||||
"mn": "ADBE Fill-0001",
|
||||
"ix": 1,
|
||||
"v": { "a": 0, "k": 0, "ix": 1 }
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "All Masks",
|
||||
"mn": "ADBE Fill-0007",
|
||||
"ix": 2,
|
||||
"v": { "a": 0, "k": 0, "ix": 2 }
|
||||
},
|
||||
{
|
||||
"ty": 2,
|
||||
"nm": "Color",
|
||||
"mn": "ADBE Fill-0002",
|
||||
"ix": 3,
|
||||
"v": {
|
||||
"a": 0,
|
||||
"k": [0.992156863213, 0.880375564098, 0.128396704793, 1],
|
||||
"ix": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "Invert",
|
||||
"mn": "ADBE Fill-0006",
|
||||
"ix": 4,
|
||||
"v": { "a": 0, "k": 0, "ix": 4 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Horizontal Feather",
|
||||
"mn": "ADBE Fill-0003",
|
||||
"ix": 5,
|
||||
"v": { "a": 0, "k": 0, "ix": 5 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Vertical Feather",
|
||||
"mn": "ADBE Fill-0004",
|
||||
"ix": 6,
|
||||
"v": { "a": 0, "k": 0, "ix": 6 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Opacity",
|
||||
"mn": "ADBE Fill-0005",
|
||||
"ix": 7,
|
||||
"v": { "a": 0, "k": 1, "ix": 7 }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"t": {
|
||||
"d": {
|
||||
"k": [
|
||||
{
|
||||
"s": {
|
||||
"s": 40,
|
||||
"f": "SegoeUIEmoji",
|
||||
"t": "✨",
|
||||
"j": 0,
|
||||
"tr": 0,
|
||||
"lh": 48,
|
||||
"ls": 0,
|
||||
"fc": [1, 1, 1]
|
||||
},
|
||||
"t": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"p": {},
|
||||
"m": { "g": 1, "a": { "a": 0, "k": [0, 0], "ix": 2 } },
|
||||
"a": []
|
||||
},
|
||||
"ip": 0,
|
||||
"op": 123,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
},
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 3,
|
||||
"ty": 5,
|
||||
"nm": "✨",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0.055], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 0,
|
||||
"s": [0]
|
||||
},
|
||||
{
|
||||
"i": { "x": [0.055], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 30,
|
||||
"s": [8]
|
||||
},
|
||||
{ "t": 60, "s": [0] }
|
||||
],
|
||||
"ix": 10
|
||||
},
|
||||
"p": { "a": 0, "k": [935, 560, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [14.973, -6.64, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [170, 170, 100], "ix": 6 }
|
||||
},
|
||||
"ao": 0,
|
||||
"hasMask": true,
|
||||
"masksProperties": [
|
||||
{
|
||||
"inv": false,
|
||||
"mode": "a",
|
||||
"pt": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[13.957, -17.514],
|
||||
[2.928, -9.132],
|
||||
[2.487, 1.603],
|
||||
[14.105, 7.339],
|
||||
[21.605, -0.161],
|
||||
[22.193, -5.161],
|
||||
[17.34, -10.014]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 1
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 3 },
|
||||
"x": { "a": 0, "k": 0, "ix": 4 },
|
||||
"nm": "Mask 1"
|
||||
}
|
||||
],
|
||||
"ef": [
|
||||
{
|
||||
"ty": 21,
|
||||
"nm": "Fill",
|
||||
"np": 9,
|
||||
"mn": "ADBE Fill",
|
||||
"ix": 1,
|
||||
"en": 1,
|
||||
"ef": [
|
||||
{
|
||||
"ty": 10,
|
||||
"nm": "Fill Mask",
|
||||
"mn": "ADBE Fill-0001",
|
||||
"ix": 1,
|
||||
"v": { "a": 0, "k": 0, "ix": 1 }
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "All Masks",
|
||||
"mn": "ADBE Fill-0007",
|
||||
"ix": 2,
|
||||
"v": { "a": 0, "k": 0, "ix": 2 }
|
||||
},
|
||||
{
|
||||
"ty": 2,
|
||||
"nm": "Color",
|
||||
"mn": "ADBE Fill-0002",
|
||||
"ix": 3,
|
||||
"v": {
|
||||
"a": 0,
|
||||
"k": [0.992156863213, 0.880375564098, 0.128396704793, 1],
|
||||
"ix": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "Invert",
|
||||
"mn": "ADBE Fill-0006",
|
||||
"ix": 4,
|
||||
"v": { "a": 0, "k": 0, "ix": 4 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Horizontal Feather",
|
||||
"mn": "ADBE Fill-0003",
|
||||
"ix": 5,
|
||||
"v": { "a": 0, "k": 0, "ix": 5 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Vertical Feather",
|
||||
"mn": "ADBE Fill-0004",
|
||||
"ix": 6,
|
||||
"v": { "a": 0, "k": 0, "ix": 6 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Opacity",
|
||||
"mn": "ADBE Fill-0005",
|
||||
"ix": 7,
|
||||
"v": { "a": 0, "k": 1, "ix": 7 }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"t": {
|
||||
"d": {
|
||||
"k": [
|
||||
{
|
||||
"s": {
|
||||
"s": 40,
|
||||
"f": "SegoeUIEmoji",
|
||||
"t": "✨",
|
||||
"j": 0,
|
||||
"tr": 0,
|
||||
"lh": 48,
|
||||
"ls": 0,
|
||||
"fc": [1, 1, 1]
|
||||
},
|
||||
"t": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"p": {},
|
||||
"m": { "g": 1, "a": { "a": 0, "k": [0, 0], "ix": 2 } },
|
||||
"a": []
|
||||
},
|
||||
"ip": 0,
|
||||
"op": 123,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"fonts": {
|
||||
"list": [
|
||||
{
|
||||
"fName": "SegoeUIEmoji",
|
||||
"fFamily": "Segoe UI Emoji",
|
||||
"fStyle": "Regular",
|
||||
"ascent": 74.0234375
|
||||
}
|
||||
]
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 1,
|
||||
"ty": 0,
|
||||
"nm": "botão",
|
||||
"refId": "comp_0",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [155, 154, 0], "ix": 2 },
|
||||
"a": { "a": 0, "k": [960, 540, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6 }
|
||||
},
|
||||
"ao": 0,
|
||||
"ef": [
|
||||
{
|
||||
"ty": 25,
|
||||
"nm": "Drop Shadow",
|
||||
"np": 8,
|
||||
"mn": "ADBE Drop Shadow",
|
||||
"ix": 1,
|
||||
"en": 1,
|
||||
"ef": [
|
||||
{
|
||||
"ty": 2,
|
||||
"nm": "Shadow Color",
|
||||
"mn": "ADBE Drop Shadow-0001",
|
||||
"ix": 1,
|
||||
"v": {
|
||||
"a": 0,
|
||||
"k": [1, 0.829733371735, 0.414901971817, 1],
|
||||
"ix": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Opacity",
|
||||
"mn": "ADBE Drop Shadow-0002",
|
||||
"ix": 2,
|
||||
"v": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": [0], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 0,
|
||||
"s": [127.5]
|
||||
},
|
||||
{
|
||||
"i": { "x": [0], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 15,
|
||||
"s": [204]
|
||||
},
|
||||
{
|
||||
"i": { "x": [0], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 30,
|
||||
"s": [127.5]
|
||||
},
|
||||
{
|
||||
"i": { "x": [0], "y": [1] },
|
||||
"o": { "x": [0.333], "y": [0] },
|
||||
"t": 45,
|
||||
"s": [204]
|
||||
},
|
||||
{ "t": 70, "s": [76.5] }
|
||||
],
|
||||
"ix": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Direction",
|
||||
"mn": "ADBE Drop Shadow-0003",
|
||||
"ix": 3,
|
||||
"v": { "a": 0, "k": 135, "ix": 3 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Distance",
|
||||
"mn": "ADBE Drop Shadow-0004",
|
||||
"ix": 4,
|
||||
"v": { "a": 0, "k": 0, "ix": 4 }
|
||||
},
|
||||
{
|
||||
"ty": 0,
|
||||
"nm": "Softness",
|
||||
"mn": "ADBE Drop Shadow-0005",
|
||||
"ix": 5,
|
||||
"v": { "a": 0, "k": 40, "ix": 5 }
|
||||
},
|
||||
{
|
||||
"ty": 7,
|
||||
"nm": "Shadow Only",
|
||||
"mn": "ADBE Drop Shadow-0006",
|
||||
"ix": 6,
|
||||
"v": { "a": 0, "k": 0, "ix": 6 }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"ip": 0,
|
||||
"op": 120,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
],
|
||||
"markers": [],
|
||||
"chars": [
|
||||
{
|
||||
"ch": "✨",
|
||||
"size": 40,
|
||||
"style": "Regular",
|
||||
"w": 137.3,
|
||||
"data": {
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0.423, 1.042],
|
||||
[0, 0],
|
||||
[0.7, 0],
|
||||
[0.293, -0.618],
|
||||
[0, 0],
|
||||
[1.041, -0.488],
|
||||
[0, 0],
|
||||
[0, -0.684],
|
||||
[-0.652, -0.293],
|
||||
[0, 0],
|
||||
[-0.423, -1.041],
|
||||
[0, 0],
|
||||
[-0.716, 0],
|
||||
[-0.293, 0.619],
|
||||
[0, 0],
|
||||
[-1.042, 0.488],
|
||||
[0, 0],
|
||||
[0, 0.684],
|
||||
[0.618, 0.293],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[-0.326, -0.618],
|
||||
[-0.7, 0],
|
||||
[0, 0],
|
||||
[-0.456, 1.009],
|
||||
[0, 0],
|
||||
[-0.652, 0.293],
|
||||
[0, 0.684],
|
||||
[0, 0],
|
||||
[1.074, 0.456],
|
||||
[0, 0],
|
||||
[0.293, 0.619],
|
||||
[0.716, 0],
|
||||
[0, 0],
|
||||
[0.455, -1.009],
|
||||
[0, 0],
|
||||
[0.618, -0.293],
|
||||
[0, -0.684],
|
||||
[0, 0],
|
||||
[-1.074, -0.455]
|
||||
],
|
||||
"v": [
|
||||
[47.119, -68.994],
|
||||
[43.799, -76.562],
|
||||
[42.261, -77.49],
|
||||
[40.771, -76.562],
|
||||
[37.402, -68.994],
|
||||
[35.156, -66.748],
|
||||
[30.908, -64.893],
|
||||
[29.932, -63.428],
|
||||
[30.908, -61.963],
|
||||
[35.156, -60.107],
|
||||
[37.402, -57.861],
|
||||
[40.771, -50.244],
|
||||
[42.285, -49.316],
|
||||
[43.799, -50.244],
|
||||
[47.119, -57.861],
|
||||
[49.365, -60.107],
|
||||
[53.662, -61.963],
|
||||
[54.59, -63.428],
|
||||
[53.662, -64.893],
|
||||
[49.365, -66.748]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "✨",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ind": 1,
|
||||
"ty": "sh",
|
||||
"ix": 2,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[1.334, 3.223],
|
||||
[0, 0],
|
||||
[1.204, 0.423],
|
||||
[1.204, -0.423],
|
||||
[0.618, -1.237],
|
||||
[0, 0],
|
||||
[3.125, -1.432],
|
||||
[0, 0],
|
||||
[0.423, -1.221],
|
||||
[-0.423, -1.221],
|
||||
[-1.27, -0.618],
|
||||
[0, 0],
|
||||
[-1.335, -3.223],
|
||||
[0, 0],
|
||||
[-1.205, -0.407],
|
||||
[-1.205, 0.407],
|
||||
[-0.619, 1.27],
|
||||
[0, 0],
|
||||
[-3.125, 1.433],
|
||||
[0, 0],
|
||||
[-0.423, 1.221],
|
||||
[0.423, 1.221],
|
||||
[1.27, 0.619],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[-0.619, -1.237],
|
||||
[-1.205, -0.423],
|
||||
[-1.205, 0.423],
|
||||
[0, 0],
|
||||
[-1.367, 3.223],
|
||||
[0, 0],
|
||||
[-1.27, 0.619],
|
||||
[-0.423, 1.221],
|
||||
[0.423, 1.221],
|
||||
[0, 0],
|
||||
[3.157, 1.433],
|
||||
[0, 0],
|
||||
[0.618, 1.27],
|
||||
[1.204, 0.407],
|
||||
[1.204, -0.407],
|
||||
[0, 0],
|
||||
[1.367, -3.223],
|
||||
[0, 0],
|
||||
[1.27, -0.618],
|
||||
[0.423, -1.221],
|
||||
[-0.423, -1.221],
|
||||
[0, 0],
|
||||
[-3.158, -1.432]
|
||||
],
|
||||
"v": [
|
||||
[95.605, -50.83],
|
||||
[85.498, -74.658],
|
||||
[82.764, -77.148],
|
||||
[79.15, -77.148],
|
||||
[76.416, -74.658],
|
||||
[66.357, -50.83],
|
||||
[59.619, -43.848],
|
||||
[46.875, -38.086],
|
||||
[44.336, -35.327],
|
||||
[44.336, -31.665],
|
||||
[46.875, -28.906],
|
||||
[59.619, -23.145],
|
||||
[66.357, -16.162],
|
||||
[76.416, 7.666],
|
||||
[79.15, 10.181],
|
||||
[82.764, 10.181],
|
||||
[85.498, 7.666],
|
||||
[95.605, -16.162],
|
||||
[102.344, -23.145],
|
||||
[115.088, -28.906],
|
||||
[117.627, -31.665],
|
||||
[117.627, -35.327],
|
||||
[115.088, -38.086],
|
||||
[102.344, -43.848]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "✨",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ind": 2,
|
||||
"ty": "sh",
|
||||
"ix": 3,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[-1.367, -0.651],
|
||||
[0, 0],
|
||||
[0, -0.928],
|
||||
[0.813, -0.423],
|
||||
[0, 0],
|
||||
[0.586, -1.399],
|
||||
[0, 0],
|
||||
[0.895, 0],
|
||||
[0.391, 0.846],
|
||||
[0, 0],
|
||||
[1.334, 0.652],
|
||||
[0, 0],
|
||||
[0, 0.928],
|
||||
[-0.814, 0.423],
|
||||
[0, 0],
|
||||
[-0.586, 1.4],
|
||||
[0, 0],
|
||||
[-0.896, 0],
|
||||
[-0.391, -0.846],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[0.813, 0.423],
|
||||
[0, 0.928],
|
||||
[0, 0],
|
||||
[-1.335, 0.652],
|
||||
[0, 0],
|
||||
[-0.391, 0.846],
|
||||
[-0.896, 0],
|
||||
[0, 0],
|
||||
[-0.586, -1.399],
|
||||
[0, 0],
|
||||
[-0.814, -0.423],
|
||||
[0, -0.928],
|
||||
[0, 0],
|
||||
[1.334, -0.651],
|
||||
[0, 0],
|
||||
[0.391, -0.846],
|
||||
[0.895, 0],
|
||||
[0, 0],
|
||||
[0.553, 1.4]
|
||||
],
|
||||
"v": [
|
||||
[44.385, -16.943],
|
||||
[49.854, -14.404],
|
||||
[51.074, -12.378],
|
||||
[49.854, -10.352],
|
||||
[44.385, -7.812],
|
||||
[41.504, -4.736],
|
||||
[37.158, 5.713],
|
||||
[35.229, 6.982],
|
||||
[33.301, 5.713],
|
||||
[28.955, -4.736],
|
||||
[26.074, -7.812],
|
||||
[20.605, -10.352],
|
||||
[19.385, -12.378],
|
||||
[20.605, -14.404],
|
||||
[26.074, -16.943],
|
||||
[28.955, -20.02],
|
||||
[33.301, -30.469],
|
||||
[35.229, -31.738],
|
||||
[37.158, -30.469],
|
||||
[41.504, -20.02]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "✨",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"nm": "✨",
|
||||
"np": 6,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"fFamily": "Segoe UI Emoji"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
import { vars } from "../../theme.css";
|
||||
|
||||
export const profileAvatar = style({
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: vars.color.background,
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
cursor: "pointer",
|
||||
color: vars.color.muted,
|
||||
position: "relative",
|
||||
});
|
||||
|
||||
export const profileAvatarImage = style({
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
objectFit: "cover",
|
||||
overflow: "hidden",
|
||||
borderRadius: "4px",
|
||||
});
|
||||
21
src/renderer/src/components/avatar/avatar.scss
Normal file
21
src/renderer/src/components/avatar/avatar.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
@use "../../scss/globals.scss";
|
||||
|
||||
.profile-avatar {
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: globals.$background-color;
|
||||
border: solid 1px globals.$border-color;
|
||||
cursor: pointer;
|
||||
color: globals.$muted-color;
|
||||
position: relative;
|
||||
|
||||
&__image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PersonIcon } from "@primer/octicons-react";
|
||||
|
||||
import * as styles from "./avatar.css";
|
||||
import "./avatar.scss";
|
||||
|
||||
export interface AvatarProps
|
||||
extends Omit<
|
||||
@@ -16,14 +16,9 @@ export interface AvatarProps
|
||||
|
||||
export function Avatar({ size, alt, src, ...props }: AvatarProps) {
|
||||
return (
|
||||
<div className={styles.profileAvatar} style={{ width: size, height: size }}>
|
||||
<div className="profile-avatar" style={{ width: size, height: size }}>
|
||||
{src ? (
|
||||
<img
|
||||
className={styles.profileAvatarImage}
|
||||
alt={alt}
|
||||
src={src}
|
||||
{...props}
|
||||
/>
|
||||
<img className="profile-avatar__image" alt={alt} src={src} {...props} />
|
||||
) : (
|
||||
<PersonIcon size={size * 0.7} />
|
||||
)}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
import { SPACING_UNIT } from "../../theme.css";
|
||||
|
||||
export const badge = style({
|
||||
color: "#c0c1c7",
|
||||
fontSize: "10px",
|
||||
padding: `${SPACING_UNIT / 2}px ${SPACING_UNIT}px`,
|
||||
border: "solid 1px #c0c1c7",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
});
|
||||
11
src/renderer/src/components/badge/badge.scss
Normal file
11
src/renderer/src/components/badge/badge.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
@use "../../scss/globals.scss";
|
||||
|
||||
.badge {
|
||||
color: globals.$muted-color;
|
||||
font-size: 10px;
|
||||
padding: calc(globals.$spacing-unit / 2) globals.$spacing-unit;
|
||||
border: solid 1px globals.$muted-color;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import * as styles from "./badge.css";
|
||||
|
||||
import "./badge.scss";
|
||||
|
||||
export interface BadgeProps {
|
||||
children: React.ReactNode;
|
||||
@@ -7,7 +8,7 @@ export interface BadgeProps {
|
||||
|
||||
export function Badge({ children }: BadgeProps) {
|
||||
return (
|
||||
<div className={styles.badge}>
|
||||
<div className="badge">
|
||||
<span>{children}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
|
||||
export const bottomPanel = style({
|
||||
width: "100%",
|
||||
borderTop: `solid 1px ${vars.color.border}`,
|
||||
backgroundColor: vars.color.background,
|
||||
padding: `${SPACING_UNIT / 2}px ${SPACING_UNIT * 2}px`,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
transition: "all ease 0.2s",
|
||||
justifyContent: "space-between",
|
||||
position: "relative",
|
||||
zIndex: vars.zIndex.bottomPanel,
|
||||
});
|
||||
|
||||
export const downloadsButton = style({
|
||||
color: vars.color.body,
|
||||
borderBottom: "1px solid transparent",
|
||||
":hover": {
|
||||
borderBottom: `1px solid ${vars.color.body}`,
|
||||
cursor: "pointer",
|
||||
},
|
||||
});
|
||||
24
src/renderer/src/components/bottom-panel/bottom-panel.scss
Normal file
24
src/renderer/src/components/bottom-panel/bottom-panel.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
@use "../../scss/globals.scss";
|
||||
|
||||
.bottom-panel {
|
||||
width: 100%;
|
||||
border-top: solid 1px globals.$border-color;
|
||||
background-color: globals.$background-color;
|
||||
padding: calc(globals.$spacing-unit / 2) calc(globals.$spacing-unit * 2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all ease 0.2s;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
z-index: globals.$bottom-panel-z-index;
|
||||
|
||||
&__downloads-button {
|
||||
color: globals.$body-color;
|
||||
border-bottom: solid 1px transparent;
|
||||
|
||||
&:hover {
|
||||
border-bottom: solid 1px globals.$body-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useDownload, useUserDetails } from "@renderer/hooks";
|
||||
|
||||
import * as styles from "./bottom-panel.css";
|
||||
import "./bottom-panel.scss";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { VERSION_CODENAME } from "@renderer/constants";
|
||||
@@ -72,10 +72,10 @@ export function BottomPanel() {
|
||||
]);
|
||||
|
||||
return (
|
||||
<footer className={styles.bottomPanel}>
|
||||
<footer className="bottom-panel">
|
||||
<button
|
||||
type="button"
|
||||
className={styles.downloadsButton}
|
||||
className="bottom-panel__downloads-button"
|
||||
onClick={() => navigate("/downloads")}
|
||||
>
|
||||
<small>{status}</small>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { style, styleVariants } from "@vanilla-extract/css";
|
||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
|
||||
const base = style({
|
||||
padding: `${SPACING_UNIT}px ${SPACING_UNIT * 2}px`,
|
||||
backgroundColor: vars.color.muted,
|
||||
borderRadius: "8px",
|
||||
border: "solid 1px transparent",
|
||||
transition: "all ease 0.2s",
|
||||
cursor: "pointer",
|
||||
minHeight: "40px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
":active": {
|
||||
opacity: vars.opacity.active,
|
||||
},
|
||||
":disabled": {
|
||||
opacity: vars.opacity.disabled,
|
||||
cursor: "not-allowed",
|
||||
},
|
||||
});
|
||||
|
||||
export const button = styleVariants({
|
||||
primary: [
|
||||
base,
|
||||
{
|
||||
":hover": {
|
||||
backgroundColor: "#DADBE1",
|
||||
},
|
||||
":disabled": {
|
||||
backgroundColor: vars.color.muted,
|
||||
},
|
||||
},
|
||||
],
|
||||
outline: [
|
||||
base,
|
||||
{
|
||||
backgroundColor: "transparent",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
color: vars.color.muted,
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
},
|
||||
":disabled": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
],
|
||||
dark: [
|
||||
base,
|
||||
{
|
||||
backgroundColor: vars.color.darkBackground,
|
||||
color: "#c0c1c7",
|
||||
},
|
||||
],
|
||||
danger: [
|
||||
base,
|
||||
{
|
||||
borderColor: "transparent",
|
||||
backgroundColor: "#a31533",
|
||||
color: "#c0c1c7",
|
||||
":hover": {
|
||||
backgroundColor: "#b3203f",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
63
src/renderer/src/components/button/button.scss
Normal file
63
src/renderer/src/components/button/button.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
@use "../../scss/globals.scss";
|
||||
|
||||
.button {
|
||||
padding: globals.$spacing-unit globals.$spacing-unit * 2;
|
||||
background-color: globals.$muted-color;
|
||||
border-radius: 8px;
|
||||
border: solid 1px transparent;
|
||||
transition: all ease 0.2s;
|
||||
cursor: pointer;
|
||||
min-height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: globals.$spacing-unit;
|
||||
|
||||
&:active {
|
||||
opacity: globals.$active-opacity;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: globals.$disabled-opacity;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
&:hover {
|
||||
background-color: #dadbe1;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: globals.$muted-color;
|
||||
}
|
||||
}
|
||||
|
||||
&--outline {
|
||||
background-color: transparent;
|
||||
border: solid 1px globals.$border-color;
|
||||
color: globals.$muted-color;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&--dark {
|
||||
background-color: globals.$dark-background-color;
|
||||
color: globals.$muted-color;
|
||||
}
|
||||
|
||||
&--danger {
|
||||
border-color: transparent;
|
||||
background-color: globals.$danger-color;
|
||||
color: globals.$muted-color;
|
||||
|
||||
&:hover {
|
||||
background-color: #b3203f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import cn from "classnames";
|
||||
import * as styles from "./button.css";
|
||||
|
||||
import "./button.scss";
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.DetailedHTMLProps<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
HTMLButtonElement
|
||||
> {
|
||||
theme?: keyof typeof styles.button;
|
||||
theme?: "primary" | "outline" | "dark" | "danger";
|
||||
}
|
||||
|
||||
export function Button({
|
||||
@@ -18,7 +19,7 @@ export function Button({
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(styles.button[theme], className)}
|
||||
className={cn("button", `button--${theme}`, className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -107,6 +107,7 @@ export function Modal({
|
||||
aria-labelledby={title}
|
||||
aria-describedby={description}
|
||||
ref={modalContentRef}
|
||||
data-hydra-dialog
|
||||
>
|
||||
<div className={styles.modalHeader}>
|
||||
<div style={{ display: "flex", gap: 4, flexDirection: "column" }}>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import Lottie from "lottie-react";
|
||||
|
||||
import downloadingAnimation from "@renderer/assets/lottie/downloading.json";
|
||||
|
||||
export interface DownloadIconProps {
|
||||
isDownloading: boolean;
|
||||
}
|
||||
|
||||
export function DownloadIcon({ isDownloading }: DownloadIconProps) {
|
||||
return (
|
||||
<Lottie
|
||||
animationData={downloadingAnimation}
|
||||
loop={isDownloading}
|
||||
autoplay={isDownloading}
|
||||
style={{ width: 16 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { AppsIcon, GearIcon, HomeIcon } from "@primer/octicons-react";
|
||||
|
||||
import { DownloadIcon } from "./download-icon";
|
||||
import {
|
||||
AppsIcon,
|
||||
DownloadIcon,
|
||||
GearIcon,
|
||||
HomeIcon,
|
||||
} from "@primer/octicons-react";
|
||||
|
||||
export const routes = [
|
||||
{
|
||||
@@ -16,9 +19,7 @@ export const routes = [
|
||||
{
|
||||
path: "/downloads",
|
||||
nameKey: "downloads",
|
||||
render: (isDownloading: boolean) => (
|
||||
<DownloadIcon isDownloading={isDownloading} />
|
||||
),
|
||||
render: () => <DownloadIcon />,
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
|
||||
@@ -13,6 +13,7 @@ export const sidebar = recipe({
|
||||
borderRight: `solid 1px ${vars.color.border}`,
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
variants: {
|
||||
resizing: {
|
||||
@@ -124,3 +125,28 @@ export const section = style({
|
||||
flexDirection: "column",
|
||||
paddingBottom: `${SPACING_UNIT}px`,
|
||||
});
|
||||
|
||||
export const helpButton = style({
|
||||
color: vars.color.muted,
|
||||
padding: `${SPACING_UNIT}px ${SPACING_UNIT * 2}px`,
|
||||
gap: "9px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
borderTop: `solid 1px ${vars.color.border}`,
|
||||
transition: "background-color ease 0.1s",
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
},
|
||||
});
|
||||
|
||||
export const helpButtonIcon = style({
|
||||
background: "linear-gradient(0deg, #16B195 50%, #3E62C0 100%)",
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "#fff",
|
||||
borderRadius: "50%",
|
||||
});
|
||||
|
||||
@@ -5,7 +5,12 @@ import { useLocation, useNavigate } from "react-router-dom";
|
||||
import type { LibraryGame } from "@types";
|
||||
|
||||
import { TextField } from "@renderer/components";
|
||||
import { useDownload, useLibrary, useToast } from "@renderer/hooks";
|
||||
import {
|
||||
useDownload,
|
||||
useLibrary,
|
||||
useToast,
|
||||
useUserDetails,
|
||||
} from "@renderer/hooks";
|
||||
|
||||
import { routes } from "./routes";
|
||||
|
||||
@@ -15,6 +20,7 @@ import { buildGameDetailsPath } from "@renderer/helpers";
|
||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||
import { SidebarProfile } from "./sidebar-profile";
|
||||
import { sortBy } from "lodash-es";
|
||||
import { CommentDiscussionIcon } from "@primer/octicons-react";
|
||||
|
||||
const SIDEBAR_MIN_WIDTH = 200;
|
||||
const SIDEBAR_INITIAL_WIDTH = 250;
|
||||
@@ -42,6 +48,8 @@ export function Sidebar() {
|
||||
return sortBy(library, (game) => game.title);
|
||||
}, [library]);
|
||||
|
||||
const { hasActiveSubscription } = useUserDetails();
|
||||
|
||||
const { lastPacket, progress } = useDownload();
|
||||
|
||||
const { showWarningToast } = useToast();
|
||||
@@ -50,10 +58,6 @@ export function Sidebar() {
|
||||
updateLibrary();
|
||||
}, [lastPacket?.game.id, updateLibrary]);
|
||||
|
||||
const isDownloading = sortedLibrary.some(
|
||||
(game) => game.status === "active" && game.progress !== 1
|
||||
);
|
||||
|
||||
const sidebarRef = useRef<HTMLElement>(null);
|
||||
|
||||
const cursorPos = useRef({ x: 0 });
|
||||
@@ -170,77 +174,95 @@ export function Sidebar() {
|
||||
maxWidth: sidebarWidth,
|
||||
}}
|
||||
>
|
||||
<SidebarProfile />
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", overflow: "hidden" }}
|
||||
>
|
||||
<SidebarProfile />
|
||||
|
||||
<div className={styles.content}>
|
||||
<section className={styles.section}>
|
||||
<ul className={styles.menu}>
|
||||
{routes.map(({ nameKey, path, render }) => (
|
||||
<li
|
||||
key={nameKey}
|
||||
className={styles.menuItem({
|
||||
active: location.pathname === path,
|
||||
})}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.menuItemButton}
|
||||
onClick={() => handleSidebarItemClick(path)}
|
||||
<div className={styles.content}>
|
||||
<section className={styles.section}>
|
||||
<ul className={styles.menu}>
|
||||
{routes.map(({ nameKey, path, render }) => (
|
||||
<li
|
||||
key={nameKey}
|
||||
className={styles.menuItem({
|
||||
active: location.pathname === path,
|
||||
})}
|
||||
>
|
||||
{render(isDownloading)}
|
||||
<span>{t(nameKey)}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.menuItemButton}
|
||||
onClick={() => handleSidebarItemClick(path)}
|
||||
>
|
||||
{render()}
|
||||
<span>{t(nameKey)}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<small className={styles.sectionTitle}>{t("my_library")}</small>
|
||||
<section className={styles.section}>
|
||||
<small className={styles.sectionTitle}>{t("my_library")}</small>
|
||||
|
||||
<TextField
|
||||
ref={filterRef}
|
||||
placeholder={t("filter")}
|
||||
onChange={handleFilter}
|
||||
theme="dark"
|
||||
/>
|
||||
<TextField
|
||||
ref={filterRef}
|
||||
placeholder={t("filter")}
|
||||
onChange={handleFilter}
|
||||
theme="dark"
|
||||
/>
|
||||
|
||||
<ul className={styles.menu}>
|
||||
{filteredLibrary.map((game) => (
|
||||
<li
|
||||
key={game.id}
|
||||
className={styles.menuItem({
|
||||
active:
|
||||
location.pathname === `/game/${game.shop}/${game.objectID}`,
|
||||
muted: game.status === "removed",
|
||||
})}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.menuItemButton}
|
||||
onClick={(event) => handleSidebarGameClick(event, game)}
|
||||
<ul className={styles.menu}>
|
||||
{filteredLibrary.map((game) => (
|
||||
<li
|
||||
key={game.id}
|
||||
className={styles.menuItem({
|
||||
active:
|
||||
location.pathname ===
|
||||
`/game/${game.shop}/${game.objectID}`,
|
||||
muted: game.status === "removed",
|
||||
})}
|
||||
>
|
||||
{game.iconUrl ? (
|
||||
<img
|
||||
className={styles.gameIcon}
|
||||
src={game.iconUrl}
|
||||
alt={game.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<SteamLogo className={styles.gameIcon} />
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className={styles.menuItemButton}
|
||||
onClick={(event) => handleSidebarGameClick(event, game)}
|
||||
>
|
||||
{game.iconUrl ? (
|
||||
<img
|
||||
className={styles.gameIcon}
|
||||
src={game.iconUrl}
|
||||
alt={game.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<SteamLogo className={styles.gameIcon} />
|
||||
)}
|
||||
|
||||
<span className={styles.menuItemButtonLabel}>
|
||||
{getGameTitle(game)}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
<span className={styles.menuItemButtonLabel}>
|
||||
{getGameTitle(game)}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasActiveSubscription && (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.helpButton}
|
||||
data-open-support-chat
|
||||
>
|
||||
<div className={styles.helpButtonIcon}>
|
||||
<CommentDiscussionIcon size={14} />
|
||||
</div>
|
||||
<span>{t("need_help")}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={styles.handle}
|
||||
|
||||
@@ -147,7 +147,8 @@ export function GameDetailsContextProvider({
|
||||
if (
|
||||
result?.content_descriptors.ids.includes(
|
||||
SteamContentDescriptor.AdultOnlySexualContent
|
||||
)
|
||||
) &&
|
||||
!userPreferences?.disableNsfwAlert
|
||||
) {
|
||||
setHasNSFWContentBlocked(true);
|
||||
}
|
||||
@@ -180,6 +181,7 @@ export function GameDetailsContextProvider({
|
||||
shop,
|
||||
i18n.language,
|
||||
userDetails,
|
||||
userPreferences,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
46
src/renderer/src/cookies.ts
Normal file
46
src/renderer/src/cookies.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export function addCookieInterceptor(isStaging: boolean) {
|
||||
const cookieKey = isStaging ? "cookies-staging" : "cookies";
|
||||
|
||||
Object.defineProperty(document, "cookie", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return localStorage.getItem(cookieKey) || "";
|
||||
},
|
||||
set(cookieString) {
|
||||
try {
|
||||
const [cookieName, cookieValue] = cookieString.split(";")[0].split("=");
|
||||
|
||||
const currentCookies = localStorage.getItem(cookieKey) || "";
|
||||
|
||||
const cookiesObject = parseCookieStringsToObjects(currentCookies);
|
||||
cookiesObject[cookieName] = cookieValue;
|
||||
|
||||
const newString = Object.entries(cookiesObject)
|
||||
.map(([key, value]) => {
|
||||
return key + "=" + value;
|
||||
})
|
||||
.join("; ");
|
||||
|
||||
localStorage.setItem(cookieKey, newString);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const parseCookieStringsToObjects = (
|
||||
cookieStrings: string
|
||||
): { [key: string]: string } => {
|
||||
const result = {};
|
||||
|
||||
if (cookieStrings === "") return result;
|
||||
|
||||
cookieStrings.split(";").forEach((cookieString) => {
|
||||
const [name, value] = cookieString.split("=");
|
||||
result[name.trim()] = value.trim();
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
29
src/renderer/src/declaration.d.ts
vendored
29
src/renderer/src/declaration.d.ts
vendored
@@ -60,22 +60,13 @@ declare global {
|
||||
) => Promise<ShopDetails | null>;
|
||||
getRandomGame: () => Promise<Steam250Game>;
|
||||
getHowLongToBeat: (
|
||||
title: string
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => Promise<HowLongToBeatCategory[] | null>;
|
||||
getGames: (take?: number, skip?: number) => Promise<CatalogueEntry[]>;
|
||||
searchGameRepacks: (query: string) => Promise<GameRepack[]>;
|
||||
getGameStats: (objectId: string, shop: GameShop) => Promise<GameStats>;
|
||||
getTrendingGames: () => Promise<TrendingGame[]>;
|
||||
onAchievementUnlocked: (
|
||||
cb: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
achievements?: { displayName: string; iconUrl: string }[]
|
||||
) => void
|
||||
) => () => Electron.IpcRenderer;
|
||||
onCombinedAchievementsUnlocked: (
|
||||
cb: (gameCount: number, achievementCount: number) => void
|
||||
) => () => Electron.IpcRenderer;
|
||||
onUpdateAchievements: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
@@ -89,8 +80,14 @@ declare global {
|
||||
shop: GameShop
|
||||
) => Promise<void>;
|
||||
createGameShortcut: (id: number) => Promise<boolean>;
|
||||
updateExecutablePath: (id: number, executablePath: string) => Promise<void>;
|
||||
selectGameWinePrefix: (id: number, winePrefixPath: string) => Promise<void>;
|
||||
updateExecutablePath: (
|
||||
id: number,
|
||||
executablePath: string | null
|
||||
) => Promise<void>;
|
||||
selectGameWinePrefix: (
|
||||
id: number,
|
||||
winePrefixPath: string | null
|
||||
) => Promise<void>;
|
||||
verifyExecutablePathInUse: (executablePath: string) => Promise<Game>;
|
||||
getLibrary: () => Promise<LibraryGame[]>;
|
||||
openGameInstaller: (gameId: number) => Promise<boolean>;
|
||||
@@ -114,7 +111,10 @@ declare global {
|
||||
updateUserPreferences: (
|
||||
preferences: Partial<UserPreferences>
|
||||
) => Promise<void>;
|
||||
autoLaunch: (enabled: boolean) => Promise<void>;
|
||||
autoLaunch: (autoLaunchProps: {
|
||||
enabled: boolean;
|
||||
minimized: boolean;
|
||||
}) => Promise<void>;
|
||||
authenticateRealDebrid: (apiToken: string) => Promise<RealDebridUser>;
|
||||
|
||||
/* Download sources */
|
||||
@@ -169,6 +169,7 @@ declare global {
|
||||
openExternal: (src: string) => Promise<void>;
|
||||
openCheckout: () => Promise<void>;
|
||||
getVersion: () => Promise<string>;
|
||||
isStaging: () => Promise<boolean>;
|
||||
ping: () => string;
|
||||
getDefaultDownloadsPath: () => Promise<string>;
|
||||
isPortableVersion: () => Promise<boolean>;
|
||||
|
||||
@@ -10,12 +10,22 @@ export interface HowLongToBeatEntry {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface CatalogueCache {
|
||||
id?: number;
|
||||
category: string;
|
||||
games: { objectId: string; shop: GameShop }[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
export const db = new Dexie("Hydra");
|
||||
|
||||
db.version(4).stores({
|
||||
db.version(5).stores({
|
||||
repacks: `++id, title, uris, fileSize, uploadDate, downloadSourceId, repacker, createdAt, updatedAt`,
|
||||
downloadSources: `++id, url, name, etag, downloadCount, status, createdAt, updatedAt`,
|
||||
howLongToBeatEntries: `++id, categories, [shop+objectId], createdAt, updatedAt`,
|
||||
catalogueCache: `++id, category, games, createdAt, updatedAt, expiresAt`,
|
||||
});
|
||||
|
||||
export const downloadSourcesTable = db.table("downloadSources");
|
||||
@@ -24,4 +34,6 @@ export const howLongToBeatEntriesTable = db.table<HowLongToBeatEntry>(
|
||||
"howLongToBeatEntries"
|
||||
);
|
||||
|
||||
export const catalogueCacheTable = db.table<CatalogueCache>("catalogueCache");
|
||||
|
||||
db.open();
|
||||
|
||||
@@ -77,6 +77,8 @@ export function useDate() {
|
||||
},
|
||||
|
||||
formatDate: (date: number | Date | string): string => {
|
||||
if (isNaN(new Date(date).getDate())) return "N/A";
|
||||
|
||||
const locale = getDateLocale();
|
||||
return format(date, locale == enUS ? "MM/dd/yyyy" : "dd/MM/yyyy");
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
UserDetails,
|
||||
} from "@types";
|
||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||
import { isFuture, isToday } from "date-fns";
|
||||
|
||||
export function useUserDetails() {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -55,6 +56,8 @@ export function useUserDetails() {
|
||||
clearUserDetails();
|
||||
}
|
||||
|
||||
window["userDetails"] = userDetails;
|
||||
|
||||
return userDetails;
|
||||
});
|
||||
}, [clearUserDetails]);
|
||||
@@ -128,10 +131,8 @@ export function useUserDetails() {
|
||||
const unblockUser = (userId: string) => window.electron.unblockUser(userId);
|
||||
|
||||
const hasActiveSubscription = useMemo(() => {
|
||||
return (
|
||||
userDetails?.subscription?.expiresAt &&
|
||||
new Date(userDetails.subscription.expiresAt) > new Date()
|
||||
);
|
||||
const expiresAt = userDetails?.subscription?.expiresAt;
|
||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
||||
}, [userDetails]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -6,8 +6,6 @@ import { Provider } from "react-redux";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import { HashRouter, Route, Routes } from "react-router-dom";
|
||||
|
||||
import * as Sentry from "@sentry/electron/renderer";
|
||||
|
||||
import "@fontsource/noto-sans/400.css";
|
||||
import "@fontsource/noto-sans/500.css";
|
||||
import "@fontsource/noto-sans/700.css";
|
||||
@@ -19,11 +17,11 @@ import { App } from "./app";
|
||||
import { store } from "./store";
|
||||
|
||||
import resources from "@locales";
|
||||
import { AchievementNotification } from "./pages/achievements/notification/achievement-notification";
|
||||
|
||||
import "./workers";
|
||||
import { RepacksContextProvider } from "./context";
|
||||
import { SuspenseWrapper } from "./components";
|
||||
import { logger } from "./logger";
|
||||
import { addCookieInterceptor } from "./cookies";
|
||||
|
||||
const Home = React.lazy(() => import("./pages/home/home"));
|
||||
const GameDetails = React.lazy(
|
||||
@@ -38,7 +36,10 @@ const Achievements = React.lazy(
|
||||
() => import("./pages/achievements/achievements")
|
||||
);
|
||||
|
||||
Sentry.init({});
|
||||
console.log = logger.log;
|
||||
|
||||
const isStaging = await window.electron.isStaging();
|
||||
addCookieInterceptor(isStaging);
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
@@ -97,10 +98,6 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
element={<SuspenseWrapper Component={Achievements} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="/achievement-notification"
|
||||
Component={AchievementNotification}
|
||||
/>
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
</RepacksContextProvider>
|
||||
|
||||
@@ -273,7 +273,6 @@ export function AchievementsContent({
|
||||
src={steamUrlBuilder.libraryHero(objectId)}
|
||||
style={{ display: "none" }}
|
||||
alt={gameTitle}
|
||||
className={styles.heroImage}
|
||||
onLoad={handleHeroLoad}
|
||||
/>
|
||||
|
||||
|
||||
@@ -23,31 +23,6 @@ export const hero = style({
|
||||
flexDirection: "column",
|
||||
position: "relative",
|
||||
transition: "all ease 0.2s",
|
||||
"@media": {
|
||||
"(min-width: 1250px)": {
|
||||
height: "350px",
|
||||
minHeight: "350px",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const heroImage = style({
|
||||
width: "100%",
|
||||
height: `${HERO_HEIGHT}px`,
|
||||
minHeight: `${HERO_HEIGHT}px`,
|
||||
objectFit: "cover",
|
||||
objectPosition: "top",
|
||||
transition: "all ease 0.2s",
|
||||
position: "absolute",
|
||||
zIndex: "0",
|
||||
filter: "blur(5px)",
|
||||
"@media": {
|
||||
"(min-width: 1250px)": {
|
||||
objectPosition: "center",
|
||||
height: "350px",
|
||||
minHeight: "350px",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const heroContent = style({
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { recipe } from "@vanilla-extract/recipes";
|
||||
import { vars } from "../../../theme.css";
|
||||
import { keyframes, style } from "@vanilla-extract/css";
|
||||
|
||||
const animationIn = keyframes({
|
||||
"0%": { transform: `translateY(-240px)` },
|
||||
"100%": { transform: "translateY(0)" },
|
||||
});
|
||||
|
||||
const animationOut = keyframes({
|
||||
"0%": { transform: `translateY(0)` },
|
||||
"100%": { transform: "translateY(-240px)" },
|
||||
});
|
||||
|
||||
export const container = recipe({
|
||||
base: {
|
||||
marginTop: "24px",
|
||||
marginLeft: "24px",
|
||||
animationDuration: "1.0s",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
},
|
||||
variants: {
|
||||
closing: {
|
||||
true: {
|
||||
animationName: animationOut,
|
||||
transform: "translateY(-240px)",
|
||||
},
|
||||
false: {
|
||||
animationName: animationIn,
|
||||
transform: "translateY(0)",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const content = style({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "8px",
|
||||
alignItems: "center",
|
||||
background: vars.color.background,
|
||||
paddingRight: "8px",
|
||||
});
|
||||
@@ -1,141 +0,0 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import achievementSound from "@renderer/assets/audio/achievement.wav";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import * as styles from "./achievement-notification.css";
|
||||
|
||||
interface AchievementInfo {
|
||||
displayName: string;
|
||||
iconUrl: string;
|
||||
}
|
||||
|
||||
const NOTIFICATION_TIMEOUT = 4000;
|
||||
|
||||
export function AchievementNotification() {
|
||||
const { t } = useTranslation("achievement");
|
||||
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const [achievements, setAchievements] = useState<AchievementInfo[]>([]);
|
||||
const [currentAchievement, setCurrentAchievement] =
|
||||
useState<AchievementInfo | null>(null);
|
||||
|
||||
const achievementAnimation = useRef(-1);
|
||||
const closingAnimation = useRef(-1);
|
||||
const visibleAnimation = useRef(-1);
|
||||
|
||||
const playAudio = useCallback(() => {
|
||||
const audio = new Audio(achievementSound);
|
||||
audio.volume = 0.2;
|
||||
audio.play();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onCombinedAchievementsUnlocked(
|
||||
(gameCount, achievementCount) => {
|
||||
if (gameCount === 0 || achievementCount === 0) return;
|
||||
|
||||
setAchievements([
|
||||
{
|
||||
displayName: t("new_achievements_unlocked", {
|
||||
gameCount,
|
||||
achievementCount,
|
||||
}),
|
||||
iconUrl:
|
||||
"https://avatars.githubusercontent.com/u/164102380?s=400&u=01a13a7b4f0c642f7e547b8e1d70440ea06fa750&v=4",
|
||||
},
|
||||
]);
|
||||
|
||||
playAudio();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [playAudio]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onAchievementUnlocked(
|
||||
(_object, _shop, achievements) => {
|
||||
if (!achievements || !achievements.length) return;
|
||||
|
||||
setAchievements((ach) => ach.concat(achievements));
|
||||
|
||||
playAudio();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [playAudio]);
|
||||
|
||||
const hasAchievementsPending = achievements.length > 0;
|
||||
|
||||
const startAnimateClosing = useCallback(() => {
|
||||
cancelAnimationFrame(closingAnimation.current);
|
||||
cancelAnimationFrame(visibleAnimation.current);
|
||||
cancelAnimationFrame(achievementAnimation.current);
|
||||
|
||||
setIsClosing(true);
|
||||
|
||||
const zero = performance.now();
|
||||
closingAnimation.current = requestAnimationFrame(
|
||||
function animateClosing(time) {
|
||||
if (time - zero <= 1000) {
|
||||
closingAnimation.current = requestAnimationFrame(animateClosing);
|
||||
} else {
|
||||
setIsVisible(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAchievementsPending) {
|
||||
setIsClosing(false);
|
||||
setIsVisible(true);
|
||||
|
||||
let zero = performance.now();
|
||||
cancelAnimationFrame(closingAnimation.current);
|
||||
cancelAnimationFrame(visibleAnimation.current);
|
||||
cancelAnimationFrame(achievementAnimation.current);
|
||||
achievementAnimation.current = requestAnimationFrame(
|
||||
function animateLock(time) {
|
||||
if (time - zero > NOTIFICATION_TIMEOUT) {
|
||||
zero = performance.now();
|
||||
setAchievements((ach) => ach.slice(1));
|
||||
}
|
||||
achievementAnimation.current = requestAnimationFrame(animateLock);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
startAnimateClosing();
|
||||
}
|
||||
}, [hasAchievementsPending]);
|
||||
|
||||
useEffect(() => {
|
||||
if (achievements.length) {
|
||||
setCurrentAchievement(achievements[0]);
|
||||
}
|
||||
}, [achievements]);
|
||||
|
||||
if (!isVisible || !currentAchievement) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.container({ closing: isClosing })}>
|
||||
<div className={styles.content}>
|
||||
<img
|
||||
src={currentAchievement.iconUrl}
|
||||
alt={currentAchievement.displayName}
|
||||
style={{ flex: 1, width: "60px" }}
|
||||
/>
|
||||
<div>
|
||||
<p>{t("achievement_unlocked")}</p>
|
||||
<p>{currentAchievement.displayName}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user