mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-12 15:14:35 +01:00
Run prettier formatter
This commit is contained in:
@@ -1,48 +1,40 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es2021": true,
|
"es2021": true,
|
||||||
"node": true
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"ecmaVersion": "latest",
|
||||||
"eslint:recommended",
|
"sourceType": "module"
|
||||||
"plugin:react/recommended",
|
},
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugins": ["react", "@typescript-eslint"],
|
||||||
"prettier"
|
"rules": {
|
||||||
],
|
"@typescript-eslint/ban-types": [
|
||||||
"parser": "@typescript-eslint/parser",
|
"warn",
|
||||||
"parserOptions": {
|
{
|
||||||
"ecmaFeatures": {
|
"extendDefaults": true,
|
||||||
"jsx": true
|
"types": {
|
||||||
},
|
"{}": false
|
||||||
"ecmaVersion": "latest",
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"react",
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/ban-types": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"extendDefaults": true,
|
|
||||||
"types": {
|
|
||||||
"{}": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"varsIgnorePattern": "^_"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "detect"
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"varsIgnorePattern": "^_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
.github/workflows/backend-checks.yml
vendored
9
.github/workflows/backend-checks.yml
vendored
@@ -3,12 +3,12 @@ name: Check backend
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/backend-checks.yml"
|
- '.github/workflows/backend-checks.yml'
|
||||||
- "src-tauri/**"
|
- 'src-tauri/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/backend-checks.yml"
|
- '.github/workflows/backend-checks.yml'
|
||||||
- "src-tauri/**"
|
- 'src-tauri/**'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.ref }}-${{ github.workflow }}
|
group: ${{ github.ref }}-${{ github.workflow }}
|
||||||
@@ -59,4 +59,3 @@ jobs:
|
|||||||
name: clippy (${{ runner.os }})
|
name: clippy (${{ runner.os }})
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
args: --manifest-path ./src-tauri/Cargo.toml --no-default-features -- -D warnings
|
args: --manifest-path ./src-tauri/Cargo.toml --no-default-features -- -D warnings
|
||||||
|
|
||||||
|
|||||||
25
.github/workflows/frontend-checks.yml
vendored
25
.github/workflows/frontend-checks.yml
vendored
@@ -3,20 +3,20 @@ name: Check frontend
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/frontend-checks.yml"
|
- '.github/workflows/frontend-checks.yml'
|
||||||
- "src/**"
|
- 'src/**'
|
||||||
- ".eslintrc.json"
|
- '.eslintrc.json'
|
||||||
- "package.json"
|
- 'package.json'
|
||||||
- "tsconfig.json"
|
- 'tsconfig.json'
|
||||||
- "yarn.lock"
|
- 'yarn.lock'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/frontend-checks.yml"
|
- '.github/workflows/frontend-checks.yml'
|
||||||
- "src/**"
|
- 'src/**'
|
||||||
- ".eslintrc.json"
|
- '.eslintrc.json'
|
||||||
- "package.json"
|
- 'package.json'
|
||||||
- "tsconfig.json"
|
- 'tsconfig.json'
|
||||||
- "yarn.lock"
|
- 'yarn.lock'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.ref }}-${{ github.workflow }}
|
group: ${{ github.ref }}-${{ github.workflow }}
|
||||||
@@ -34,4 +34,3 @@ jobs:
|
|||||||
run: yarn tsc --noEmit
|
run: yarn tsc --noEmit
|
||||||
- name: Run ESLint
|
- name: Run ESLint
|
||||||
run: yarn eslint src
|
run: yarn eslint src
|
||||||
|
|
||||||
|
|||||||
55
README.md
55
README.md
@@ -1,25 +1,28 @@
|
|||||||
# Client Patching Notice
|
# Client Patching Notice
|
||||||
|
|
||||||
For game versions 2.8 and above, Cultivation automatically makes a small patch to your game client when launching using Grasscutter, and restores it upon closing the game. In theory, you should still be totally safe, however it would be dishonest to not explicitly state that **modifying the game client could, theoretically, lead to a ban if you connect to official servers with it**. It is extremely unlikely AND there are no instances known of it happening, but the possibility exists.
|
For game versions 2.8 and above, Cultivation automatically makes a small patch to your game client when launching using Grasscutter, and restores it upon closing the game. In theory, you should still be totally safe, however it would be dishonest to not explicitly state that **modifying the game client could, theoretically, lead to a ban if you connect to official servers with it**. It is extremely unlikely AND there are no instances known of it happening, but the possibility exists.
|
||||||
|
|
||||||
# Cultivation
|
# Cultivation
|
||||||
|
|
||||||
A game launcher designed to easily proxy traffic from anime game to private servers.
|
A game launcher designed to easily proxy traffic from anime game to private servers.
|
||||||
|
|
||||||
While the Cultivation repository is **open**. This does **not** mean it has released.
|
While the Cultivation repository is **open**. This does **not** mean it has released.
|
||||||
Please do **NOT install, download, or use pre-compiled versions of Cultivation found elsewhere**. Only use releases from this GitHub repository.
|
Please do **NOT install, download, or use pre-compiled versions of Cultivation found elsewhere**. Only use releases from this GitHub repository.
|
||||||
|
|
||||||
# Table Of Contents
|
# Table Of Contents
|
||||||
* [Download](#download)
|
|
||||||
* [Developer Quick-start](#developer-quickstart)
|
|
||||||
* [Setup](#setup)
|
|
||||||
* [Building](#building)
|
|
||||||
* [Code Formatting and Linting](#code-formatting-and-linting)
|
|
||||||
* [Generating Update Artifacts](#generating-update-artifacts)
|
|
||||||
* [Theming](#theming)
|
|
||||||
* [Screenshots](#screenshots)
|
|
||||||
* [Credits](#credits)
|
|
||||||
|
|
||||||
|
- [Download](#download)
|
||||||
|
- [Developer Quick-start](#developer-quickstart)
|
||||||
|
- [Setup](#setup)
|
||||||
|
- [Building](#building)
|
||||||
|
- [Code Formatting and Linting](#code-formatting-and-linting)
|
||||||
|
- [Generating Update Artifacts](#generating-update-artifacts)
|
||||||
|
- [Theming](#theming)
|
||||||
|
- [Screenshots](#screenshots)
|
||||||
|
- [Credits](#credits)
|
||||||
|
|
||||||
# Download
|
# Download
|
||||||
|
|
||||||
[Find release builds here!](https://github.com/Grasscutters/Cultivation/releases)
|
[Find release builds here!](https://github.com/Grasscutters/Cultivation/releases)
|
||||||
|
|
||||||
Once downloaded, extract somewhere and open as administrator.
|
Once downloaded, extract somewhere and open as administrator.
|
||||||
@@ -27,27 +30,33 @@ Once downloaded, extract somewhere and open as administrator.
|
|||||||
# Developer Quickstart
|
# Developer Quickstart
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
* Install [NodeJS >12](https://nodejs.org/en/)
|
|
||||||
* Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install) (cry about it `npm` lovers)
|
- Install [NodeJS >12](https://nodejs.org/en/)
|
||||||
* Install [Rust](https://www.rust-lang.org/tools/install)
|
- Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install) (cry about it `npm` lovers)
|
||||||
* `yarn install`
|
- Install [Rust](https://www.rust-lang.org/tools/install)
|
||||||
* `yarn start:dev`
|
- `yarn install`
|
||||||
|
- `yarn start:dev`
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
For a release build,
|
For a release build,
|
||||||
|
|
||||||
- `yarn build`
|
- `yarn build`
|
||||||
|
|
||||||
For a debug build,
|
For a debug build,
|
||||||
|
|
||||||
- `yarn build --debug`
|
- `yarn build --debug`
|
||||||
|
|
||||||
### Code Formatting and Linting
|
### Code Formatting and Linting
|
||||||
|
|
||||||
- `yarn format`
|
- `yarn format`
|
||||||
- `yarn lint`
|
- `yarn lint`
|
||||||
|
|
||||||
### Generating Update Artifacts
|
### Generating Update Artifacts
|
||||||
* Add the `TAURI_PRIVATE_KEY` as an environment variable with a path to your private key.
|
|
||||||
* Add the `TAURI_KEY_PASSWORD` as an environment variable with the password for your private key.
|
- Add the `TAURI_PRIVATE_KEY` as an environment variable with a path to your private key.
|
||||||
* `yarn build`
|
- Add the `TAURI_KEY_PASSWORD` as an environment variable with the password for your private key.
|
||||||
|
- `yarn build`
|
||||||
|
|
||||||
The update will be at `src-tauri/target/(release|debug)/msi/Cultivation_X.X.X_x64_xx-XX.msi.zip`
|
The update will be at `src-tauri/target/(release|debug)/msi/Cultivation_X.X.X_x64_xx-XX.msi.zip`
|
||||||
|
|
||||||
@@ -56,6 +65,7 @@ The update will be at `src-tauri/target/(release|debug)/msi/Cultivation_X.X.X_x6
|
|||||||
A full theming reference can be found [here!](/THEMES.md)
|
A full theming reference can be found [here!](/THEMES.md)
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
@@ -63,8 +73,9 @@ A full theming reference can be found [here!](/THEMES.md)
|
|||||||

|

|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
* [SpikeHD](https://github.com/SpikeHD): For originally creating **GrassClipper** and creating the amazing UI of Cultivation.
|
|
||||||
* [KingRainbow44](https://github.com/KingRainbow44): For building a proxy daemon from scratch and integrating it with Cultivation.
|
- [SpikeHD](https://github.com/SpikeHD): For originally creating **GrassClipper** and creating the amazing UI of Cultivation.
|
||||||
* [Benj](https://github.com/4Benj): For assistance in client patching.
|
- [KingRainbow44](https://github.com/KingRainbow44): For building a proxy daemon from scratch and integrating it with Cultivation.
|
||||||
* [lilmayofuksu](https://github.com/lilmayofuksu): For assistance in client patching.
|
- [Benj](https://github.com/4Benj): For assistance in client patching.
|
||||||
* [Tauri](https://tauri.app): For providing an amazing, efficient, and simple desktop application framework/library.
|
- [lilmayofuksu](https://github.com/lilmayofuksu): For assistance in client patching.
|
||||||
|
- [Tauri](https://tauri.app): For providing an amazing, efficient, and simple desktop application framework/library.
|
||||||
|
|||||||
47
THEMES.md
47
THEMES.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
1. Download your favorite theme! (You can find some in the `#themes` channel on Discord)
|
1. Download your favorite theme! (You can find some in the `#themes` channel on Discord)
|
||||||
2. Place the unzipped theme folder inside of `%appdata%/cultivation/themes` (The path should look something like this: `cultivation/themes/theme_name/index.json`)
|
2. Place the unzipped theme folder inside of `%appdata%/cultivation/themes` (The path should look something like this: `cultivation/themes/theme_name/index.json`)
|
||||||
4. Enable within Cultivation!
|
3. Enable within Cultivation!
|
||||||
|
|
||||||
# Creating your own theme
|
# Creating your own theme
|
||||||
|
|
||||||
@@ -16,16 +16,16 @@ You will need CSS and JS experience if you want to do anything cool.
|
|||||||
|
|
||||||
`index.json` is where you tell Cultivation which files and images to include. It supports the following properties:
|
`index.json` is where you tell Cultivation which files and images to include. It supports the following properties:
|
||||||
|
|
||||||
| Property | Description |
|
| Property | Description |
|
||||||
| :--- | :--- |
|
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `name` | The name of the theme. |
|
| `name` | The name of the theme. |
|
||||||
| `version` | Not shown anywhere, the version of the theme. |
|
| `version` | Not shown anywhere, the version of the theme. |
|
||||||
| `description` | Not shown anywhere, the description of the theme. |
|
| `description` | Not shown anywhere, the description of the theme. |
|
||||||
| `includes` | The files and folders to include. |
|
| `includes` | The files and folders to include. |
|
||||||
| `includes.css` | Array of CSS files to include. Example: `css: ["index.css"]` |
|
| `includes.css` | Array of CSS files to include. Example: `css: ["index.css"]` |
|
||||||
| `includes.js` | Array of JS files to includes. Example `js: ["index.js"]` |
|
| `includes.js` | Array of JS files to includes. Example `js: ["index.js"]` |
|
||||||
| `customBackgroundURL` | A custom image URL to set as the background. Backgrounds that users set in their config supercede this. Example: `"https://website.com/image.png"` |
|
| `customBackgroundURL` | A custom image URL to set as the background. Backgrounds that users set in their config supercede this. Example: `"https://website.com/image.png"` |
|
||||||
| `customBackgroundFile` | Path to a custom background image file. Backgrounds that users set in their config supercede this. Example: `"/image.png"` |
|
| `customBackgroundFile` | Path to a custom background image file. Backgrounds that users set in their config supercede this. Example: `"/image.png"` |
|
||||||
|
|
||||||
A full, complete `index.json` will look something like this:
|
A full, complete `index.json` will look something like this:
|
||||||
|
|
||||||
@@ -55,15 +55,17 @@ Below are some small examples of what you can do:
|
|||||||
```css
|
```css
|
||||||
/* Change the font */
|
/* Change the font */
|
||||||
body {
|
body {
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Remove the news section */
|
/* Remove the news section */
|
||||||
.NewsSection {
|
.NewsSection {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Change the right bar width */
|
/* Change the right bar width */
|
||||||
.RightBar {
|
.RightBar {
|
||||||
@@ -72,6 +74,7 @@ body {
|
|||||||
```
|
```
|
||||||
|
|
||||||
## How can I change XYZ element?
|
## How can I change XYZ element?
|
||||||
|
|
||||||
Every element is documented and describe [here](/docs/elementIds.md). Every\* single DOM element is assigned an ID to allow for easy and hyper-specific editing.
|
Every element is documented and describe [here](/docs/elementIds.md). Every\* single DOM element is assigned an ID to allow for easy and hyper-specific editing.
|
||||||
|
|
||||||
## Writing your JS
|
## Writing your JS
|
||||||
@@ -83,24 +86,26 @@ Below are some examples of what you can do:
|
|||||||
```js
|
```js
|
||||||
/* Change the version number every 500ms */
|
/* Change the version number every 500ms */
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
document.getElementById("version").innerHTML = "v" + Math.floor(Math.random() * 100);
|
document.getElementById('version').innerHTML = 'v' + Math.floor(Math.random() * 100)
|
||||||
}, 500);
|
}, 500)
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
/* Load a custom font */
|
/* Load a custom font */
|
||||||
const head = document.head
|
const head = document.head
|
||||||
const link = document.createElement("link")
|
const link = document.createElement('link')
|
||||||
|
|
||||||
link.href = "https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"
|
link.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap'
|
||||||
link.rel = "stylesheet"
|
link.rel = 'stylesheet'
|
||||||
link.type = "text/css"
|
link.type = 'text/css'
|
||||||
|
|
||||||
head.appendChild(link)
|
head.appendChild(link)
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
/* Create a new button that does nothing */
|
/* Create a new button that does nothing */
|
||||||
const newButton = document.createElement("button");
|
const newButton = document.createElement('button')
|
||||||
newButton.innerHTML = "New Button";
|
newButton.innerHTML = 'New Button'
|
||||||
|
|
||||||
document.body.appendChild(newButton);
|
document.body.appendChild(newButton)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,133 +1,135 @@
|
|||||||
# Documentation of Element ID's and Classes for custom theming
|
# Documentation of Element ID's and Classes for custom theming
|
||||||
|
|
||||||
## IDs
|
## IDs
|
||||||
|
|
||||||
This does not include commonly used components (buttons, divider lines, commit author and message, etc...) for accessing and modifying those elements, please check `Classes` section bellow.
|
This does not include commonly used components (buttons, divider lines, commit author and message, etc...) for accessing and modifying those elements, please check `Classes` section bellow.
|
||||||
|
|
||||||
| #ID | Description |
|
| #ID | Description |
|
||||||
|----------------------------------------|-----------------------------------------------------------------|
|
| ------------------------------------ | --------------------------------------------------------------- |
|
||||||
| `#miniDialogContainer` | Main container of MiniDialog |
|
| `#miniDialogContainer` | Main container of MiniDialog |
|
||||||
| `#miniDialogContainerTop` | Affects only top section of MiniDialog |
|
| `#miniDialogContainerTop` | Affects only top section of MiniDialog |
|
||||||
| `#miniDialogButtonClose` | Close button (SVG) of MiniDialog |
|
| `#miniDialogButtonClose` | Close button (SVG) of MiniDialog |
|
||||||
| `#miniDialogContent` | MiniDialog content |
|
| `#miniDialogContent` | MiniDialog content |
|
||||||
| `#rightBarContainer` | Main container of RightBar |
|
| `#rightBarContainer` | Main container of RightBar |
|
||||||
| `#rightBarContent` | RightBar content |
|
| `#rightBarContent` | RightBar content |
|
||||||
| `#rightBarButtonDiscord` | Discord button on the RightBar |
|
| `#rightBarButtonDiscord` | Discord button on the RightBar |
|
||||||
| `#rightBarButtonGithub` | Github button on the RightBar |
|
| `#rightBarButtonGithub` | Github button on the RightBar |
|
||||||
| `#playButton` | Main container for whole launch buttons section |
|
| `#playButton` | Main container for whole launch buttons section |
|
||||||
| `#serverControls` | Container of "play on grasscutter" checkbox |
|
| `#serverControls` | Container of "play on grasscutter" checkbox |
|
||||||
| `#enableGC` | "play on grasscutter" checkbox |
|
| `#enableGC` | "play on grasscutter" checkbox |
|
||||||
| `#ip` | Server ip input if play on grasscutter is enabled |
|
| `#ip` | Server ip input if play on grasscutter is enabled |
|
||||||
| `#port` | Server port input if play on grasscutter is enabled |
|
| `#port` | Server port input if play on grasscutter is enabled |
|
||||||
| `#httpsEnable` | "Enable https" checkbox if play on grasscutter is enabled |
|
| `#httpsEnable` | "Enable https" checkbox if play on grasscutter is enabled |
|
||||||
| `#officialPlay` | Launch button |
|
| `#officialPlay` | Launch button |
|
||||||
| `#serverLaunch` | Launch server button |
|
| `#serverLaunch` | Launch server button |
|
||||||
| `#serverlaunchIcon` | Icon (SVG) of server launch button |
|
| `#serverlaunchIcon` | Icon (SVG) of server launch button |
|
||||||
| `#serverConfigContainer` | Main container of server configuration section |
|
| `#serverConfigContainer` | Main container of server configuration section |
|
||||||
| `#serverLaunchContainer` | Main container of launch buttons (includes launch server) |
|
| `#serverLaunchContainer` | Main container of launch buttons (includes launch server) |
|
||||||
| `#topBarContainer` | Main container of launcher TopBar (minimize, exit, settings...) |
|
| `#topBarContainer` | Main container of launcher TopBar (minimize, exit, settings...) |
|
||||||
| `#title` | Title of the TopBar |
|
| `#title` | Title of the TopBar |
|
||||||
| `#version` | Version of the launcher in TopBar |
|
| `#version` | Version of the launcher in TopBar |
|
||||||
| `#topBarButtonContainer` | Container of launcher TopBar buttons only |
|
| `#topBarButtonContainer` | Container of launcher TopBar buttons only |
|
||||||
| `#closeBtn` | Exit launcher button |
|
| `#closeBtn` | Exit launcher button |
|
||||||
| `#minBtn` | Minimize launcher button |
|
| `#minBtn` | Minimize launcher button |
|
||||||
| `#settingsBtn` | Settings button |
|
| `#settingsBtn` | Settings button |
|
||||||
| `#downloadsBtn` | Downloads button (grasscutter resources, grasscutter...) |
|
| `#downloadsBtn` | Downloads button (grasscutter resources, grasscutter...) |
|
||||||
| `#newsContainer` | Main container of the news section |
|
| `#newsContainer` | Main container of the news section |
|
||||||
| `#newsTabsContainer` | Container for news tabs |
|
| `#newsTabsContainer` | Container for news tabs |
|
||||||
| `#commits` | News tabs container commits button |
|
| `#commits` | News tabs container commits button |
|
||||||
| `#latest_version` | News tabs for latest version button |
|
| `#latest_version` | News tabs for latest version button |
|
||||||
| `#newsContent` | Content section of news container |
|
| `#newsContent` | Content section of news container |
|
||||||
| `#newsCommitsTable` | Commits table of news section |
|
| `#newsCommitsTable` | Commits table of news section |
|
||||||
| `#downloadMenuContainerGCStable` | Grasscutter stable update container |
|
| `#downloadMenuContainerGCStable` | Grasscutter stable update container |
|
||||||
| `#downloadMenuLabelGCStable` | Label for stable update button |
|
| `#downloadMenuLabelGCStable` | Label for stable update button |
|
||||||
| `#downloadMenuButtonGCStable` | Button container for stable update button |
|
| `#downloadMenuButtonGCStable` | Button container for stable update button |
|
||||||
| `#grasscutterStableBtn` | "Update grasscutter stable" button |
|
| `#grasscutterStableBtn` | "Update grasscutter stable" button |
|
||||||
| `#downloadMenuContainerGCDev` | Grasscutter development update container |
|
| `#downloadMenuContainerGCDev` | Grasscutter development update container |
|
||||||
| `#downloadMenuLabelGCDev` | Label for latest update button |
|
| `#downloadMenuLabelGCDev` | Label for latest update button |
|
||||||
| `#downloadMenuButtonGCDev` | Button container for latest update button |
|
| `#downloadMenuButtonGCDev` | Button container for latest update button |
|
||||||
| `grasscutterLatestBtn` | "Update grasscutter latest" button |
|
| `grasscutterLatestBtn` | "Update grasscutter latest" button |
|
||||||
| `#downloadMenuContainerGCStableData` | Grasscutter stable data update container |
|
| `#downloadMenuContainerGCStableData` | Grasscutter stable data update container |
|
||||||
| `#downloadMenuLabelGCStableData` | Label for stable data update |
|
| `#downloadMenuLabelGCStableData` | Label for stable data update |
|
||||||
| `#downloadMenuButtonGCStableData` | Button container for stable data update button |
|
| `#downloadMenuButtonGCStableData` | Button container for stable data update button |
|
||||||
| `#grasscutterStableRepo` | "Update grasscutter stable data" button |
|
| `#grasscutterStableRepo` | "Update grasscutter stable data" button |
|
||||||
| `#downloadMenuContainerGCDevData` | Grasscutter latest data update container |
|
| `#downloadMenuContainerGCDevData` | Grasscutter latest data update container |
|
||||||
| `#downloadMenuLabelGCDevData` | Label for latest data update |
|
| `#downloadMenuLabelGCDevData` | Label for latest data update |
|
||||||
| `#downloadMenuButtonGCDevData` | Button container for latest data update button |
|
| `#downloadMenuButtonGCDevData` | Button container for latest data update button |
|
||||||
| `#grasscutterDevRepo` | "Update grasscutter latest data" button |
|
| `#grasscutterDevRepo` | "Update grasscutter latest data" button |
|
||||||
| `#downloadMenuContainerResources` | Container for grasscutter resources download |
|
| `#downloadMenuContainerResources` | Container for grasscutter resources download |
|
||||||
| `#downloadMenuLabelResources` | label for resources download |
|
| `#downloadMenuLabelResources` | label for resources download |
|
||||||
| `#downloadMenuButtonResources` | Button container for resources download button |
|
| `#downloadMenuButtonResources` | Button container for resources download button |
|
||||||
| `#resourcesBtn` | "Download grasscutter resources" button |
|
| `#resourcesBtn` | "Download grasscutter resources" button |
|
||||||
| `#menuContainer` | Generic Popup modal like menu container |
|
| `#menuContainer` | Generic Popup modal like menu container |
|
||||||
| `#menuContainerTop` | Top section of menu container |
|
| `#menuContainerTop` | Top section of menu container |
|
||||||
| `#menuHeading` | Menu title |
|
| `#menuHeading` | Menu title |
|
||||||
| `#menuButtonCloseContainer` | Container for menu close button |
|
| `#menuButtonCloseContainer` | Container for menu close button |
|
||||||
| `#menuButtonCloseIcon` | Menu close icon (SVG) |
|
| `#menuButtonCloseIcon` | Menu close icon (SVG) |
|
||||||
| `#menuContent` | Content section of the menu |
|
| `#menuContent` | Content section of the menu |
|
||||||
| `#menuOptionsContainerGameExec` | Container for game executable option section |
|
| `#menuOptionsContainerGameExec` | Container for game executable option section |
|
||||||
| `#menuOptionsLabelGameExec` | Label for game executable option |
|
| `#menuOptionsLabelGameExec` | Label for game executable option |
|
||||||
| `#menuOptionsDirGameExec` | Set game executable file browser |
|
| `#menuOptionsDirGameExec` | Set game executable file browser |
|
||||||
| `#menuOptionsContainerGCJar` | Container for grasscutter jar option |
|
| `#menuOptionsContainerGCJar` | Container for grasscutter jar option |
|
||||||
| `#menuOptionsLabelGCJar` | Label for grasscutter jar option |
|
| `#menuOptionsLabelGCJar` | Label for grasscutter jar option |
|
||||||
| `#menuOptionsDirGCJar` | Set grasscutter jar file browser |
|
| `#menuOptionsDirGCJar` | Set grasscutter jar file browser |
|
||||||
| `#menuOptionsContainerToggleEnc` | Container for toggle encryption option |
|
| `#menuOptionsContainerToggleEnc` | Container for toggle encryption option |
|
||||||
| `#menuOptionsLabelToggleEnc` | Label for toggle encryption option |
|
| `#menuOptionsLabelToggleEnc` | Label for toggle encryption option |
|
||||||
| `#menuOptionsButtonToggleEnc` | Toggle encryption button container |
|
| `#menuOptionsButtonToggleEnc` | Toggle encryption button container |
|
||||||
| `#toggleEnc` | Toggle encryption button |
|
| `#toggleEnc` | Toggle encryption button |
|
||||||
| `#menuOptionsContainerGCWGame` | Container for "grasscutter with game" option |
|
| `#menuOptionsContainerGCWGame` | Container for "grasscutter with game" option |
|
||||||
| `#menuOptionsLabelGCWDame` | Label for "grasscutter with game" option |
|
| `#menuOptionsLabelGCWDame` | Label for "grasscutter with game" option |
|
||||||
| `#menuOptionsCheckboxGCWGame` | Container for "grasscutter with game" option checkbox |
|
| `#menuOptionsCheckboxGCWGame` | Container for "grasscutter with game" option checkbox |
|
||||||
| `#gcWithGame` | Grasscutter with game checkbox |
|
| `#gcWithGame` | Grasscutter with game checkbox |
|
||||||
| `#menuOptionsContainerThemes` | Container for themes section |
|
| `#menuOptionsContainerThemes` | Container for themes section |
|
||||||
| `#menuOptionsLabelThemes` | Label for set themes option |
|
| `#menuOptionsLabelThemes` | Label for set themes option |
|
||||||
| `#menuOptionsSelectThemes` | Container for themes select menu |
|
| `#menuOptionsSelectThemes` | Container for themes select menu |
|
||||||
| `#menuOptionsSelectMenuThemes` | Set theme select menu |
|
| `#menuOptionsSelectMenuThemes` | Set theme select menu |
|
||||||
| `#menuOptionsContainerJavaPath` | Container for Java Path option |
|
| `#menuOptionsContainerJavaPath` | Container for Java Path option |
|
||||||
| `#menuOptionsLabelJavaPath` | Label for Java path option |
|
| `#menuOptionsLabelJavaPath` | Label for Java path option |
|
||||||
| `#menuOptionsDirJavaPath` | Container for java path file browser |
|
| `#menuOptionsDirJavaPath` | Container for java path file browser |
|
||||||
| `#menuOptionsContainerBG` | Container for Background option |
|
| `#menuOptionsContainerBG` | Container for Background option |
|
||||||
| `#menuOptionsLabelBG` | Label for background option |
|
| `#menuOptionsLabelBG` | Label for background option |
|
||||||
| `#menuOptionsDirBG` | Container for background url/local path option |
|
| `#menuOptionsDirBG` | Container for background url/local path option |
|
||||||
| `#menuOptionsContainerLang` | Container for language change option |
|
| `#menuOptionsContainerLang` | Container for language change option |
|
||||||
| `#menuOptionsLabelLang` | Label for language change option |
|
| `#menuOptionsLabelLang` | Label for language change option |
|
||||||
| `#menuOptionsSelectLang` | Container for language change select menu |
|
| `#menuOptionsSelectLang` | Container for language change select menu |
|
||||||
| `#menuOptionsSelectMenuLang` | Language select menu |
|
| `#menuOptionsSelectMenuLang` | Language select menu |
|
||||||
| `#DownloadProgress` | Download progress container |
|
| `#DownloadProgress` | Download progress container |
|
||||||
| `#bottomSectionContainer` | Bottom section container |
|
| `#bottomSectionContainer` | Bottom section container |
|
||||||
| `#miniDownloadContainer` | Container for mini download |
|
| `#miniDownloadContainer` | Container for mini download |
|
||||||
|
|
||||||
## Classes
|
## Classes
|
||||||
|
|
||||||
This is not full list of all classes, rather its list of classes for commonly used components that can not be accessed using element id system.
|
This is not full list of all classes, rather its list of classes for commonly used components that can not be accessed using element id system.
|
||||||
|
|
||||||
| .Class | Description |
|
| .Class | Description |
|
||||||
|-----------------------------|---------------------------------------------------------|
|
| ------------------------- | ------------------------------------------------------- | --- |
|
||||||
| `.BigButton` | Class for all buttons |
|
| `.BigButton` | Class for all buttons |
|
||||||
| `.BigButtonText` | Text inside a button | |
|
| `.BigButtonText` | Text inside a button | |
|
||||||
| `.Checkbox` | Checkbox container |
|
| `.Checkbox` | Checkbox container |
|
||||||
| `.CheckboxDisplay` | Content of checkbox |
|
| `.CheckboxDisplay` | Content of checkbox |
|
||||||
| `.DirInput` | Container for DirInput |
|
| `.DirInput` | Container for DirInput |
|
||||||
| `.FileSelectIcon` | Icon of DirInput |
|
| `.FileSelectIcon` | Icon of DirInput |
|
||||||
| `.DownloadList` | List of all downloads |
|
| `.DownloadList` | List of all downloads |
|
||||||
| `.DownloadSection` | Container for each download |
|
| `.DownloadSection` | Container for each download |
|
||||||
| `.DownloadTitle` | Contains file download path and current status |
|
| `.DownloadTitle` | Contains file download path and current status |
|
||||||
| `.DownloadPath` | Path of a download |
|
| `.DownloadPath` | Path of a download |
|
||||||
| `.DownloadStatus` | Status of a download |
|
| `.DownloadStatus` | Status of a download |
|
||||||
| `.DownloadSectionInner` | Contains progressbar of the download section |
|
| `.DownloadSectionInner` | Contains progressbar of the download section |
|
||||||
| `.HelpSection` | Container for help "?" circle button |
|
| `.HelpSection` | Container for help "?" circle button |
|
||||||
| `.HelpButton` | HelpButton itself |
|
| `.HelpButton` | HelpButton itself |
|
||||||
| `.HelpContents` | Content of help button once expanded |
|
| `.HelpContents` | Content of help button once expanded |
|
||||||
| `.MainProgressBarWrapper` | Container for MainProgressBar |
|
| `.MainProgressBarWrapper` | Container for MainProgressBar |
|
||||||
| `.ProgressBar` | ProgressBar (creativity left the brain) |
|
| `.ProgressBar` | ProgressBar (creativity left the brain) |
|
||||||
| `.InnerProgress` | ProgressBar percentage |
|
| `.InnerProgress` | ProgressBar percentage |
|
||||||
| `.MainProgressText` | Text for MainProgressBar |
|
| `.MainProgressText` | Text for MainProgressBar |
|
||||||
| `.ProgressBarWrapper` | Container for ProgressBar |
|
| `.ProgressBarWrapper` | Container for ProgressBar |
|
||||||
| `.DownloadControls` | DownloadControls of ProgressBar |
|
| `.DownloadControls` | DownloadControls of ProgressBar |
|
||||||
| `.downloadStop` | Container for download stop icon (SVG) |
|
| `.downloadStop` | Container for download stop icon (SVG) |
|
||||||
| `.ProgressText` | Text of the ProgressBar display current download status |
|
| `.ProgressText` | Text of the ProgressBar display current download status |
|
||||||
| `.TextInputWrapper` | Container for TextInput |
|
| `.TextInputWrapper` | Container for TextInput |
|
||||||
| `.TextClear` | Container for clear input content button |
|
| `.TextClear` | Container for clear input content button |
|
||||||
| `.TextInputClear` | TextInput clear button icon (SVG) |
|
| `.TextInputClear` | TextInput clear button icon (SVG) |
|
||||||
| `.Divider` | Container for line dividers |
|
| `.Divider` | Container for line dividers |
|
||||||
| `.DividerLine` | Divider line itself |
|
| `.DividerLine` | Divider line itself |
|
||||||
| `.CommitAuthor` | Author of a commit |
|
| `.CommitAuthor` | Author of a commit |
|
||||||
| `.CommitMessage` | Message of a commit |
|
| `.CommitMessage` | Message of a commit |
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
A guide dedicated for trying to troubleshoot Cultivation.
|
A guide dedicated for trying to troubleshoot Cultivation.
|
||||||
|
|
||||||
## The launcher doesn't appear to open.
|
## The launcher doesn't appear to open.
|
||||||
|
|
||||||
Try running the launcher with **administrative privileges**.\
|
Try running the launcher with **administrative privileges**.\
|
||||||
If this fixes your issue, you can force enable it in the **Compatability**\
|
If this fixes your issue, you can force enable it in the **Compatability**\
|
||||||
tab for the launcher's executable.
|
tab for the launcher's executable.
|
||||||
|
|
||||||
## Unable to play on `localhost`.
|
## Unable to play on `localhost`.
|
||||||
|
|
||||||
Make sure your server is running with **encryption disabled** and `useInRouting` to **false**.\
|
Make sure your server is running with **encryption disabled** and `useInRouting` to **false**.\
|
||||||
Additionally, make sure Cultivation **is set to not use HTTPS**.
|
Additionally, make sure Cultivation **is set to not use HTTPS**.
|
||||||
|
|
||||||
## "I can't do anything requiring the internet after closing Cultivation!"
|
## "I can't do anything requiring the internet after closing Cultivation!"
|
||||||
|
|
||||||
You probably didn't close Cultivation properly.\
|
You probably didn't close Cultivation properly.\
|
||||||
Go to your *Windows Settings*, then *Network*, then *Proxy*, then disable it.
|
Go to your _Windows Settings_, then _Network_, then _Proxy_, then disable it.
|
||||||
|
|||||||
@@ -5,10 +5,7 @@
|
|||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta name="description" content="Tauri-powered anime game launcher" />
|
||||||
name="description"
|
|
||||||
content="Tauri-powered anime game launcher"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
<title>Cultivation</title>
|
<title>Cultivation</title>
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
{
|
{
|
||||||
"lang_name": "Deutsch",
|
"lang_name": "Deutsch",
|
||||||
"main": {
|
"main": {
|
||||||
"title": "Cultivation",
|
"title": "Cultivation",
|
||||||
"launch_button": "Starten",
|
"launch_button": "Starten",
|
||||||
"gc_enable": "Über Grasscutter verbinden",
|
"gc_enable": "Über Grasscutter verbinden",
|
||||||
"https_enable": "HTTPS nutzen",
|
"https_enable": "HTTPS nutzen",
|
||||||
"ip_placeholder": "Server Adresse...",
|
"ip_placeholder": "Server Adresse...",
|
||||||
"port_placeholder": "Port...",
|
"port_placeholder": "Port...",
|
||||||
"files_downloading": "Herunterladen von Dateien: ",
|
"files_downloading": "Herunterladen von Dateien: ",
|
||||||
"files_extracting": "Extrahieren von Dateien: "
|
"files_extracting": "Extrahieren von Dateien: "
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"enabled": "Aktiviert",
|
"enabled": "Aktiviert",
|
||||||
"disabled": "Deaktiviert",
|
"disabled": "Deaktiviert",
|
||||||
"game_executable": "Spiel Datei auswählen",
|
"game_executable": "Spiel Datei auswählen",
|
||||||
"grasscutter_jar": "Grasscuter JAR auswählen",
|
"grasscutter_jar": "Grasscuter JAR auswählen",
|
||||||
"toggle_encryption": "Verschlüsselung umschalten",
|
"toggle_encryption": "Verschlüsselung umschalten",
|
||||||
"java_path": "Benutzerdefinierten Java Pfad setzen",
|
"java_path": "Benutzerdefinierten Java Pfad setzen",
|
||||||
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
|
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
|
||||||
"language": "Sprache auswählen",
|
"language": "Sprache auswählen",
|
||||||
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
|
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
|
||||||
"theme": "Theme auswählen"
|
"theme": "Theme auswählen"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
|
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
|
||||||
"grasscutter_latest_data": "Aktuellste Grasscutter Daten herunterladen",
|
"grasscutter_latest_data": "Aktuellste Grasscutter Daten herunterladen",
|
||||||
"grasscutter_stable_data_update": "Stabile Grasscutter Daten aktualisieren",
|
"grasscutter_stable_data_update": "Stabile Grasscutter Daten aktualisieren",
|
||||||
"grasscutter_latest_data_update": "Aktuellste Grasscutter Daten aktualisieren",
|
"grasscutter_latest_data_update": "Aktuellste Grasscutter Daten aktualisieren",
|
||||||
"grasscutter_stable": "Stabile Grasscutter Version herunterladen",
|
"grasscutter_stable": "Stabile Grasscutter Version herunterladen",
|
||||||
"grasscutter_latest": "Aktuellste Grasscutter Version herunterladen",
|
"grasscutter_latest": "Aktuellste Grasscutter Version herunterladen",
|
||||||
"grasscutter_stable_update": "Stabile Grasscutter Version aktualisieren",
|
"grasscutter_stable_update": "Stabile Grasscutter Version aktualisieren",
|
||||||
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
|
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
|
||||||
"resources": "Grasscutter Ressourcen herunterladen"
|
"resources": "Grasscutter Ressourcen herunterladen"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Lädt herunter",
|
"downloading": "Lädt herunter",
|
||||||
"extracting": "Extrahiert",
|
"extracting": "Extrahiert",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"finished": "Fertig",
|
"finished": "Fertig",
|
||||||
"stopped": "Gestoppt"
|
"stopped": "Gestoppt"
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"select_file": "Datei oder Ordner auswählen...",
|
"select_file": "Datei oder Ordner auswählen...",
|
||||||
"select_folder": "Ordner auswählen...",
|
"select_folder": "Ordner auswählen...",
|
||||||
"download": "Herunterladen"
|
"download": "Herunterladen"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
"latest_commits": "Letzte Commits",
|
"latest_commits": "Letzte Commits",
|
||||||
"latest_version": "Letzte Version"
|
"latest_version": "Letzte Version"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"port_help_text": "Vergewissern Sie sich, dass es sich um den Port des Dispatch-Servers handelt, nicht um den Port des Spiel-Servers. Dieser ist fast immer '443'.",
|
"port_help_text": "Vergewissern Sie sich, dass es sich um den Port des Dispatch-Servers handelt, nicht um den Port des Spiel-Servers. Dieser ist fast immer '443'.",
|
||||||
"game_help_text": "Sie müssen keine separate Kopie verwenden, um mit Grasscutter zu spielen. Dies ist entweder für ein Downgrade auf die Version 2.6 oder wenn Sie das Spiel nicht installiert haben.",
|
"game_help_text": "Sie müssen keine separate Kopie verwenden, um mit Grasscutter zu spielen. Dies ist entweder für ein Downgrade auf die Version 2.6 oder wenn Sie das Spiel nicht installiert haben.",
|
||||||
"gc_stable_jar": "Laden Sie den aktuellen stabilen Grasscutter-Build herunter, der eine Jar-Datei und Datendateien enthält.",
|
"gc_stable_jar": "Laden Sie den aktuellen stabilen Grasscutter-Build herunter, der eine Jar-Datei und Datendateien enthält.",
|
||||||
"gc_dev_jar": "Laden Sie die neueste Grasscutter-Entwicklungsversion herunter, welche eine Jar-Datei und Datendateien enthält.",
|
"gc_dev_jar": "Laden Sie die neueste Grasscutter-Entwicklungsversion herunter, welche eine Jar-Datei und Datendateien enthält.",
|
||||||
"gc_stable_data": "Laden Sie die stabilen Grasscutter Daten herunter, welche keine Jar-Datei enthalten. Dies ist nützlich zum Aktualisieren.",
|
"gc_stable_data": "Laden Sie die stabilen Grasscutter Daten herunter, welche keine Jar-Datei enthalten. Dies ist nützlich zum Aktualisieren.",
|
||||||
"gc_dev_data": "Laden Sie die neuesten Grasscutter-Entwicklungsdateien herunter, welche keine Jar-Datei enthält. Dies ist nützlich zum Aktualisieren.",
|
"gc_dev_data": "Laden Sie die neuesten Grasscutter-Entwicklungsdateien herunter, welche keine Jar-Datei enthält. Dies ist nützlich zum Aktualisieren.",
|
||||||
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben"
|
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,4 +69,4 @@
|
|||||||
"akebi": "Set Akebi Executable",
|
"akebi": "Set Akebi Executable",
|
||||||
"migoto": "Set 3dMigoto Executable"
|
"migoto": "Set 3dMigoto Executable"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,4 +66,4 @@
|
|||||||
"swag": {
|
"swag": {
|
||||||
"akebi": "Establecer el ejecutable de Akebi"
|
"akebi": "Establecer el ejecutable de Akebi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"background": "Atur Kustom Latar Belakang (link atau gambar file)",
|
"background": "Atur Kustom Latar Belakang (link atau gambar file)",
|
||||||
"theme": "Atur Tema"
|
"theme": "Atur Tema"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"grasscutter_stable_data": "Sedang Mendownload Grasscutter Versi Stabil",
|
"grasscutter_stable_data": "Sedang Mendownload Grasscutter Versi Stabil",
|
||||||
"grasscutter_latest_data": "Sedang Mendownload Grasscutter Data Terbaru",
|
"grasscutter_latest_data": "Sedang Mendownload Grasscutter Data Terbaru",
|
||||||
"grasscutter_stable_data_update": "Memperbaharui Grasscutter Data Stabil",
|
"grasscutter_stable_data_update": "Memperbaharui Grasscutter Data Stabil",
|
||||||
@@ -54,4 +54,4 @@
|
|||||||
"gc_dev_data": "Unduh file data Grasscutter Development saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",
|
"gc_dev_data": "Unduh file data Grasscutter Development saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",
|
||||||
"resources": "Ini juga diperlukan untuk menjalankan server Grasscutter. Tombol ini akan berwarna abu-abu jika Anda memiliki folder Resource yang ada dengan File di dalamnya"
|
"resources": "Ini juga diperlukan untuk menjalankan server Grasscutter. Tombol ini akan berwarna abu-abu jika Anda memiliki folder Resource yang ada dengan File di dalamnya"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
{
|
{
|
||||||
"lang_name": "Русский",
|
"lang_name": "Русский",
|
||||||
"main": {
|
"main": {
|
||||||
"title": "Cultivation",
|
"title": "Cultivation",
|
||||||
"launch_button": "Запустить",
|
"launch_button": "Запустить",
|
||||||
"gc_enable": "Подключиться с Grasscutter",
|
"gc_enable": "Подключиться с Grasscutter",
|
||||||
"https_enable": "Исп. HTTPS",
|
"https_enable": "Исп. HTTPS",
|
||||||
"ip_placeholder": "Айпи адрес...",
|
"ip_placeholder": "Айпи адрес...",
|
||||||
"port_placeholder": "Порт...",
|
"port_placeholder": "Порт...",
|
||||||
"files_downloading": "Файлов скачано: ",
|
"files_downloading": "Файлов скачано: ",
|
||||||
"files_extracting": "Извлечено файлов: "
|
"files_extracting": "Извлечено файлов: "
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"enabled": "Включено",
|
"enabled": "Включено",
|
||||||
"disabled": "Выключено",
|
"disabled": "Выключено",
|
||||||
"game_executable": "Установить исполняемый файл игры",
|
"game_executable": "Установить исполняемый файл игры",
|
||||||
"grasscutter_jar": "Установить Grasscutter JAR",
|
"grasscutter_jar": "Установить Grasscutter JAR",
|
||||||
"toggle_encryption": "Переключить шифрование",
|
"toggle_encryption": "Переключить шифрование",
|
||||||
"java_path": "Установить пользовательский путь Java",
|
"java_path": "Установить пользовательский путь Java",
|
||||||
"grasscutter_with_game": "Автоматически запускать Grasscutter вместе с игрой",
|
"grasscutter_with_game": "Автоматически запускать Grasscutter вместе с игрой",
|
||||||
"language": "Установить язык",
|
"language": "Установить язык",
|
||||||
"background": "Установить свой фон (ссылка или файл)",
|
"background": "Установить свой фон (ссылка или файл)",
|
||||||
"theme": "Установить тему"
|
"theme": "Установить тему"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
|
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
|
||||||
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
|
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
|
||||||
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",
|
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",
|
||||||
"grasscutter_latest_data_update": "Обновить последние данные Grasscutter",
|
"grasscutter_latest_data_update": "Обновить последние данные Grasscutter",
|
||||||
"grasscutter_stable": "Скачать стабильную версию Grasscutter",
|
"grasscutter_stable": "Скачать стабильную версию Grasscutter",
|
||||||
"grasscutter_latest": "Скачать последнюю версию Grasscutter",
|
"grasscutter_latest": "Скачать последнюю версию Grasscutter",
|
||||||
"grasscutter_stable_update": "Обновить стабильную версию Grasscutter",
|
"grasscutter_stable_update": "Обновить стабильную версию Grasscutter",
|
||||||
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
|
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
|
||||||
"resources": "Скачать ресурсы Grasscutter"
|
"resources": "Скачать ресурсы Grasscutter"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Скачивание",
|
"downloading": "Скачивание",
|
||||||
"extracting": "Извлечение",
|
"extracting": "Извлечение",
|
||||||
"error": "Ошибка",
|
"error": "Ошибка",
|
||||||
"finished": "Закончено",
|
"finished": "Закончено",
|
||||||
"stopped": "Остановлено"
|
"stopped": "Остановлено"
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"select_file": "Выберите файл или папку...",
|
"select_file": "Выберите файл или папку...",
|
||||||
"select_folder": "Выберите папку...",
|
"select_folder": "Выберите папку...",
|
||||||
"download": "Скачать"
|
"download": "Скачать"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
"latest_commits": "Последние коммиты",
|
"latest_commits": "Последние коммиты",
|
||||||
"latest_version": "Последняя версия"
|
"latest_version": "Последняя версия"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"port_help_text": "Убедитесь, что это порт Dispatch-сервера, не порт игрового сервера. Обычно это '443'.",
|
"port_help_text": "Убедитесь, что это порт Dispatch-сервера, не порт игрового сервера. Обычно это '443'.",
|
||||||
"game_help_text": "Вам не нужно устанавливать еще одну копию, что бы играть с Grascutter. Это нужно или для версии 2.6, или если у Вас не установлена игра.",
|
"game_help_text": "Вам не нужно устанавливать еще одну копию, что бы играть с Grascutter. Это нужно или для версии 2.6, или если у Вас не установлена игра.",
|
||||||
"gc_stable_jar": "Скачать последнюю стабильную версию Grasscutter, которая содержит jar файл и данные.",
|
"gc_stable_jar": "Скачать последнюю стабильную версию Grasscutter, которая содержит jar файл и данные.",
|
||||||
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
|
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
|
||||||
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
||||||
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
||||||
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами."
|
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,4 @@
|
|||||||
"gc_dev_data": "Tải xuống bản phát triển mới nhất các tệp dữ liệu của Grasscutter, không bao gồm file jar. Phù hợp khi cập nhật.",
|
"gc_dev_data": "Tải xuống bản phát triển mới nhất các tệp dữ liệu của Grasscutter, không bao gồm file jar. Phù hợp khi cập nhật.",
|
||||||
"resources": "Chúng được yêu cầu để chạy máy chủ Grasscutter. Nút này sẽ có màu xám nếu bạn có một thư mục tài nguyên có nội dung bên trong"
|
"resources": "Chúng được yêu cầu để chạy máy chủ Grasscutter. Nút này sẽ có màu xám nếu bạn có một thư mục tài nguyên có nội dung bên trong"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,20 +12,12 @@
|
|||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
"fs": {
|
"fs": {
|
||||||
"scope": [
|
"scope": ["$DATA", "$DATA/cultivation", "$DATA/cultivation/*"]
|
||||||
"$DATA",
|
|
||||||
"$DATA/cultivation",
|
|
||||||
"$DATA/cultivation/*"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"protocol": {
|
"protocol": {
|
||||||
"all": true,
|
"all": true,
|
||||||
"asset": true,
|
"asset": true,
|
||||||
"assetScope": [
|
"assetScope": ["$DATA", "$DATA/cultivation", "$DATA/cultivation/*"]
|
||||||
"$DATA",
|
|
||||||
"$DATA/cultivation",
|
|
||||||
"$DATA/cultivation/*"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"all": true
|
"all": true
|
||||||
},
|
},
|
||||||
@@ -37,13 +29,7 @@
|
|||||||
"depends": []
|
"depends": []
|
||||||
},
|
},
|
||||||
"externalBin": [],
|
"externalBin": [],
|
||||||
"icon": [
|
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
],
|
|
||||||
"identifier": "io.grasscutter",
|
"identifier": "io.grasscutter",
|
||||||
"shortDescription": "A game launcher.",
|
"shortDescription": "A game launcher.",
|
||||||
"longDescription": "A launcher for a certain anime game that proxies all related game traffic to external servers.",
|
"longDescription": "A launcher for a certain anime game that proxies all related game traffic to external servers.",
|
||||||
@@ -54,11 +40,7 @@
|
|||||||
"providerShortName": null,
|
"providerShortName": null,
|
||||||
"signingIdentity": null
|
"signingIdentity": null
|
||||||
},
|
},
|
||||||
"resources": [
|
"resources": ["lang/*.json", "keys/*", "./mhycrypto.dll"],
|
||||||
"lang/*.json",
|
|
||||||
"keys/*",
|
|
||||||
"./mhycrypto.dll"
|
|
||||||
],
|
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
"windows": {
|
"windows": {
|
||||||
"allowDowngrades": false,
|
"allowDowngrades": false,
|
||||||
@@ -93,4 +75,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'MiHoYo_SDK_Web', 'Helvetica Neue', BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: 'MiHoYo_SDK_Web', 'Helvetica Neue', BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
|
'Cantarell', 'Fira Sans', 'Droid Sans', sans-serif;
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||||
monospace;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,23 +7,15 @@ import Debug from './ui/Debug'
|
|||||||
|
|
||||||
import { getConfigOption } from './utils/configuration'
|
import { getConfigOption } from './utils/configuration'
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
|
||||||
document.getElementById('root') as HTMLElement
|
|
||||||
)
|
|
||||||
|
|
||||||
let isDebug = false;
|
let isDebug = false
|
||||||
|
|
||||||
(async() => {
|
;async () => {
|
||||||
isDebug = await getConfigOption('debug_enabled')
|
isDebug = await getConfigOption('debug_enabled')
|
||||||
})
|
}
|
||||||
|
|
||||||
root.render(
|
root.render(<React.StrictMode>{isDebug ? <Debug /> : <App />}</React.StrictMode>)
|
||||||
<React.StrictMode>
|
|
||||||
{
|
|
||||||
isDebug ? <Debug /> : <App />
|
|
||||||
}
|
|
||||||
</React.StrictMode>
|
|
||||||
)
|
|
||||||
|
|
||||||
import reportWebVitals from './utils/reportWebVitals'
|
import reportWebVitals from './utils/reportWebVitals'
|
||||||
isDebug && reportWebVitals(console.log)
|
isDebug && reportWebVitals(console.log)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ select:focus {
|
|||||||
border-bottom-color: #ffd326;
|
border-bottom-color: #ffd326;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root, .App {
|
#root,
|
||||||
|
.App {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +65,8 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.arrow-down {
|
.arrow-down {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-left: 50px solid transparent;
|
border-left: 50px solid transparent;
|
||||||
border-right: 50px solid transparent;
|
border-right: 50px solid transparent;
|
||||||
border-top: 50px solid transparent;
|
border-top: 50px solid transparent;
|
||||||
@@ -82,28 +83,28 @@ select:focus {
|
|||||||
|
|
||||||
.BottomSection {
|
.BottomSection {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0%;
|
bottom: 0%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, 0%);
|
transform: translate(-50%, 0%);
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 160px;
|
height: 160px;
|
||||||
|
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
box-shadow: inset 0px 5px 12px -3px rgb(50 50 50 / 75%);
|
box-shadow: inset 0px 5px 12px -3px rgb(50 50 50 / 75%);
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-height: 580px) {
|
@media (max-height: 580px) {
|
||||||
.BottomSection {
|
.BottomSection {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-height: 500px) {
|
@media (max-height: 500px) {
|
||||||
.BottomSection {
|
.BottomSection {
|
||||||
height: 140px;
|
height: 140px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,16 +25,16 @@ import { getTheme, loadTheme } from '../utils/themes'
|
|||||||
import { unpatchGame } from '../utils/metadata'
|
import { unpatchGame } from '../utils/metadata'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
[key: string]: never;
|
[key: string]: never
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
isDownloading: boolean;
|
isDownloading: boolean
|
||||||
optionsOpen: boolean;
|
optionsOpen: boolean
|
||||||
miniDownloadsOpen: boolean;
|
miniDownloadsOpen: boolean
|
||||||
downloadsOpen: boolean;
|
downloadsOpen: boolean
|
||||||
gameDownloadsOpen: boolean;
|
gameDownloadsOpen: boolean
|
||||||
bgFile: string;
|
bgFile: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_BG = 'https://api.grasscutter.io/cultivation/bgfile'
|
const DEFAULT_BG = 'https://api.grasscutter.io/cultivation/bgfile'
|
||||||
@@ -57,7 +57,7 @@ class App extends React.Component<IProps, IState> {
|
|||||||
console.log(payload)
|
console.log(payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
listen('jar_extracted', ({ payload }: { payload: string}) => {
|
listen('jar_extracted', ({ payload }: { payload: string }) => {
|
||||||
setConfigOption('grasscutter_path', payload)
|
setConfigOption('grasscutter_path', payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -69,9 +69,11 @@ class App extends React.Component<IProps, IState> {
|
|||||||
const unpatched = await unpatchGame()
|
const unpatched = await unpatchGame()
|
||||||
|
|
||||||
console.log(`unpatched game? ${unpatched}`)
|
console.log(`unpatched game? ${unpatched}`)
|
||||||
|
|
||||||
if (!unpatched) {
|
if (!unpatched) {
|
||||||
alert(`Could not unpatch game! (You should be able to find your metadata backup in ${await dataDir()}\\cultivation\\)`)
|
alert(
|
||||||
|
`Could not unpatch game! (You should be able to find your metadata backup in ${await dataDir()}\\cultivation\\)`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -108,45 +110,55 @@ class App extends React.Component<IProps, IState> {
|
|||||||
// Get custom bg AFTER theme is loaded !! important !!
|
// Get custom bg AFTER theme is loaded !! important !!
|
||||||
const custom_bg = await getConfigOption('customBackground')
|
const custom_bg = await getConfigOption('customBackground')
|
||||||
|
|
||||||
if(!custom_bg || !/png|jpg|jpeg$/.test(custom_bg)) {
|
if (!custom_bg || !/png|jpg|jpeg$/.test(custom_bg)) {
|
||||||
if(game_path) {
|
if (game_path) {
|
||||||
// Get the bg by invoking, then set the background to that bg.
|
// Get the bg by invoking, then set the background to that bg.
|
||||||
const bgLoc: string = await invoke('get_bg_file', {
|
const bgLoc: string = await invoke('get_bg_file', {
|
||||||
bgPath: root_path,
|
bgPath: root_path,
|
||||||
appdata: await dataDir()
|
appdata: await dataDir(),
|
||||||
})
|
})
|
||||||
|
|
||||||
bgLoc && this.setState({
|
bgLoc &&
|
||||||
bgFile: bgLoc
|
this.setState(
|
||||||
}, this.forceUpdate)
|
{
|
||||||
|
bgFile: bgLoc,
|
||||||
|
},
|
||||||
|
this.forceUpdate
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const isUrl = /^http(s)?:\/\//gm.test(custom_bg)
|
const isUrl = /^http(s)?:\/\//gm.test(custom_bg)
|
||||||
|
|
||||||
if (!isUrl) {
|
if (!isUrl) {
|
||||||
const isValid = await invoke('dir_exists', {
|
const isValid = await invoke('dir_exists', {
|
||||||
path: custom_bg
|
path: custom_bg,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
bgFile: isValid ? convertFileSrc(custom_bg) : DEFAULT_BG
|
{
|
||||||
}, this.forceUpdate)
|
bgFile: isValid ? convertFileSrc(custom_bg) : DEFAULT_BG,
|
||||||
|
},
|
||||||
|
this.forceUpdate
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Check if URL returns a valid image.
|
// Check if URL returns a valid image.
|
||||||
const isValid = await invoke('valid_url', {
|
const isValid = await invoke('valid_url', {
|
||||||
url: custom_bg
|
url: custom_bg,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
bgFile: isValid ? custom_bg : DEFAULT_BG
|
{
|
||||||
}, this.forceUpdate)
|
bgFile: isValid ? custom_bg : DEFAULT_BG,
|
||||||
|
},
|
||||||
|
this.forceUpdate
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cert_generated) {
|
if (!cert_generated) {
|
||||||
// Generate the certificate
|
// Generate the certificate
|
||||||
await invoke('generate_ca_files', {
|
await invoke('generate_ca_files', {
|
||||||
path: await dataDir() + 'cultivation'
|
path: (await dataDir()) + 'cultivation',
|
||||||
})
|
})
|
||||||
|
|
||||||
await setConfigOption('cert_generated', true)
|
await setConfigOption('cert_generated', true)
|
||||||
@@ -155,18 +167,23 @@ class App extends React.Component<IProps, IState> {
|
|||||||
// Period check to only show progress bar when downloading files
|
// Period check to only show progress bar when downloading files
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isDownloading: downloadHandler.getDownloads().filter(d => d.status !== 'finished')?.length > 0
|
isDownloading: downloadHandler.getDownloads().filter((d) => d.status !== 'finished')?.length > 0,
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="App" style={
|
<div
|
||||||
this.state.bgFile ? {
|
className="App"
|
||||||
background: `url("${this.state.bgFile}") fixed`,
|
style={
|
||||||
} : {}
|
this.state.bgFile
|
||||||
}>
|
? {
|
||||||
|
background: `url("${this.state.bgFile}") fixed`,
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
>
|
||||||
<TopBar
|
<TopBar
|
||||||
optFunc={() => {
|
optFunc={() => {
|
||||||
this.setState({ optionsOpen: !this.state.optionsOpen })
|
this.setState({ optionsOpen: !this.state.optionsOpen })
|
||||||
@@ -199,10 +216,7 @@ class App extends React.Component<IProps, IState> {
|
|||||||
{
|
{
|
||||||
// Download menu
|
// Download menu
|
||||||
this.state.downloadsOpen ? (
|
this.state.downloadsOpen ? (
|
||||||
<Downloads
|
<Downloads downloadManager={downloadHandler} closeFn={() => this.setState({ downloadsOpen: false })} />
|
||||||
downloadManager={downloadHandler}
|
|
||||||
closeFn={() => this.setState({ downloadsOpen: false })}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,22 +233,18 @@ class App extends React.Component<IProps, IState> {
|
|||||||
{
|
{
|
||||||
// Game downloads menu
|
// Game downloads menu
|
||||||
this.state.gameDownloadsOpen ? (
|
this.state.gameDownloadsOpen ? (
|
||||||
<Game
|
<Game downloadManager={downloadHandler} closeFn={() => this.setState({ gameDownloadsOpen: false })} />
|
||||||
downloadManager={downloadHandler}
|
|
||||||
closeFn={() => this.setState({ gameDownloadsOpen: false })}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className="BottomSection" id="bottomSectionContainer">
|
<div className="BottomSection" id="bottomSectionContainer">
|
||||||
<ServerLaunchSection />
|
<ServerLaunchSection />
|
||||||
|
|
||||||
<div id="DownloadProgress"
|
<div
|
||||||
|
id="DownloadProgress"
|
||||||
onClick={() => this.setState({ miniDownloadsOpen: !this.state.miniDownloadsOpen })}
|
onClick={() => this.setState({ miniDownloadsOpen: !this.state.miniDownloadsOpen })}
|
||||||
>
|
>
|
||||||
{ this.state.isDownloading ?
|
{this.state.isDownloading ? <MainProgressBar downloadManager={downloadHandler} /> : null}
|
||||||
<MainProgressBar downloadManager={downloadHandler} />
|
|
||||||
: null }
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import './App.css'
|
|||||||
|
|
||||||
import TopBar from './components/TopBar'
|
import TopBar from './components/TopBar'
|
||||||
|
|
||||||
import {invoke} from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
import {dataDir} from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import TextInput from './components/common/TextInput'
|
import TextInput from './components/common/TextInput'
|
||||||
|
|
||||||
let proxyAddress = ''
|
let proxyAddress = ''
|
||||||
@@ -15,7 +15,7 @@ async function setProxyAddress(address: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function startProxy() {
|
async function startProxy() {
|
||||||
await invoke('connect', { port: 2222, certificatePath: await dataDir() + '\\cultivation\\ca' })
|
await invoke('connect', { port: 2222, certificatePath: (await dataDir()) + '\\cultivation\\ca' })
|
||||||
await invoke('open_in_browser', { url: 'https://hoyoverse.com' })
|
await invoke('open_in_browser', { url: 'https://hoyoverse.com' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,14 +24,14 @@ async function stopProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function generateCertificates() {
|
async function generateCertificates() {
|
||||||
await invoke('generate_ca_files', { path: await dataDir() + '\\cultivation' })
|
await invoke('generate_ca_files', { path: (await dataDir()) + '\\cultivation' })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateInfo() {
|
async function generateInfo() {
|
||||||
console.log({
|
console.log({
|
||||||
certificatePath: await dataDir() + '\\cultivation\\ca',
|
certificatePath: (await dataDir()) + '\\cultivation\\ca',
|
||||||
isAdmin: await invoke('is_elevated'),
|
isAdmin: await invoke('is_elevated'),
|
||||||
connectingTo: proxyAddress
|
connectingTo: proxyAddress,
|
||||||
})
|
})
|
||||||
alert('check your dev console and send that in #cultivation')
|
alert('check your dev console and send that in #cultivation')
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ function none() {
|
|||||||
alert('none')
|
alert('none')
|
||||||
}
|
}
|
||||||
|
|
||||||
class Debug extends React.Component{
|
class Debug extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
@@ -55,4 +55,4 @@ class Debug extends React.Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Debug
|
export default Debug
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.MiniDialog {
|
.MiniDialog {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
|
||||||
/* Len and width */
|
/* Len and width */
|
||||||
height: 30%;
|
height: 30%;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
@@ -32,4 +32,4 @@
|
|||||||
|
|
||||||
.MiniDialog .ProgressText {
|
.MiniDialog .ProgressText {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import Close from '../../resources/icons/close.svg'
|
|||||||
import './MiniDialog.css'
|
import './MiniDialog.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children: React.ReactNode[] | React.ReactNode;
|
children: React.ReactNode[] | React.ReactNode
|
||||||
title?: string;
|
title?: string
|
||||||
closeable?: boolean;
|
closeable?: boolean
|
||||||
closeFn: () => void;
|
closeFn: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MiniDialog extends React.Component<IProps, never> {
|
export default class MiniDialog extends React.Component<IProps, never> {
|
||||||
@@ -19,7 +19,7 @@ export default class MiniDialog extends React.Component<IProps, never> {
|
|||||||
document.addEventListener('mousedown', (evt) => {
|
document.addEventListener('mousedown', (evt) => {
|
||||||
const tgt = evt.target as HTMLElement
|
const tgt = evt.target as HTMLElement
|
||||||
const isInside = tgt.closest('.MiniDialog') !== null
|
const isInside = tgt.closest('.MiniDialog') !== null
|
||||||
|
|
||||||
if (!isInside) {
|
if (!isInside) {
|
||||||
this.props.closeFn()
|
this.props.closeFn()
|
||||||
}
|
}
|
||||||
@@ -33,13 +33,12 @@ export default class MiniDialog extends React.Component<IProps, never> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="MiniDialog" id="miniDialogContainer">
|
<div className="MiniDialog" id="miniDialogContainer">
|
||||||
{
|
{this.props.closeable !== undefined && this.props.closeable ? (
|
||||||
this.props.closeable !== undefined && this.props.closeable ?
|
<div className="MiniDialogTop" id="miniDialogContainerTop" onClick={this.props.closeFn}>
|
||||||
<div className="MiniDialogTop" id="miniDialogContainerTop" onClick={this.props.closeFn}>
|
<span>{this.props?.title}</span>
|
||||||
<span>{this.props?.title}</span>
|
<img src={Close} className="MiniDialogClose" id="miniDialogButtonClose" />
|
||||||
<img src={Close} className="MiniDialogClose" id="miniDialogButtonClose" />
|
</div>
|
||||||
</div> : null
|
) : null}
|
||||||
}
|
|
||||||
|
|
||||||
<div className="MiniDialogInner" id="miniDialogContent">
|
<div className="MiniDialogInner" id="miniDialogContent">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
@@ -47,4 +46,4 @@ export default class MiniDialog extends React.Component<IProps, never> {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(0%, 0%);
|
transform: translate(0%, 0%);
|
||||||
|
|
||||||
display:flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -36,14 +36,14 @@
|
|||||||
filter: invert(75%) sepia(0%) saturate(100%) hue-rotate(0deg) brightness(100%) contrast(100%);
|
filter: invert(75%) sepia(0%) saturate(100%) hue-rotate(0deg) brightness(100%) contrast(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-height: 580px) {
|
@media (max-height: 580px) {
|
||||||
.RightBar {
|
.RightBar {
|
||||||
height: calc(100vh - 180px);
|
height: calc(100vh - 180px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-height: 500px) {
|
@media (max-height: 500px) {
|
||||||
.RightBar {
|
.RightBar {
|
||||||
height: calc(100vh - 170px);
|
height: calc(100vh - 170px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { invoke } from '@tauri-apps/api'
|
import { invoke } from '@tauri-apps/api'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import Discord from '../../resources/icons/discord.svg'
|
import Discord from '../../resources/icons/discord.svg'
|
||||||
import Github from '../../resources/icons/github.svg'
|
import Github from '../../resources/icons/github.svg'
|
||||||
|
|
||||||
import './RightBar.css'
|
import './RightBar.css'
|
||||||
|
|
||||||
@@ -28,4 +28,4 @@ export default class RightBar extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,17 +109,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1040px) {
|
@media (max-width: 1040px) {
|
||||||
#playButton {
|
#playButton {
|
||||||
right: 5%;
|
right: 5%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 870px) {
|
@media (max-width: 870px) {
|
||||||
#playButton {
|
#playButton {
|
||||||
min-width: 235px;
|
min-width: 235px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#officialPlay {
|
#officialPlay {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,26 +11,26 @@ import Server from '../../resources/icons/server.svg'
|
|||||||
import Akebi from '../../resources/icons/akebi.svg'
|
import Akebi from '../../resources/icons/akebi.svg'
|
||||||
|
|
||||||
import './ServerLaunchSection.css'
|
import './ServerLaunchSection.css'
|
||||||
import {dataDir} from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import { getGameExecutable } from '../../utils/game'
|
import { getGameExecutable } from '../../utils/game'
|
||||||
import { patchGame, unpatchGame } from '../../utils/metadata'
|
import { patchGame, unpatchGame } from '../../utils/metadata'
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
grasscutterEnabled: boolean;
|
grasscutterEnabled: boolean
|
||||||
buttonLabel: string;
|
buttonLabel: string
|
||||||
checkboxLabel: string;
|
checkboxLabel: string
|
||||||
ip: string;
|
ip: string
|
||||||
port: string;
|
port: string
|
||||||
|
|
||||||
ipPlaceholder: string;
|
ipPlaceholder: string
|
||||||
portPlaceholder: string;
|
portPlaceholder: string
|
||||||
|
|
||||||
portHelpText: string;
|
portHelpText: string
|
||||||
|
|
||||||
httpsLabel: string;
|
httpsLabel: string
|
||||||
httpsEnabled: boolean;
|
httpsEnabled: boolean
|
||||||
|
|
||||||
swag: boolean;
|
swag: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ServerLaunchSection extends React.Component<{}, IState> {
|
export default class ServerLaunchSection extends React.Component<{}, IState> {
|
||||||
@@ -48,7 +48,7 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
portHelpText: '',
|
portHelpText: '',
|
||||||
httpsLabel: '',
|
httpsLabel: '',
|
||||||
httpsEnabled: false,
|
httpsEnabled: false,
|
||||||
swag: false
|
swag: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
||||||
@@ -74,7 +74,7 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
portHelpText: await translate('help.port_help_text'),
|
portHelpText: await translate('help.port_help_text'),
|
||||||
httpsLabel: await translate('main.https_enable'),
|
httpsLabel: await translate('main.https_enable'),
|
||||||
httpsEnabled: config.https_enabled || false,
|
httpsEnabled: config.https_enabled || false,
|
||||||
swag: config.swag_mode || false
|
swag: config.swag_mode || false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
|
|
||||||
// Set state as well
|
// Set state as well
|
||||||
this.setState({
|
this.setState({
|
||||||
grasscutterEnabled: config.toggle_grasscutter
|
grasscutterEnabled: config.toggle_grasscutter,
|
||||||
})
|
})
|
||||||
|
|
||||||
await saveConfig(config)
|
await saveConfig(config)
|
||||||
@@ -94,11 +94,11 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
async playGame(exe?: string, proc_name?: string) {
|
async playGame(exe?: string, proc_name?: string) {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
|
|
||||||
if(!await getGameExecutable()) {
|
if (!(await getGameExecutable())) {
|
||||||
alert('Game executable not set!')
|
alert('Game executable not set!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to proxy
|
// Connect to proxy
|
||||||
if (config.toggle_grasscutter) {
|
if (config.toggle_grasscutter) {
|
||||||
if (config.patch_metadata) {
|
if (config.patch_metadata) {
|
||||||
@@ -117,14 +117,16 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
await setConfigOption('last_port', this.state.port)
|
await setConfigOption('last_port', this.state.port)
|
||||||
|
|
||||||
await invoke('enable_process_watcher', {
|
await invoke('enable_process_watcher', {
|
||||||
process: proc_name || game_exe
|
process: proc_name || game_exe,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (config.use_internal_proxy) {
|
if (config.use_internal_proxy) {
|
||||||
// Set IP
|
// Set IP
|
||||||
await invoke('set_proxy_addr', { addr: (this.state.httpsEnabled ? 'https':'http') + '://' + this.state.ip + ':' + this.state.port })
|
await invoke('set_proxy_addr', {
|
||||||
|
addr: (this.state.httpsEnabled ? 'https' : 'http') + '://' + this.state.ip + ':' + this.state.port,
|
||||||
|
})
|
||||||
// Connect to proxy
|
// Connect to proxy
|
||||||
await invoke('connect', { port: 8365, certificatePath: await dataDir() + '\\cultivation\\ca' })
|
await invoke('connect', { port: 8365, certificatePath: (await dataDir()) + '\\cultivation\\ca' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open server as well if the options are set
|
// Open server as well if the options are set
|
||||||
@@ -137,21 +139,23 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
await invoke('run_jar', {
|
await invoke('run_jar', {
|
||||||
path: config.grasscutter_path,
|
path: config.grasscutter_path,
|
||||||
executeIn: jarFolder,
|
executeIn: jarFolder,
|
||||||
javaPath: config.java_path || ''
|
javaPath: config.java_path || '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const unpatched = await unpatchGame()
|
const unpatched = await unpatchGame()
|
||||||
|
|
||||||
if (!unpatched) {
|
if (!unpatched) {
|
||||||
alert(`Could not unpatch game, aborting launch! (You can find your metadata backup in ${await dataDir()}\\cultivation\\)`)
|
alert(
|
||||||
|
`Could not unpatch game, aborting launch! (You can find your metadata backup in ${await dataDir()}\\cultivation\\)`
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch the program
|
// Launch the program
|
||||||
const gameExists = await invoke('dir_exists', {
|
const gameExists = await invoke('dir_exists', {
|
||||||
path: exe || config.game_install_path
|
path: exe || config.game_install_path,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (gameExists) await invoke('run_program', { path: exe || config.game_install_path })
|
if (gameExists) await invoke('run_program', { path: exe || config.game_install_path })
|
||||||
@@ -175,7 +179,7 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
await invoke('run_jar', {
|
await invoke('run_jar', {
|
||||||
path: config.grasscutter_path,
|
path: config.grasscutter_path,
|
||||||
executeIn: jarFolder,
|
executeIn: jarFolder,
|
||||||
javaPath: config.java_path || ''
|
javaPath: config.java_path || '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +198,7 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
|
|
||||||
// First launch 3dm
|
// First launch 3dm
|
||||||
invoke('run_program', {
|
invoke('run_program', {
|
||||||
path: config.migoto_path
|
path: config.migoto_path,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Then play the game as normal
|
// Then play the game as normal
|
||||||
@@ -203,13 +207,13 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
|
|
||||||
setIp(text: string) {
|
setIp(text: string) {
|
||||||
this.setState({
|
this.setState({
|
||||||
ip: text
|
ip: text,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setPort(text: string) {
|
setPort(text: string) {
|
||||||
this.setState({
|
this.setState({
|
||||||
port: text
|
port: text,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +224,7 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
|
|
||||||
// Set state as well
|
// Set state as well
|
||||||
this.setState({
|
this.setState({
|
||||||
httpsEnabled: config.https_enabled
|
httpsEnabled: config.https_enabled,
|
||||||
})
|
})
|
||||||
|
|
||||||
await saveConfig(config)
|
await saveConfig(config)
|
||||||
@@ -230,40 +234,59 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
return (
|
return (
|
||||||
<div id="playButton">
|
<div id="playButton">
|
||||||
<div id="serverControls">
|
<div id="serverControls">
|
||||||
<Checkbox id="enableGC" label={this.state.checkboxLabel} onChange={this.toggleGrasscutter} checked={this.state.grasscutterEnabled}/>
|
<Checkbox
|
||||||
|
id="enableGC"
|
||||||
|
label={this.state.checkboxLabel}
|
||||||
|
onChange={this.toggleGrasscutter}
|
||||||
|
checked={this.state.grasscutterEnabled}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{this.state.grasscutterEnabled && (
|
||||||
this.state.grasscutterEnabled && (
|
<div>
|
||||||
<div>
|
<div className="ServerConfig" id="serverConfigContainer">
|
||||||
<div className="ServerConfig" id="serverConfigContainer">
|
<TextInput
|
||||||
<TextInput id="ip" key="ip" placeholder={this.state.ipPlaceholder} onChange={this.setIp} initalValue={this.state.ip} />
|
id="ip"
|
||||||
<TextInput style={{
|
key="ip"
|
||||||
|
placeholder={this.state.ipPlaceholder}
|
||||||
|
onChange={this.setIp}
|
||||||
|
initalValue={this.state.ip}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
width: '10%',
|
width: '10%',
|
||||||
}} id="port" key="port" placeholder={this.state.portPlaceholder} onChange={this.setPort} initalValue={this.state.port} />
|
}}
|
||||||
<HelpButton contents={this.state.portHelpText} />
|
id="port"
|
||||||
<Checkbox id="httpsEnable" label={this.state.httpsLabel} onChange={this.toggleHttps} checked={this.state.httpsEnabled} />
|
key="port"
|
||||||
</div>
|
placeholder={this.state.portPlaceholder}
|
||||||
|
onChange={this.setPort}
|
||||||
|
initalValue={this.state.port}
|
||||||
|
/>
|
||||||
|
<HelpButton contents={this.state.portHelpText} />
|
||||||
|
<Checkbox
|
||||||
|
id="httpsEnable"
|
||||||
|
label={this.state.httpsLabel}
|
||||||
|
onChange={this.toggleHttps}
|
||||||
|
checked={this.state.httpsEnabled}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<div className="ServerLaunchButtons" id="serverLaunchContainer">
|
<div className="ServerLaunchButtons" id="serverLaunchContainer">
|
||||||
<BigButton onClick={this.playGame} id="officialPlay">{this.state.buttonLabel}</BigButton>
|
<BigButton onClick={this.playGame} id="officialPlay">
|
||||||
{
|
{this.state.buttonLabel}
|
||||||
this.state.swag && (
|
</BigButton>
|
||||||
<>
|
{this.state.swag && (
|
||||||
<BigButton onClick={this.launchAkebi} id="akebiLaunch">
|
<>
|
||||||
<img className="AkebiIcon" id="akebiIcon" src={Akebi} />
|
<BigButton onClick={this.launchAkebi} id="akebiLaunch">
|
||||||
</BigButton>
|
<img className="AkebiIcon" id="akebiIcon" src={Akebi} />
|
||||||
<BigButton onClick={this.launch3dm} id="serverLaunch">
|
</BigButton>
|
||||||
3DM
|
<BigButton onClick={this.launch3dm} id="serverLaunch">
|
||||||
</BigButton>
|
3DM
|
||||||
</>
|
</BigButton>
|
||||||
|
</>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
<BigButton onClick={this.launchServer} id="serverLaunch">
|
<BigButton onClick={this.launchServer} id="serverLaunch">
|
||||||
<img className="ServerIcon" id="serverLaunchIcon" src={Server} />
|
<img className="ServerIcon" id="serverLaunchIcon" src={Server} />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
@@ -271,4 +294,4 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,4 @@
|
|||||||
to {
|
to {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ import './TopBar.css'
|
|||||||
import { getConfig, setConfigOption } from '../../utils/configuration'
|
import { getConfig, setConfigOption } from '../../utils/configuration'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
optFunc: () => void;
|
optFunc: () => void
|
||||||
downFunc: () => void;
|
downFunc: () => void
|
||||||
gameFunc: () => void;
|
gameFunc: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
version: string;
|
version: string
|
||||||
clicks: number;
|
clicks: number
|
||||||
intv: NodeJS.Timeout | null;
|
intv: NodeJS.Timeout | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TopBar extends React.Component<IProps, IState> {
|
export default class TopBar extends React.Component<IProps, IState> {
|
||||||
@@ -30,7 +30,7 @@ export default class TopBar extends React.Component<IProps, IState> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
version: '0.0.0',
|
version: '0.0.0',
|
||||||
clicks: 0,
|
clicks: 0,
|
||||||
intv: null
|
intv: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.activateClick = this.activateClick.bind(this)
|
this.activateClick = this.activateClick.bind(this)
|
||||||
@@ -59,10 +59,10 @@ export default class TopBar extends React.Component<IProps, IState> {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Gotta clear it so it goes back to regular colors
|
// Gotta clear it so it goes back to regular colors
|
||||||
this.setState({
|
this.setState({
|
||||||
clicks: 0
|
clicks: 0,
|
||||||
})
|
})
|
||||||
}, 600)
|
}, 600)
|
||||||
|
|
||||||
// Activate... SWAG MODE
|
// Activate... SWAG MODE
|
||||||
await setConfigOption('swag_mode', true)
|
await setConfigOption('swag_mode', true)
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export default class TopBar extends React.Component<IProps, IState> {
|
|||||||
if (this.state.clicks < 3) {
|
if (this.state.clicks < 3) {
|
||||||
this.setState({
|
this.setState({
|
||||||
clicks: this.state.clicks + 1,
|
clicks: this.state.clicks + 1,
|
||||||
intv: setTimeout(() => this.setState({ clicks: 0 }), 1500)
|
intv: setTimeout(() => this.setState({ clicks: 0 }), 1500),
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -89,29 +89,31 @@ export default class TopBar extends React.Component<IProps, IState> {
|
|||||||
<span data-tauri-drag-region>
|
<span data-tauri-drag-region>
|
||||||
<Tr text="main.title" />
|
<Tr text="main.title" />
|
||||||
</span>
|
</span>
|
||||||
<span data-tauri-drag-region id="version">{this.state?.version}</span>
|
<span data-tauri-drag-region id="version">
|
||||||
|
{this.state?.version}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/**
|
||||||
|
* HEY YOU
|
||||||
|
*
|
||||||
|
* If you're looking at the source code to find the swag mode thing, that's okay! If you're not, move along...
|
||||||
|
* Just do me a favor and don't go telling everyone about how you found it. If you are just helping someone who
|
||||||
|
* for some reason needs it, that's fine, but not EVERYONE needs it, which is why it exists in the first place.
|
||||||
|
*/}
|
||||||
|
<div id="unassumingButton" className={this.state.clicks === 2 ? 'spin' : ''} onClick={this.activateClick}>
|
||||||
|
?
|
||||||
</div>
|
</div>
|
||||||
{
|
|
||||||
/**
|
|
||||||
* HEY YOU
|
|
||||||
*
|
|
||||||
* If you're looking at the source code to find the swag mode thing, that's okay! If you're not, move along...
|
|
||||||
* Just do me a favor and don't go telling everyone about how you found it. If you are just helping someone who
|
|
||||||
* for some reason needs it, that's fine, but not EVERYONE needs it, which is why it exists in the first place.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
<div id="unassumingButton" className={this.state.clicks === 2 ? 'spin' : ''} onClick={this.activateClick}>?</div>
|
|
||||||
<div className="TopBtns" id="topBarButtonContainer">
|
<div className="TopBtns" id="topBarButtonContainer">
|
||||||
<div id="closeBtn" onClick={this.handleClose} className='TopButton'>
|
<div id="closeBtn" onClick={this.handleClose} className="TopButton">
|
||||||
<img src={closeIcon} alt="close" />
|
<img src={closeIcon} alt="close" />
|
||||||
</div>
|
</div>
|
||||||
<div id="minBtn" onClick={this.handleMinimize} className='TopButton'>
|
<div id="minBtn" onClick={this.handleMinimize} className="TopButton">
|
||||||
<img src={minIcon} alt="minimize" />
|
<img src={minIcon} alt="minimize" />
|
||||||
</div>
|
</div>
|
||||||
<div id="settingsBtn" onClick={this.props.optFunc} className='TopButton'>
|
<div id="settingsBtn" onClick={this.props.optFunc} className="TopButton">
|
||||||
<img src={cogBtn} alt="settings" />
|
<img src={cogBtn} alt="settings" />
|
||||||
</div>
|
</div>
|
||||||
<div id="downloadsBtn" className='TopButton' onClick={this.props.downFunc}>
|
<div id="downloadsBtn" className="TopButton" onClick={this.props.downFunc}>
|
||||||
<img src={downBtn} alt="downloads" />
|
<img src={downBtn} alt="downloads" />
|
||||||
</div>
|
</div>
|
||||||
{/* <div id="gameBtn" className="TopButton" onClick={this.props.gameFunc}>
|
{/* <div id="gameBtn" className="TopButton" onClick={this.props.gameFunc}>
|
||||||
@@ -121,4 +123,4 @@ export default class TopBar extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,4 @@
|
|||||||
|
|
||||||
.BigButton.disabled:hover {
|
.BigButton.disabled:hover {
|
||||||
background: linear-gradient(#949494, #9c9c9c);
|
background: linear-gradient(#949494, #9c9c9c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import React from 'react'
|
|||||||
import './BigButton.css'
|
import './BigButton.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode
|
||||||
onClick: () => unknown;
|
onClick: () => unknown
|
||||||
id: string;
|
id: string
|
||||||
disabled?: boolean;
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
disabled?: boolean;
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class BigButton extends React.Component<IProps, IState> {
|
export default class BigButton extends React.Component<IProps, IState> {
|
||||||
@@ -17,7 +17,7 @@ export default class BigButton extends React.Component<IProps, IState> {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
disabled: this.props.disabled
|
disabled: this.props.disabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleClick = this.handleClick.bind(this)
|
this.handleClick = this.handleClick.bind(this)
|
||||||
@@ -25,7 +25,7 @@ export default class BigButton extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
static getDerivedStateFromProps(props: IProps, _state: IState) {
|
static getDerivedStateFromProps(props: IProps, _state: IState) {
|
||||||
return {
|
return {
|
||||||
disabled: props.disabled
|
disabled: props.disabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,9 +37,13 @@ export default class BigButton extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={'BigButton ' + (this.state.disabled ? 'disabled' : '')} onClick={this.handleClick} id={this.props.id}>
|
<div
|
||||||
|
className={'BigButton ' + (this.state.disabled ? 'disabled' : '')}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
id={this.props.id}
|
||||||
|
>
|
||||||
<div className="BigButtonText">{this.props.children}</div>
|
<div className="BigButtonText">{this.props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.Checkbox input[type="checkbox"] {
|
.Checkbox input[type='checkbox'] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
.CheckboxDisplay img {
|
.CheckboxDisplay img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
filter: invert(99%) sepia(0%) saturate(1188%) hue-rotate(186deg) brightness(97%) contrast(67%)
|
filter: invert(99%) sepia(0%) saturate(1188%) hue-rotate(186deg) brightness(97%) contrast(67%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Checkbox label {
|
.Checkbox label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import checkmark from '../../../resources/icons/check.svg'
|
|||||||
import './Checkbox.css'
|
import './Checkbox.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
label?: string,
|
label?: string
|
||||||
checked: boolean,
|
checked: boolean
|
||||||
onChange: () => void,
|
onChange: () => void
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,14 +19,14 @@ export default class Checkbox extends React.Component<IProps, IState> {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
checked: props.checked
|
checked: props.checked,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: IProps, state: IState) {
|
static getDerivedStateFromProps(props: IProps, state: IState) {
|
||||||
if (props.checked !== state.checked) {
|
if (props.checked !== state.checked) {
|
||||||
return {
|
return {
|
||||||
checked: props.checked
|
checked: props.checked,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,14 +41,12 @@ export default class Checkbox extends React.Component<IProps, IState> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="Checkbox">
|
<div className="Checkbox">
|
||||||
<input type='checkbox' id={this.props.id} checked={this.state.checked} onChange={this.handleChange} />
|
<input type="checkbox" id={this.props.id} checked={this.state.checked} onChange={this.handleChange} />
|
||||||
<label htmlFor={this.props.id}>
|
<label htmlFor={this.props.id}>
|
||||||
<div className="CheckboxDisplay">
|
<div className="CheckboxDisplay">{this.state.checked ? <img src={checkmark} alt="Checkmark" /> : null}</div>
|
||||||
{this.state.checked ? <img src={checkmark} alt='Checkmark' /> : null}
|
|
||||||
</div>
|
|
||||||
<span>{this.props.label || ''}</span>
|
<span>{this.props.label || ''}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,4 @@
|
|||||||
|
|
||||||
.FileSelectIcon img {
|
.FileSelectIcon img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface IProps {
|
|||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
folder?: boolean
|
folder?: boolean
|
||||||
customClearBehaviour?: () => void,
|
customClearBehaviour?: () => void
|
||||||
openFolder?: string
|
openFolder?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export default class DirInput extends React.Component<IProps, IState> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
value: props.value || '',
|
value: props.value || '',
|
||||||
placeholder: this.props.placeholder || 'Select file or folder...',
|
placeholder: this.props.placeholder || 'Select file or folder...',
|
||||||
folder: this.props.folder || false
|
folder: this.props.folder || false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleIconClick = this.handleIconClick.bind(this)
|
this.handleIconClick = this.handleIconClick.bind(this)
|
||||||
@@ -54,8 +54,8 @@ export default class DirInput extends React.Component<IProps, IState> {
|
|||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (!this.props.placeholder) {
|
if (!this.props.placeholder) {
|
||||||
const translation = await translate('components.select_file')
|
const translation = await translate('components.select_file')
|
||||||
this.setState( {
|
this.setState({
|
||||||
placeholder: translation
|
placeholder: translation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,15 +65,13 @@ export default class DirInput extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
if (this.state.folder) {
|
if (this.state.folder) {
|
||||||
path = await open({
|
path = await open({
|
||||||
directory: true
|
directory: true,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log(this.props.openFolder)
|
console.log(this.props.openFolder)
|
||||||
path = await open({
|
path = await open({
|
||||||
filters: [
|
filters: [{ name: 'Files', extensions: this.props.extensions || ['*'] }],
|
||||||
{ name: 'Files', extensions: this.props.extensions || ['*'] }
|
defaultPath: this.props.openFolder,
|
||||||
],
|
|
||||||
defaultPath: this.props.openFolder
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +79,7 @@ export default class DirInput extends React.Component<IProps, IState> {
|
|||||||
if (!path) return
|
if (!path) return
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
value: path
|
value: path,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.props.onChange) this.props.onChange(path)
|
if (this.props.onChange) this.props.onChange(path)
|
||||||
@@ -89,12 +87,13 @@ export default class DirInput extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className='DirInput'>
|
<div className="DirInput">
|
||||||
<TextInput
|
<TextInput
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
placeholder={this.state.placeholder}
|
placeholder={this.state.placeholder}
|
||||||
clearable={this.props.clearable !== undefined ? this.props.clearable : true}
|
clearable={this.props.clearable !== undefined ? this.props.clearable : true}
|
||||||
readOnly={this.props.readonly !== undefined ? this.props.readonly : true } onChange={(text: string) => {
|
readOnly={this.props.readonly !== undefined ? this.props.readonly : true}
|
||||||
|
onChange={(text: string) => {
|
||||||
this.setState({ value: text })
|
this.setState({ value: text })
|
||||||
|
|
||||||
if (this.props.onChange) this.props.onChange(text)
|
if (this.props.onChange) this.props.onChange(text)
|
||||||
@@ -108,4 +107,4 @@ export default class DirInput extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,4 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import DownloadSection from './DownloadSection'
|
|||||||
import './DownloadList.css'
|
import './DownloadList.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
downloadManager: DownloadHandler;
|
downloadManager: DownloadHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DownloadList extends React.Component<IProps, never> {
|
export default class DownloadList extends React.Component<IProps, never> {
|
||||||
@@ -16,17 +16,14 @@ export default class DownloadList extends React.Component<IProps, never> {
|
|||||||
render() {
|
render() {
|
||||||
const list = this.props.downloadManager.getDownloads().map((download) => {
|
const list = this.props.downloadManager.getDownloads().map((download) => {
|
||||||
return (
|
return (
|
||||||
<DownloadSection key={download.path} downloadName={download.path} downloadManager={this.props.downloadManager} />
|
<DownloadSection
|
||||||
|
key={download.path}
|
||||||
|
downloadName={download.path}
|
||||||
|
downloadManager={this.props.downloadManager}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return <div className="DownloadList">{list.length > 0 ? list : 'No downloads present'}</div>
|
||||||
return (
|
|
||||||
<div className="DownloadList">
|
|
||||||
{
|
|
||||||
list.length > 0 ? list : 'No downloads present'
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,4 @@
|
|||||||
|
|
||||||
.DownloadStatus {
|
.DownloadStatus {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import ProgressBar from './ProgressBar'
|
|||||||
import './DownloadSection.css'
|
import './DownloadSection.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
downloadManager: DownloadHandler;
|
downloadManager: DownloadHandler
|
||||||
downloadName: string;
|
downloadName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DownloadSection extends React.Component<IProps, never> {
|
export default class DownloadSection extends React.Component<IProps, never> {
|
||||||
@@ -32,4 +32,4 @@ export default class DownloadSection extends React.Component<IProps, never> {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,4 @@
|
|||||||
right: -450%;
|
right: -450%;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Help from '../../../resources/icons/help.svg'
|
|||||||
import MiniDialog from '../MiniDialog'
|
import MiniDialog from '../MiniDialog'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children?: React.ReactNode[] | React.ReactNode;
|
children?: React.ReactNode[] | React.ReactNode
|
||||||
contents?: string
|
contents?: string
|
||||||
id?: string
|
id?: string
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ export default class HelpButton extends React.Component<IProps, IState> {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
opened: false
|
opened: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setOpen = this.setOpen.bind(this)
|
this.setOpen = this.setOpen.bind(this)
|
||||||
@@ -41,14 +41,15 @@ export default class HelpButton extends React.Component<IProps, IState> {
|
|||||||
<img src={Help} />
|
<img src={Help} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="HelpContents" style={{
|
<div
|
||||||
display: this.state.opened ? 'block' : 'none'
|
className="HelpContents"
|
||||||
}}>
|
style={{
|
||||||
<MiniDialog closeFn={this.setClosed}>
|
display: this.state.opened ? 'block' : 'none',
|
||||||
{this.props.contents || this.props.children}
|
}}
|
||||||
</MiniDialog>
|
>
|
||||||
|
<MiniDialog closeFn={this.setClosed}>{this.props.contents || this.props.children}</MiniDialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import Tr from '../../../utils/language'
|
|||||||
import './ProgressBar.css'
|
import './ProgressBar.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
downloadManager: DownloadHandler,
|
downloadManager: DownloadHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
average: number,
|
average: number
|
||||||
files: number,
|
files: number
|
||||||
extracting: number,
|
extracting: number
|
||||||
total: number,
|
total: number
|
||||||
speed: string,
|
speed: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,7 +29,7 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
|||||||
files,
|
files,
|
||||||
extracting,
|
extracting,
|
||||||
total: totalSize,
|
total: totalSize,
|
||||||
speed: '0 B/s'
|
speed: '0 B/s',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,20 +51,23 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
|||||||
return (
|
return (
|
||||||
<div className="MainProgressBarWrapper">
|
<div className="MainProgressBarWrapper">
|
||||||
<div className="ProgressBar">
|
<div className="ProgressBar">
|
||||||
<div className="InnerProgress" style={{
|
<div
|
||||||
width: `${(() => {
|
className="InnerProgress"
|
||||||
// Handles no files downloading
|
style={{
|
||||||
if (this.state.files === 0) {
|
width: `${(() => {
|
||||||
return '100'
|
// Handles no files downloading
|
||||||
}
|
if (this.state.files === 0) {
|
||||||
|
return '100'
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.total <= 0) {
|
if (this.state.total <= 0) {
|
||||||
return '0'
|
return '0'
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.state.average
|
return this.state.average
|
||||||
})()}%`,
|
})()}%`,
|
||||||
}}></div>
|
}}
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="MainProgressText">
|
<div className="MainProgressText">
|
||||||
@@ -75,4 +78,4 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
.ProgressBar, .InnerProgress {
|
.ProgressBar,
|
||||||
|
.InnerProgress {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,4 +92,4 @@
|
|||||||
|
|
||||||
.downloadStop:hover {
|
.downloadStop:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { capitalize } from '../../../utils/string'
|
import { capitalize } from '../../../utils/string'
|
||||||
|
|
||||||
import Stop from '../../../resources/icons/close.svg'
|
import Stop from '../../../resources/icons/close.svg'
|
||||||
import './ProgressBar.css'
|
import './ProgressBar.css'
|
||||||
import DownloadHandler from '../../../utils/download'
|
import DownloadHandler from '../../../utils/download'
|
||||||
import { translate } from '../../../utils/language'
|
import { translate } from '../../../utils/language'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
path: string,
|
path: string
|
||||||
downloadManager: DownloadHandler,
|
downloadManager: DownloadHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
progress: number,
|
progress: number
|
||||||
status: string,
|
status: string
|
||||||
total: number,
|
total: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ProgressBar extends React.Component<IProps, IState> {
|
export default class ProgressBar extends React.Component<IProps, IState> {
|
||||||
@@ -36,7 +36,7 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
|||||||
const prog = this.props.downloadManager.getDownloadProgress(this.props.path)
|
const prog = this.props.downloadManager.getDownloadProgress(this.props.path)
|
||||||
this.setState({
|
this.setState({
|
||||||
progress: prog?.progress || 0,
|
progress: prog?.progress || 0,
|
||||||
status: await translate(`download_status.${prog?.status || 'stopped'}`) || 'stopped',
|
status: (await translate(`download_status.${prog?.status || 'stopped'}`)) || 'stopped',
|
||||||
total: prog?.total || 0,
|
total: prog?.total || 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -54,24 +54,29 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="ProgressBarWrapper">
|
<div className="ProgressBarWrapper">
|
||||||
<div style={{
|
<div
|
||||||
width: '80%'
|
style={{
|
||||||
}}>
|
width: '80%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="ProgressBar">
|
<div className="ProgressBar">
|
||||||
<div className="InnerProgress" style={{
|
<div
|
||||||
width: `${(() => {
|
className="InnerProgress"
|
||||||
// Handles files with content-lengths of 0
|
style={{
|
||||||
if (this.state.status === 'finished') {
|
width: `${(() => {
|
||||||
return '100'
|
// Handles files with content-lengths of 0
|
||||||
}
|
if (this.state.status === 'finished') {
|
||||||
|
return '100'
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.total <= 0) {
|
if (this.state.total <= 0) {
|
||||||
return '0'
|
return '0'
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.state.progress / this.state.total * 100
|
return (this.state.progress / this.state.total) * 100
|
||||||
})()}%`,
|
})()}%`,
|
||||||
}}></div>
|
}}
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="DownloadControls">
|
<div className="DownloadControls">
|
||||||
<div onClick={this.stopDownload} className="downloadStop">
|
<div onClick={this.stopDownload} className="downloadStop">
|
||||||
@@ -80,10 +85,8 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ProgressText">
|
<div className="ProgressText">{capitalize(this.state.status) || 'Waiting'}</div>
|
||||||
{capitalize(this.state.status) || 'Waiting'}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 16%;
|
right: 16%;
|
||||||
|
|
||||||
filter: invert(99%) sepia(0%) saturate(1188%) hue-rotate(186deg) brightness(97%) contrast(67%);
|
filter: invert(99%) sepia(0%) saturate(1188%) hue-rotate(186deg) brightness(97%) contrast(67%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,4 +28,4 @@
|
|||||||
|
|
||||||
.TextInputClear {
|
.TextInputClear {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import './TextInput.css'
|
|||||||
import Close from '../../../resources/icons/close.svg'
|
import Close from '../../../resources/icons/close.svg'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
value?: string;
|
value?: string
|
||||||
initalValue?: string;
|
initalValue?: string
|
||||||
placeholder?: string;
|
placeholder?: string
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void
|
||||||
readOnly?: boolean;
|
readOnly?: boolean
|
||||||
id?: string;
|
id?: string
|
||||||
clearable?: boolean;
|
clearable?: boolean
|
||||||
customClearBehaviour?: () => void;
|
customClearBehaviour?: () => void
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
@@ -24,14 +24,14 @@ export default class TextInput extends React.Component<IProps, IState> {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
value: props.value || ''
|
value: props.value || '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (this.props.initalValue) {
|
if (this.props.initalValue) {
|
||||||
this.setState({
|
this.setState({
|
||||||
value: this.props.initalValue
|
value: this.props.initalValue,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,26 +43,35 @@ export default class TextInput extends React.Component<IProps, IState> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="TextInputWrapper" style={this.props.style || {}}>
|
<div className="TextInputWrapper" style={this.props.style || {}}>
|
||||||
<input id={this.props?.id} readOnly={this.props.readOnly || false} placeholder={this.props.placeholder || ''} className="TextInput" value={this.state.value} onChange={(e) => {
|
<input
|
||||||
this.setState({ value: e.target.value })
|
id={this.props?.id}
|
||||||
if (this.props.onChange) this.props.onChange(e.target.value)
|
readOnly={this.props.readOnly || false}
|
||||||
}} />
|
placeholder={this.props.placeholder || ''}
|
||||||
{
|
className="TextInput"
|
||||||
this.props.clearable ?
|
value={this.state.value}
|
||||||
<div className="TextClear" onClick={() => {
|
onChange={(e) => {
|
||||||
|
this.setState({ value: e.target.value })
|
||||||
|
if (this.props.onChange) this.props.onChange(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{this.props.clearable ? (
|
||||||
|
<div
|
||||||
|
className="TextClear"
|
||||||
|
onClick={() => {
|
||||||
// Run custom behaviour first
|
// Run custom behaviour first
|
||||||
if (this.props.customClearBehaviour) return this.props.customClearBehaviour()
|
if (this.props.customClearBehaviour) return this.props.customClearBehaviour()
|
||||||
|
|
||||||
this.setState({ value: '' })
|
this.setState({ value: '' })
|
||||||
|
|
||||||
if (this.props.onChange) this.props.onChange('')
|
if (this.props.onChange) this.props.onChange('')
|
||||||
|
|
||||||
this.forceUpdate()
|
this.forceUpdate()
|
||||||
}}>
|
}}
|
||||||
<img src={Close} className="TextInputClear" />
|
>
|
||||||
</div> : null
|
<img src={Close} className="TextInputClear" />
|
||||||
}
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
.Divider {
|
.Divider {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -13,4 +12,4 @@
|
|||||||
.DividerLine {
|
.DividerLine {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ export default class Divider extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,4 @@
|
|||||||
|
|
||||||
.DownloadMenuSection .HelpButton img {
|
.DownloadMenuSection .HelpButton img {
|
||||||
filter: none;
|
filter: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const DEV_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/bu
|
|||||||
const RESOURCES_DOWNLOAD = 'https://gitlab.com/yukiz/GrasscutterResources/-/archive/2.8/GrasscutterResources-2.8.zip'
|
const RESOURCES_DOWNLOAD = 'https://gitlab.com/yukiz/GrasscutterResources/-/archive/2.8/GrasscutterResources-2.8.zip'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeFn: () => void;
|
closeFn: () => void
|
||||||
downloadManager: DownloadHandler;
|
downloadManager: DownloadHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
@@ -41,7 +41,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
resources_downloading: this.props.downloadManager.downloadingResources(),
|
resources_downloading: this.props.downloadManager.downloadingResources(),
|
||||||
repo_downloading: this.props.downloadManager.downloadingRepo(),
|
repo_downloading: this.props.downloadManager.downloadingRepo(),
|
||||||
grasscutter_set: false,
|
grasscutter_set: false,
|
||||||
resources_exist: false
|
resources_exist: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getGrasscutterFolder = this.getGrasscutterFolder.bind(this)
|
this.getGrasscutterFolder = this.getGrasscutterFolder.bind(this)
|
||||||
@@ -63,7 +63,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
if (!gc_path || gc_path === '') {
|
if (!gc_path || gc_path === '') {
|
||||||
this.setState({
|
this.setState({
|
||||||
grasscutter_set: false,
|
grasscutter_set: false,
|
||||||
resources_exist: false
|
resources_exist: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -72,15 +72,17 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
const path = gc_path.substring(0, gc_path.lastIndexOf('\\'))
|
const path = gc_path.substring(0, gc_path.lastIndexOf('\\'))
|
||||||
|
|
||||||
if (gc_path) {
|
if (gc_path) {
|
||||||
const resources_exist: boolean = await invoke('dir_exists', {
|
const resources_exist: boolean =
|
||||||
path: path + '\\resources'
|
((await invoke('dir_exists', {
|
||||||
}) as boolean && !(await invoke('dir_is_empty', {
|
path: path + '\\resources',
|
||||||
path: path + '\\resources'
|
})) as boolean) &&
|
||||||
})) as boolean
|
(!(await invoke('dir_is_empty', {
|
||||||
|
path: path + '\\resources',
|
||||||
|
})) as boolean)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
grasscutter_set: gc_path !== '',
|
grasscutter_set: gc_path !== '',
|
||||||
resources_exist
|
resources_exist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +111,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
async downloadGrasscutterStableRepo() {
|
async downloadGrasscutterStableRepo() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(STABLE_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', () =>{
|
this.props.downloadManager.addDownload(STABLE_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', () => {
|
||||||
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', this.toggleButtons)
|
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', this.toggleButtons)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -118,7 +120,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
async downloadGrasscutterDevRepo() {
|
async downloadGrasscutterDevRepo() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(DEV_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', () =>{
|
this.props.downloadManager.addDownload(DEV_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', () => {
|
||||||
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', this.toggleButtons)
|
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', this.toggleButtons)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -127,7 +129,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
async downloadGrasscutterStable() {
|
async downloadGrasscutterStable() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(STABLE_DOWNLOAD, folder + '\\grasscutter.zip', () =>{
|
this.props.downloadManager.addDownload(STABLE_DOWNLOAD, folder + '\\grasscutter.zip', () => {
|
||||||
unzip(folder + '\\grasscutter.zip', folder + '\\', this.toggleButtons)
|
unzip(folder + '\\grasscutter.zip', folder + '\\', this.toggleButtons)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -135,11 +137,11 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
this.downloadGrasscutterStableRepo()
|
this.downloadGrasscutterStableRepo()
|
||||||
|
|
||||||
this.toggleButtons()
|
this.toggleButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadGrasscutterLatest() {
|
async downloadGrasscutterLatest() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(DEV_DOWNLOAD, folder + '\\grasscutter.zip', () =>{
|
this.props.downloadManager.addDownload(DEV_DOWNLOAD, folder + '\\grasscutter.zip', () => {
|
||||||
unzip(folder + '\\grasscutter.zip', folder + '\\', this.toggleButtons)
|
unzip(folder + '\\grasscutter.zip', folder + '\\', this.toggleButtons)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -152,12 +154,14 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
async downloadResources() {
|
async downloadResources() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(RESOURCES_DOWNLOAD, folder + '\\resources.zip', async () => {
|
this.props.downloadManager.addDownload(RESOURCES_DOWNLOAD, folder + '\\resources.zip', async () => {
|
||||||
// Delete the existing folder if it exists
|
// Delete the existing folder if it exists
|
||||||
if (await invoke('dir_exists', {
|
if (
|
||||||
path: folder + '\\resources'
|
await invoke('dir_exists', {
|
||||||
})) {
|
path: folder + '\\resources',
|
||||||
|
})
|
||||||
|
) {
|
||||||
await invoke('dir_delete', {
|
await invoke('dir_delete', {
|
||||||
path: folder + '\\resources'
|
path: folder + '\\resources',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +169,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
// Rename folder to resources
|
// Rename folder to resources
|
||||||
invoke('rename', {
|
invoke('rename', {
|
||||||
path: folder + '\\Resources',
|
path: folder + '\\Resources',
|
||||||
newName: 'resources'
|
newName: 'resources',
|
||||||
})
|
})
|
||||||
|
|
||||||
this.toggleButtons()
|
this.toggleButtons()
|
||||||
@@ -190,32 +194,40 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Menu closeFn={this.props.closeFn} className="Downloads" heading="Downloads">
|
<Menu closeFn={this.props.closeFn} className="Downloads" heading="Downloads">
|
||||||
<div className='DownloadMenuSection' id="downloadMenuContainerGCStable">
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCStable">
|
||||||
<div className='DownloadLabel' id="downloadMenuLabelGCStable">
|
<div className="DownloadLabel" id="downloadMenuLabelGCStable">
|
||||||
<Tr text={
|
<Tr
|
||||||
this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'
|
text={this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'}
|
||||||
} />
|
/>
|
||||||
<HelpButton>
|
<HelpButton>
|
||||||
<Tr text="help.gc_stable_jar" />
|
<Tr text="help.gc_stable_jar" />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadValue' id="downloadMenuButtonGCStable">
|
<div className="DownloadValue" id="downloadMenuButtonGCStable">
|
||||||
<BigButton disabled={this.state.grasscutter_downloading} onClick={this.downloadGrasscutterStable} id="grasscutterStableBtn" >
|
<BigButton
|
||||||
|
disabled={this.state.grasscutter_downloading}
|
||||||
|
onClick={this.downloadGrasscutterStable}
|
||||||
|
id="grasscutterStableBtn"
|
||||||
|
>
|
||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadMenuSection' id="downloadMenuContainerGCDev">
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCDev">
|
||||||
<div className='DownloadLabel' id="downloadMenuLabelGCDev">
|
<div className="DownloadLabel" id="downloadMenuLabelGCDev">
|
||||||
<Tr text={
|
<Tr
|
||||||
this.state.grasscutter_set ? 'downloads.grasscutter_latest' : 'downloads.grasscutter_latest_update'
|
text={this.state.grasscutter_set ? 'downloads.grasscutter_latest' : 'downloads.grasscutter_latest_update'}
|
||||||
} />
|
/>
|
||||||
<HelpButton>
|
<HelpButton>
|
||||||
<Tr text="help.gc_dev_jar" />
|
<Tr text="help.gc_dev_jar" />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadValue' id="downloadMenuButtonGCDev">
|
<div className="DownloadValue" id="downloadMenuButtonGCDev">
|
||||||
<BigButton disabled={this.state.grasscutter_downloading} onClick={this.downloadGrasscutterLatest} id="grasscutterLatestBtn" >
|
<BigButton
|
||||||
|
disabled={this.state.grasscutter_downloading}
|
||||||
|
onClick={this.downloadGrasscutterLatest}
|
||||||
|
id="grasscutterLatestBtn"
|
||||||
|
>
|
||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,32 +235,48 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<div className='DownloadMenuSection' id="downloadMenuContainerGCStableData">
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCStableData">
|
||||||
<div className='DownloadLabel' id="downloadMenuLabelGCStableData">
|
<div className="DownloadLabel" id="downloadMenuLabelGCStableData">
|
||||||
<Tr text={
|
<Tr
|
||||||
this.state.grasscutter_set ? 'downloads.grasscutter_stable_data' : 'downloads.grasscutter_stable_data_update'
|
text={
|
||||||
} />
|
this.state.grasscutter_set
|
||||||
|
? 'downloads.grasscutter_stable_data'
|
||||||
|
: 'downloads.grasscutter_stable_data_update'
|
||||||
|
}
|
||||||
|
/>
|
||||||
<HelpButton>
|
<HelpButton>
|
||||||
<Tr text="help.gc_stable_data" />
|
<Tr text="help.gc_stable_data" />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadValue' id="downloadMenuButtonGCStableData">
|
<div className="DownloadValue" id="downloadMenuButtonGCStableData">
|
||||||
<BigButton disabled={this.state.repo_downloading} onClick={this.downloadGrasscutterStableRepo} id="grasscutterStableRepo" >
|
<BigButton
|
||||||
|
disabled={this.state.repo_downloading}
|
||||||
|
onClick={this.downloadGrasscutterStableRepo}
|
||||||
|
id="grasscutterStableRepo"
|
||||||
|
>
|
||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadMenuSection' id="downloadMenuContainerGCDevData">
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCDevData">
|
||||||
<div className='DownloadLabel' id="downloadMenuLabelGCDevData">
|
<div className="DownloadLabel" id="downloadMenuLabelGCDevData">
|
||||||
<Tr text={
|
<Tr
|
||||||
this.state.grasscutter_set ? 'downloads.grasscutter_latest_data' : 'downloads.grasscutter_latest_data_update'
|
text={
|
||||||
} />
|
this.state.grasscutter_set
|
||||||
|
? 'downloads.grasscutter_latest_data'
|
||||||
|
: 'downloads.grasscutter_latest_data_update'
|
||||||
|
}
|
||||||
|
/>
|
||||||
<HelpButton>
|
<HelpButton>
|
||||||
<Tr text="help.gc_dev_data" />
|
<Tr text="help.gc_dev_data" />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadValue' id="downloadMenuButtonGCDevData">
|
<div className="DownloadValue" id="downloadMenuButtonGCDevData">
|
||||||
<BigButton disabled={this.state.repo_downloading} onClick={this.downloadGrasscutterStableRepo} id="grasscutterDevRepo" >
|
<BigButton
|
||||||
|
disabled={this.state.repo_downloading}
|
||||||
|
onClick={this.downloadGrasscutterStableRepo}
|
||||||
|
id="grasscutterDevRepo"
|
||||||
|
>
|
||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -256,15 +284,19 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<div className='DownloadMenuSection' id="downloadMenuContainerResources">
|
<div className="DownloadMenuSection" id="downloadMenuContainerResources">
|
||||||
<div className='DownloadLabel' id="downloadMenuLabelResources">
|
<div className="DownloadLabel" id="downloadMenuLabelResources">
|
||||||
<Tr text="downloads.resources" />
|
<Tr text="downloads.resources" />
|
||||||
<HelpButton>
|
<HelpButton>
|
||||||
<Tr text="help.resources" />
|
<Tr text="help.resources" />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</div>
|
</div>
|
||||||
<div className='DownloadValue' id="downloadMenuButtonResources">
|
<div className="DownloadValue" id="downloadMenuButtonResources">
|
||||||
<BigButton disabled={this.state.resources_downloading || !this.state.grasscutter_set || this.state.resources_exist} onClick={this.downloadResources} id="resourcesBtn" >
|
<BigButton
|
||||||
|
disabled={this.state.resources_downloading || !this.state.grasscutter_set || this.state.resources_exist}
|
||||||
|
onClick={this.downloadResources}
|
||||||
|
id="resourcesBtn"
|
||||||
|
>
|
||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,4 +304,4 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
</Menu>
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,4 @@
|
|||||||
|
|
||||||
.GameDownloadDir .DirInput .TextInputWrapper input {
|
.GameDownloadDir .DirInput .TextInputWrapper input {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import { unzip } from '../../../utils/zipUtils'
|
|||||||
const GAME_DOWNLOAD = ''
|
const GAME_DOWNLOAD = ''
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeFn: () => void;
|
closeFn: () => void
|
||||||
downloadManager: DownloadHandler;
|
downloadManager: DownloadHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
gameDownloading: boolean;
|
gameDownloading: boolean
|
||||||
gameDownloadFolder: string;
|
gameDownloadFolder: string
|
||||||
dirPlaceholder: string;
|
dirPlaceholder: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Downloads extends React.Component<IProps, IState> {
|
export default class Downloads extends React.Component<IProps, IState> {
|
||||||
@@ -29,7 +29,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
gameDownloading: false,
|
gameDownloading: false,
|
||||||
gameDownloadFolder: '',
|
gameDownloadFolder: '',
|
||||||
dirPlaceholder: ''
|
dirPlaceholder: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
this.downloadGame = this.downloadGame.bind(this)
|
this.downloadGame = this.downloadGame.bind(this)
|
||||||
@@ -37,7 +37,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.setState({
|
this.setState({
|
||||||
dirPlaceholder: await translate('components.select_folder')
|
dirPlaceholder: await translate('components.select_folder'),
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(this.state)
|
console.log(this.state)
|
||||||
@@ -45,39 +45,51 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
async downloadGame() {
|
async downloadGame() {
|
||||||
const folder = this.state.gameDownloadFolder
|
const folder = this.state.gameDownloadFolder
|
||||||
this.props.downloadManager.addDownload(GAME_DOWNLOAD, folder + '\\game.zip', () =>{
|
this.props.downloadManager.addDownload(GAME_DOWNLOAD, folder + '\\game.zip', () => {
|
||||||
unzip(folder + '\\game.zip', folder + '\\', () => {
|
unzip(folder + '\\game.zip', folder + '\\', () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
gameDownloading: false
|
gameDownloading: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
gameDownloading: true
|
gameDownloading: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Menu heading='Download Game' closeFn={this.props.closeFn} className="GameDownloadMenu">
|
<Menu heading="Download Game" closeFn={this.props.closeFn} className="GameDownloadMenu">
|
||||||
<div className="GameDownload">
|
<div className="GameDownload">
|
||||||
{
|
{this.state.gameDownloadFolder !== '' && !this.state.gameDownloading ? (
|
||||||
this.state.gameDownloadFolder !== '' && !this.state.gameDownloading ?
|
<BigButton id="downloadGameBtn" onClick={this.downloadGame}>
|
||||||
<BigButton id="downloadGameBtn" onClick={this.downloadGame}>Download Game</BigButton>
|
Download Game
|
||||||
: <BigButton id="disabledGameBtn" onClick={() => null} disabled>Download Game</BigButton>
|
</BigButton>
|
||||||
}
|
) : (
|
||||||
|
<BigButton id="disabledGameBtn" onClick={() => null} disabled>
|
||||||
|
Download Game
|
||||||
|
</BigButton>
|
||||||
|
)}
|
||||||
<HelpButton>
|
<HelpButton>
|
||||||
<Tr text="main.game_help_text" />
|
<Tr text="main.game_help_text" />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="GameDownloadDir">
|
<div className="GameDownloadDir">
|
||||||
<DirInput folder placeholder={this.state.dirPlaceholder} clearable={false} readonly={true} onChange={(value: string) => this.setState({
|
<DirInput
|
||||||
gameDownloadFolder: value
|
folder
|
||||||
})}/>
|
placeholder={this.state.dirPlaceholder}
|
||||||
|
clearable={false}
|
||||||
|
readonly={true}
|
||||||
|
onChange={(value: string) =>
|
||||||
|
this.setState({
|
||||||
|
gameDownloadFolder: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Menu>
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
||||||
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -52,4 +52,4 @@
|
|||||||
|
|
||||||
.MenuExit img {
|
.MenuExit img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import './Menu.css'
|
|||||||
import Close from '../../../resources/icons/close.svg'
|
import Close from '../../../resources/icons/close.svg'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children: React.ReactNode[] | React.ReactNode;
|
children: React.ReactNode[] | React.ReactNode
|
||||||
className?: string;
|
className?: string
|
||||||
heading: string;
|
heading: string
|
||||||
closeFn: () => void;
|
closeFn: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Menu extends React.Component<IProps, never> {
|
export default class Menu extends React.Component<IProps, never> {
|
||||||
@@ -18,16 +18,18 @@ export default class Menu extends React.Component<IProps, never> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={'Menu ' + this.props.className} id="menuContainer">
|
<div className={'Menu ' + this.props.className} id="menuContainer">
|
||||||
<div className='MenuTop' id="menuContainerTop">
|
<div className="MenuTop" id="menuContainerTop">
|
||||||
<div className="MenuHeading" id="menuHeading">{this.props.heading}</div>
|
<div className="MenuHeading" id="menuHeading">
|
||||||
|
{this.props.heading}
|
||||||
|
</div>
|
||||||
<div className="MenuExit" id="menuButtonCloseContainer" onClick={this.props.closeFn}>
|
<div className="MenuExit" id="menuButtonCloseContainer" onClick={this.props.closeFn}>
|
||||||
<img src={Close} className="MenuClose" id="menuButtonCloseIcon" />
|
<img src={Close} className="MenuClose" id="menuButtonCloseIcon" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='MenuInner' id="menuContent">
|
<div className="MenuInner" id="menuContent">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,4 +15,4 @@
|
|||||||
|
|
||||||
.OptionSection .BigButtonText {
|
.OptionSection .BigButtonText {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import DownloadHandler from '../../../utils/download'
|
|||||||
import * as meta from '../../../utils/metadata'
|
import * as meta from '../../../utils/metadata'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeFn: () => void;
|
closeFn: () => void
|
||||||
downloadManager: DownloadHandler;
|
downloadManager: DownloadHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
@@ -25,7 +25,7 @@ interface IState {
|
|||||||
grasscutter_path: string
|
grasscutter_path: string
|
||||||
java_path: string
|
java_path: string
|
||||||
grasscutter_with_game: boolean
|
grasscutter_with_game: boolean
|
||||||
language_options: { [key: string]: string }[],
|
language_options: { [key: string]: string }[]
|
||||||
current_language: string
|
current_language: string
|
||||||
bg_url_or_path: string
|
bg_url_or_path: string
|
||||||
themes: string[]
|
themes: string[]
|
||||||
@@ -61,7 +61,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
// Swag stuff
|
// Swag stuff
|
||||||
akebi_path: '',
|
akebi_path: '',
|
||||||
migoto_path: ''
|
migoto_path: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setGameExecutable = this.setGameExecutable.bind(this)
|
this.setGameExecutable = this.setGameExecutable.bind(this)
|
||||||
@@ -136,7 +136,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
setConfigOption('akebi_path', value)
|
setConfigOption('akebi_path', value)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
akebi_path: value
|
akebi_path: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
setConfigOption('migoto_path', value)
|
setConfigOption('migoto_path', value)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
migoto_path: value
|
migoto_path: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,10 +217,10 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
console.log(this.props)
|
console.log(this.props)
|
||||||
await meta.restoreMetadata(this.props.downloadManager)
|
await meta.restoreMetadata(this.props.downloadManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
async installCert() {
|
async installCert() {
|
||||||
await invoke('generate_ca_files', {
|
await invoke('generate_ca_files', {
|
||||||
path: await dataDir() + 'cultivation'
|
path: (await dataDir()) + 'cultivation',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
|
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
|
||||||
<BigButton onClick={this.restoreMetadata} id="metaDownload">
|
<BigButton onClick={this.restoreMetadata} id="metaDownload">
|
||||||
<Tr text='components.download' />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -270,11 +270,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
<Tr text="options.patch_metadata" />
|
<Tr text="options.patch_metadata" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
|
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
|
||||||
<Checkbox
|
<Checkbox onChange={this.toggleMetadata} checked={this.state?.patch_metadata} id="patchMeta" />
|
||||||
onChange={this.toggleMetadata}
|
|
||||||
checked={this.state?.patch_metadata}
|
|
||||||
id="patchMeta"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionSection" id="menuOptionsContainerUseProxy">
|
<div className="OptionSection" id="menuOptionsContainerUseProxy">
|
||||||
@@ -282,18 +278,14 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
<Tr text="options.use_proxy" />
|
<Tr text="options.use_proxy" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsCheckboxUseProxy">
|
<div className="OptionValue" id="menuOptionsCheckboxUseProxy">
|
||||||
<Checkbox
|
<Checkbox onChange={this.toggleProxy} checked={this.state?.use_internal_proxy} id="useProxy" />
|
||||||
onChange={this.toggleProxy}
|
|
||||||
checked={this.state?.use_internal_proxy}
|
|
||||||
id="useProxy"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<div className='OptionSection' id="menuOptionsContainerGCJar">
|
<div className="OptionSection" id="menuOptionsContainerGCJar">
|
||||||
<div className='OptionLabel' id="menuOptionsLabelGCJar">
|
<div className="OptionLabel" id="menuOptionsLabelGCJar">
|
||||||
<Tr text="options.grasscutter_jar" />
|
<Tr text="options.grasscutter_jar" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsDirGCJar">
|
<div className="OptionValue" id="menuOptionsDirGCJar">
|
||||||
@@ -310,39 +302,34 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='OptionSection' id="menuOptionsContainerInstallCert">
|
<div className="OptionSection" id="menuOptionsContainerInstallCert">
|
||||||
<div className='OptionLabel' id="menuOptionsLabelInstallCert">
|
<div className="OptionLabel" id="menuOptionsLabelInstallCert">
|
||||||
<Tr text="options.install_certificate" />
|
<Tr text="options.install_certificate" />
|
||||||
</div>
|
</div>
|
||||||
<div className='OptionValue' id="menuOptionsButtonInstallCert">
|
<div className="OptionValue" id="menuOptionsButtonInstallCert">
|
||||||
<BigButton disabled={false} onClick={this.installCert} id="installCert">
|
<BigButton disabled={false} onClick={this.installCert} id="installCert">
|
||||||
<Tr text="components.install" />
|
<Tr text="components.install" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
{this.state.swag && (
|
||||||
this.state.swag && (
|
<>
|
||||||
<>
|
<Divider />
|
||||||
<Divider />
|
<div className="OptionSection" id="menuOptionsContainerAkebi">
|
||||||
<div className='OptionSection' id="menuOptionsContainerAkebi">
|
<div className="OptionLabel" id="menuOptionsLabelAkebi">
|
||||||
<div className='OptionLabel' id="menuOptionsLabelAkebi">
|
<Tr text="swag.akebi" />
|
||||||
<Tr text="swag.akebi" />
|
|
||||||
</div>
|
|
||||||
<div className='OptionValue' id="menuOptionsDirAkebi">
|
|
||||||
<DirInput onChange={this.setAkebi} value={this.state?.akebi_path} extensions={['exe']} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='OptionSection' id="menuOptionsContainerMigoto">
|
<div className="OptionSection" id="menuOptionsContainerMigoto">
|
||||||
<div className='OptionLabel' id="menuOptionsLabelMigoto">
|
<div className="OptionLabel" id="menuOptionsLabelMigoto">
|
||||||
<Tr text="swag.migoto" />
|
<Tr text="swag.migoto" />
|
||||||
</div>
|
</div>
|
||||||
<div className='OptionValue' id="menuOptionsDirMigoto">
|
<div className="OptionValue" id="menuOptionsDirMigoto">
|
||||||
<DirInput onChange={this.setMigoto} value={this.state?.migoto_path} extensions={['exe']} />
|
<DirInput onChange={this.setMigoto} value={this.state?.migoto_path} extensions={['exe']} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)
|
</>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
|||||||
@@ -1,97 +1,97 @@
|
|||||||
.NewsSection {
|
.NewsSection {
|
||||||
background-color: rgba(106, 105, 106, 0.6);
|
background-color: rgba(106, 105, 106, 0.6);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
min-height: 219px;
|
min-height: 219px;
|
||||||
height: 40%;
|
height: 40%;
|
||||||
width: 512px;
|
width: 512px;
|
||||||
|
|
||||||
bottom: 35%;
|
bottom: 35%;
|
||||||
left: 5%;
|
left: 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 830px) {
|
@media (max-width: 830px) {
|
||||||
.NewsSection {
|
.NewsSection {
|
||||||
width: 61%;
|
width: 61%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsTabs {
|
.NewsTabs {
|
||||||
background-color: rgba(77, 77, 77, 0.6);
|
background-color: rgba(77, 77, 77, 0.6);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 43px;
|
height: 43px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsTab {
|
.NewsTab {
|
||||||
height: 50%;
|
height: 50%;
|
||||||
|
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsTab:hover {
|
.NewsTab:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsTab.selected {
|
.NewsTab.selected {
|
||||||
border-bottom: 2px solid #ffc61e;
|
border-bottom: 2px solid #ffc61e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsContent {
|
.NewsContent {
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(100% - 43px);
|
height: calc(100% - 43px);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsContent tbody {
|
.NewsContent tbody {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewsContent tbody::-webkit-scrollbar {
|
.NewsContent tbody::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Commit {
|
.Commit {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
max-height: 42px;
|
max-height: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CommitAuthor {
|
.CommitAuthor {
|
||||||
width: calc(100% * 0.4);
|
width: calc(100% * 0.4);
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CommitMessage {
|
.CommitMessage {
|
||||||
width: calc(100% * 0.6);
|
width: calc(100% * 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.CommitMessage span {
|
.CommitMessage span {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 42px;
|
max-height: 42px;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,33 +6,33 @@ import Tr from '../../../utils/language'
|
|||||||
import './NewsSection.css'
|
import './NewsSection.css'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
selected?: string;
|
selected?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
selected: string;
|
selected: string
|
||||||
news?: JSX.Element;
|
news?: JSX.Element
|
||||||
commitList?: JSX.Element[];
|
commitList?: JSX.Element[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GrasscutterAPIResponse {
|
interface GrasscutterAPIResponse {
|
||||||
commits: {
|
commits: {
|
||||||
gc_stable: CommitResponse[];
|
gc_stable: CommitResponse[]
|
||||||
gc_dev: CommitResponse[];
|
gc_dev: CommitResponse[]
|
||||||
cultivation: CommitResponse[];
|
cultivation: CommitResponse[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommitResponse {
|
interface CommitResponse {
|
||||||
sha: string;
|
sha: string
|
||||||
commit: Commit;
|
commit: Commit
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Commit {
|
interface Commit {
|
||||||
author: {
|
author: {
|
||||||
name: string;
|
name: string
|
||||||
};
|
}
|
||||||
message: string;
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class NewsSection extends React.Component<IProps, IState> {
|
export default class NewsSection extends React.Component<IProps, IState> {
|
||||||
@@ -65,14 +65,16 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
grasscutterApiResponse = JSON.parse(response)
|
grasscutterApiResponse = JSON.parse(response)
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
grasscutterApiResponse = null
|
grasscutterApiResponse = null
|
||||||
}
|
}
|
||||||
|
|
||||||
let commits: CommitResponse[]
|
let commits: CommitResponse[]
|
||||||
if (grasscutterApiResponse?.commits == null) {
|
if (grasscutterApiResponse?.commits == null) {
|
||||||
// If it didn't work, use official API
|
// If it didn't work, use official API
|
||||||
const response: string = await invoke('req_get', { url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits' })
|
const response: string = await invoke('req_get', {
|
||||||
|
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
|
||||||
|
})
|
||||||
commits = JSON.parse(response)
|
commits = JSON.parse(response)
|
||||||
} else {
|
} else {
|
||||||
commits = grasscutterApiResponse.commits.gc_stable
|
commits = grasscutterApiResponse.commits.gc_stable
|
||||||
@@ -80,21 +82,25 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
// Probably rate-limited
|
// Probably rate-limited
|
||||||
if (!Array.isArray(commits)) return
|
if (!Array.isArray(commits)) return
|
||||||
|
|
||||||
// Get only first 5
|
// Get only first 5
|
||||||
const commitsList = commits.slice(0, 10)
|
const commitsList = commits.slice(0, 10)
|
||||||
const commitsListHtml = commitsList.map((commitResponse: CommitResponse) => {
|
const commitsListHtml = commitsList.map((commitResponse: CommitResponse) => {
|
||||||
return (
|
return (
|
||||||
<tr className="Commit" id="newsCommitsTable" key={commitResponse.sha}>
|
<tr className="Commit" id="newsCommitsTable" key={commitResponse.sha}>
|
||||||
<td className="CommitAuthor"><span>{commitResponse.commit.author.name}</span></td>
|
<td className="CommitAuthor">
|
||||||
<td className="CommitMessage"><span>{commitResponse.commit.message}</span></td>
|
<span>{commitResponse.commit.author.name}</span>
|
||||||
|
</td>
|
||||||
|
<td className="CommitMessage">
|
||||||
|
<span>{commitResponse.commit.message}</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
commitList: commitsListHtml,
|
commitList: commitsListHtml,
|
||||||
news: <>{commitsListHtml}</>
|
news: <>{commitsListHtml}</>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +110,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
async showNews() {
|
async showNews() {
|
||||||
let news: JSX.Element | JSX.Element[] = <tr></tr>
|
let news: JSX.Element | JSX.Element[] = <tr></tr>
|
||||||
|
|
||||||
switch(this.state.selected) {
|
switch (this.state.selected) {
|
||||||
case 'commits': {
|
case 'commits': {
|
||||||
const commits = await this.showLatestCommits()
|
const commits = await this.showLatestCommits()
|
||||||
if (commits != null) {
|
if (commits != null) {
|
||||||
@@ -114,16 +120,24 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'latest_version':
|
case 'latest_version':
|
||||||
news = <tr><td>Latest version</td></tr>
|
news = (
|
||||||
|
<tr>
|
||||||
|
<td>Latest version</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
news = <tr><td>Unknown</td></tr>
|
news = (
|
||||||
|
<tr>
|
||||||
|
<td>Unknown</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
news: <>{news}</>
|
news: <>{news}</>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,19 +145,25 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
return (
|
return (
|
||||||
<div className="NewsSection" id="newsContainer">
|
<div className="NewsSection" id="newsContainer">
|
||||||
<div className="NewsTabs" id="newsTabsContainer">
|
<div className="NewsTabs" id="newsTabsContainer">
|
||||||
<div className={'NewsTab ' + (this.state.selected === 'commits' ? 'selected' : '')} id="commits" onClick={() => this.setSelected('commits')}>
|
<div
|
||||||
|
className={'NewsTab ' + (this.state.selected === 'commits' ? 'selected' : '')}
|
||||||
|
id="commits"
|
||||||
|
onClick={() => this.setSelected('commits')}
|
||||||
|
>
|
||||||
<Tr text="news.latest_commits" />
|
<Tr text="news.latest_commits" />
|
||||||
</div>
|
</div>
|
||||||
<div className={'NewsTab ' + (this.state.selected === 'latest_version' ? 'selected' : '')} id="latest_version" onClick={() => this.setSelected('latest_version')}>
|
<div
|
||||||
|
className={'NewsTab ' + (this.state.selected === 'latest_version' ? 'selected' : '')}
|
||||||
|
id="latest_version"
|
||||||
|
onClick={() => this.setSelected('latest_version')}
|
||||||
|
>
|
||||||
<Tr text="news.latest_version" />
|
<Tr text="news.latest_version" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table className="NewsContent" id="newsContent">
|
<table className="NewsContent" id="newsContent">
|
||||||
<tbody>
|
<tbody>{this.state.news}</tbody>
|
||||||
{this.state.news}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { dataDir } from '@tauri-apps/api/path'
|
|||||||
|
|
||||||
let configFilePath: string
|
let configFilePath: string
|
||||||
let defaultConfig: Configuration
|
let defaultConfig: Configuration
|
||||||
|
;(async () => {
|
||||||
(async() => {
|
|
||||||
defaultConfig = {
|
defaultConfig = {
|
||||||
toggle_grasscutter: false,
|
toggle_grasscutter: false,
|
||||||
game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game\\GenshinImpact.exe',
|
game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game\\GenshinImpact.exe',
|
||||||
@@ -57,8 +56,8 @@ export interface Configuration {
|
|||||||
export async function setConfigOption<K extends keyof Configuration>(key: K, value: Configuration[K]): Promise<void> {
|
export async function setConfigOption<K extends keyof Configuration>(key: K, value: Configuration[K]): Promise<void> {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
config[key] = value
|
config[key] = value
|
||||||
|
|
||||||
await saveConfig(<Configuration> config)
|
await saveConfig(<Configuration>config)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getConfigOption<K extends keyof Configuration>(key: K): Promise<Configuration[K]> {
|
export async function getConfigOption<K extends keyof Configuration>(key: K): Promise<Configuration[K]> {
|
||||||
@@ -71,13 +70,13 @@ export async function getConfigOption<K extends keyof Configuration>(key: K): Pr
|
|||||||
export async function getConfig() {
|
export async function getConfig() {
|
||||||
const raw = await readConfigFile()
|
const raw = await readConfigFile()
|
||||||
let parsed: Configuration = defaultConfig
|
let parsed: Configuration = defaultConfig
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parsed = <Configuration> JSON.parse(raw)
|
parsed = <Configuration>JSON.parse(raw)
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
// We could not open the file
|
// We could not open the file
|
||||||
console.log(e)
|
console.log(e)
|
||||||
|
|
||||||
// TODO: Create a popup saying the config file is corrupted.
|
// TODO: Create a popup saying the config file is corrupted.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +99,7 @@ async function readConfigFile() {
|
|||||||
|
|
||||||
if (!dirs.find((fileOrDir) => fileOrDir?.name === 'cultivation')) {
|
if (!dirs.find((fileOrDir) => fileOrDir?.name === 'cultivation')) {
|
||||||
// Create dir
|
// Create dir
|
||||||
await fs.createDir(local + 'cultivation').catch(e => console.log(e))
|
await fs.createDir(local + 'cultivation').catch((e) => console.log(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
const innerDirs = await fs.readDir(local + '/cultivation')
|
const innerDirs = await fs.readDir(local + '/cultivation')
|
||||||
@@ -108,7 +107,7 @@ async function readConfigFile() {
|
|||||||
// Create grasscutter dir for potential installation
|
// Create grasscutter dir for potential installation
|
||||||
if (!innerDirs.find((fileOrDir) => fileOrDir?.name === 'grasscutter')) {
|
if (!innerDirs.find((fileOrDir) => fileOrDir?.name === 'grasscutter')) {
|
||||||
// Create dir
|
// Create dir
|
||||||
await fs.createDir(local + 'cultivation/grasscutter').catch(e => console.log(e))
|
await fs.createDir(local + 'cultivation/grasscutter').catch((e) => console.log(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataFiles = await fs.readDir(local + 'cultivation')
|
const dataFiles = await fs.readDir(local + 'cultivation')
|
||||||
@@ -118,13 +117,13 @@ async function readConfigFile() {
|
|||||||
// Create config file
|
// Create config file
|
||||||
const file: fs.FsTextFileOption = {
|
const file: fs.FsTextFileOption = {
|
||||||
path: configFilePath,
|
path: configFilePath,
|
||||||
contents: JSON.stringify(defaultConfig)
|
contents: JSON.stringify(defaultConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(file)
|
await fs.writeFile(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, read the file
|
// Finally, read the file
|
||||||
return await fs.readTextFile(configFilePath)
|
return await fs.readTextFile(configFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +131,6 @@ async function writeConfigFile(raw: string) {
|
|||||||
// All external config functions call readConfigFile, which ensure files exists
|
// All external config functions call readConfigFile, which ensure files exists
|
||||||
await fs.writeFile({
|
await fs.writeFile({
|
||||||
path: configFilePath,
|
path: configFilePath,
|
||||||
contents: raw
|
contents: raw,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import { byteToString } from './string'
|
|||||||
|
|
||||||
export default class DownloadHandler {
|
export default class DownloadHandler {
|
||||||
downloads: {
|
downloads: {
|
||||||
path: string,
|
path: string
|
||||||
progress: number,
|
progress: number
|
||||||
total: number,
|
total: number
|
||||||
total_downloaded: number,
|
total_downloaded: number
|
||||||
status: string,
|
status: string
|
||||||
startTime: number,
|
startTime: number
|
||||||
error?: string,
|
error?: string
|
||||||
speed?: string,
|
speed?: string
|
||||||
onFinish?: () => void,
|
onFinish?: () => void
|
||||||
}[]
|
}[]
|
||||||
|
|
||||||
// Pass tauri invoke function
|
// Pass tauri invoke function
|
||||||
@@ -22,13 +22,13 @@ export default class DownloadHandler {
|
|||||||
listen('download_progress', ({ payload }) => {
|
listen('download_progress', ({ payload }) => {
|
||||||
// @ts-expect-error Payload may be unknown but backend always returns this object
|
// @ts-expect-error Payload may be unknown but backend always returns this object
|
||||||
const obj: {
|
const obj: {
|
||||||
downloaded: string,
|
downloaded: string
|
||||||
total: string,
|
total: string
|
||||||
path: string,
|
path: string
|
||||||
total_downloaded: string,
|
total_downloaded: string
|
||||||
} = payload
|
} = payload
|
||||||
|
|
||||||
const index = this.downloads.findIndex(download => download.path === obj.path)
|
const index = this.downloads.findIndex((download) => download.path === obj.path)
|
||||||
this.downloads[index].progress = parseInt(obj.downloaded, 10)
|
this.downloads[index].progress = parseInt(obj.downloaded, 10)
|
||||||
this.downloads[index].total = parseInt(obj.total, 10)
|
this.downloads[index].total = parseInt(obj.total, 10)
|
||||||
this.downloads[index].total_downloaded = parseInt(obj.total_downloaded, 10)
|
this.downloads[index].total_downloaded = parseInt(obj.total_downloaded, 10)
|
||||||
@@ -52,7 +52,7 @@ export default class DownloadHandler {
|
|||||||
const filename = payload
|
const filename = payload
|
||||||
|
|
||||||
// set status to finished
|
// set status to finished
|
||||||
const index = this.downloads.findIndex(download => download.path === filename)
|
const index = this.downloads.findIndex((download) => download.path === filename)
|
||||||
this.downloads[index].status = 'finished'
|
this.downloads[index].status = 'finished'
|
||||||
|
|
||||||
// Call onFinish callback
|
// Call onFinish callback
|
||||||
@@ -65,12 +65,12 @@ export default class DownloadHandler {
|
|||||||
listen('download_error', ({ payload }) => {
|
listen('download_error', ({ payload }) => {
|
||||||
// @ts-expect-error shut up typescript
|
// @ts-expect-error shut up typescript
|
||||||
const errorData: {
|
const errorData: {
|
||||||
path: string,
|
path: string
|
||||||
error: string,
|
error: string
|
||||||
} = payload
|
} = payload
|
||||||
|
|
||||||
// Set download to error
|
// Set download to error
|
||||||
const index = this.downloads.findIndex(download => download.path === errorData.path)
|
const index = this.downloads.findIndex((download) => download.path === errorData.path)
|
||||||
this.downloads[index].status = 'error'
|
this.downloads[index].status = 'error'
|
||||||
this.downloads[index].error = errorData.error
|
this.downloads[index].error = errorData.error
|
||||||
})
|
})
|
||||||
@@ -78,13 +78,13 @@ export default class DownloadHandler {
|
|||||||
// Extraction events
|
// Extraction events
|
||||||
listen('extract_start', ({ payload }) => {
|
listen('extract_start', ({ payload }) => {
|
||||||
// Find the download that is no extracting and set it's status as such
|
// Find the download that is no extracting and set it's status as such
|
||||||
const index = this.downloads.findIndex(download => download.path === payload)
|
const index = this.downloads.findIndex((download) => download.path === payload)
|
||||||
this.downloads[index].status = 'extracting'
|
this.downloads[index].status = 'extracting'
|
||||||
})
|
})
|
||||||
|
|
||||||
listen('extract_end', ({ payload }) => {
|
listen('extract_end', ({ payload }) => {
|
||||||
// Find the download that is no extracting and set it's status as such
|
// Find the download that is no extracting and set it's status as such
|
||||||
const index = this.downloads.findIndex(download => download.path === payload)
|
const index = this.downloads.findIndex((download) => download.path === payload)
|
||||||
this.downloads[index].status = 'finished'
|
this.downloads[index].status = 'finished'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -95,16 +95,16 @@ export default class DownloadHandler {
|
|||||||
|
|
||||||
downloadingJar() {
|
downloadingJar() {
|
||||||
// Kinda hacky but it works
|
// Kinda hacky but it works
|
||||||
return this.downloads.some(d => d.path.includes('grasscutter.zip'))
|
return this.downloads.some((d) => d.path.includes('grasscutter.zip'))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadingResources() {
|
downloadingResources() {
|
||||||
// Kinda hacky but it works
|
// Kinda hacky but it works
|
||||||
return this.downloads.some(d => d.path.includes('resources'))
|
return this.downloads.some((d) => d.path.includes('resources'))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadingRepo() {
|
downloadingRepo() {
|
||||||
return this.downloads.some(d => d.path.includes('grasscutter_repo.zip'))
|
return this.downloads.some((d) => d.path.includes('grasscutter_repo.zip'))
|
||||||
}
|
}
|
||||||
|
|
||||||
addDownload(url: string, path: string, onFinish?: () => void) {
|
addDownload(url: string, path: string, onFinish?: () => void) {
|
||||||
@@ -128,24 +128,24 @@ export default class DownloadHandler {
|
|||||||
invoke('stop_download', { path })
|
invoke('stop_download', { path })
|
||||||
|
|
||||||
// Remove from list
|
// Remove from list
|
||||||
const index = this.downloads.findIndex(download => download.path === path)
|
const index = this.downloads.findIndex((download) => download.path === path)
|
||||||
this.downloads.splice(index, 1)
|
this.downloads.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
getDownloadProgress(path: string) {
|
getDownloadProgress(path: string) {
|
||||||
const index = this.downloads.findIndex(download => download.path === path)
|
const index = this.downloads.findIndex((download) => download.path === path)
|
||||||
return this.downloads[index] || null
|
return this.downloads[index] || null
|
||||||
}
|
}
|
||||||
|
|
||||||
getDownloadSize(path: string) {
|
getDownloadSize(path: string) {
|
||||||
const index = this.downloads.findIndex(download => download.path === path)
|
const index = this.downloads.findIndex((download) => download.path === path)
|
||||||
return byteToString(this.downloads[index].total) || null
|
return byteToString(this.downloads[index].total) || null
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotalAverage() {
|
getTotalAverage() {
|
||||||
const files = this.downloads.filter(d => d.status === 'downloading')
|
const files = this.downloads.filter((d) => d.status === 'downloading')
|
||||||
const total = files.reduce((acc, d) => acc + d.total, 0)
|
const total = files.reduce((acc, d) => acc + d.total, 0)
|
||||||
const progress = files.reduce((acc, d) => d.progress !== 0 ? acc + d.progress : acc + d.total_downloaded, 0)
|
const progress = files.reduce((acc, d) => (d.progress !== 0 ? acc + d.progress : acc + d.total_downloaded), 0)
|
||||||
let speedStr = '0 B/s'
|
let speedStr = '0 B/s'
|
||||||
|
|
||||||
// Get download speed based on startTimes
|
// Get download speed based on startTimes
|
||||||
@@ -158,10 +158,10 @@ export default class DownloadHandler {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
average: (progress / total) * 100 || 0,
|
average: (progress / total) * 100 || 0,
|
||||||
files: this.downloads.filter(d => d.status === 'downloading').length,
|
files: this.downloads.filter((d) => d.status === 'downloading').length,
|
||||||
extracting: this.downloads.filter(d => d.status === 'extracting').length,
|
extracting: this.downloads.filter((d) => d.status === 'extracting').length,
|
||||||
totalSize: total,
|
totalSize: total,
|
||||||
speed: speedStr
|
speed: speedStr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getConfig } from './configuration'
|
|||||||
export async function getGameExecutable() {
|
export async function getGameExecutable() {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
|
|
||||||
if(!config.game_install_path) {
|
if (!config.game_install_path) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ export async function getGameExecutable() {
|
|||||||
export async function getGameFolder() {
|
export async function getGameFolder() {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
|
|
||||||
if(!config.game_install_path) {
|
if (!config.game_install_path) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,4 +24,4 @@ export async function getGameFolder() {
|
|||||||
const path = pathArr.join('/')
|
const path = pathArr.join('/')
|
||||||
|
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import React from 'react'
|
|||||||
import { getConfigOption } from './configuration'
|
import { getConfigOption } from './configuration'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
text: string;
|
text: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
language: string;
|
language: string
|
||||||
translated_text: string;
|
translated_text: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Tr extends React.Component<IProps, IState> {
|
export default class Tr extends React.Component<IProps, IState> {
|
||||||
@@ -28,7 +28,7 @@ export default class Tr extends React.Component<IProps, IState> {
|
|||||||
if (!language) language = 'en'
|
if (!language) language = 'en'
|
||||||
|
|
||||||
invoke('get_lang', { lang: language }).then((response) => {
|
invoke('get_lang', { lang: language }).then((response) => {
|
||||||
const translation_obj = JSON.parse(response as string || '{}')
|
const translation_obj = JSON.parse((response as string) || '{}')
|
||||||
|
|
||||||
// Traversal
|
// Traversal
|
||||||
if (text.includes('.')) {
|
if (text.includes('.')) {
|
||||||
@@ -39,7 +39,7 @@ export default class Tr extends React.Component<IProps, IState> {
|
|||||||
if (!translation) {
|
if (!translation) {
|
||||||
translation = ''
|
translation = ''
|
||||||
} else {
|
} else {
|
||||||
translation = typeof translation !== 'string' ? translation[keys[i]] : translation as string
|
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export default class Tr extends React.Component<IProps, IState> {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
translated_text: translation_obj[text] || ''
|
translated_text: translation_obj[text] || '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -62,13 +62,13 @@ export default class Tr extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
export async function getLanguages() {
|
export async function getLanguages() {
|
||||||
const resp: {
|
const resp: {
|
||||||
[key: string]: string;
|
[key: string]: string
|
||||||
} = await invoke('get_languages')
|
} = await invoke('get_languages')
|
||||||
const lang_list: {
|
const lang_list: {
|
||||||
[key: string]: string;
|
[key: string]: string
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
Object.keys(resp).forEach(k => {
|
Object.keys(resp).forEach((k) => {
|
||||||
const parsed = JSON.parse(resp[k])
|
const parsed = JSON.parse(resp[k])
|
||||||
|
|
||||||
if (parsed.lang_name) {
|
if (parsed.lang_name) {
|
||||||
@@ -80,9 +80,9 @@ export async function getLanguages() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function translate(text: string) {
|
export async function translate(text: string) {
|
||||||
const language = await getConfigOption('language') || 'en'
|
const language = (await getConfigOption('language')) || 'en'
|
||||||
const translation_json = JSON.parse(await invoke('get_lang', { lang: language }) || '{}')
|
const translation_json = JSON.parse((await invoke('get_lang', { lang: language })) || '{}')
|
||||||
|
|
||||||
// Traversal
|
// Traversal
|
||||||
if (text.includes('.')) {
|
if (text.includes('.')) {
|
||||||
const keys = text.split('.')
|
const keys = text.split('.')
|
||||||
@@ -92,7 +92,7 @@ export async function translate(text: string) {
|
|||||||
if (!translation) {
|
if (!translation) {
|
||||||
translation = ''
|
translation = ''
|
||||||
} else {
|
} else {
|
||||||
translation = typeof translation !== 'string' ? translation[keys[i]] : translation as string
|
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,4 +100,4 @@ export async function translate(text: string) {
|
|||||||
} else {
|
} else {
|
||||||
return translation_json[text] || ''
|
return translation_json[text] || ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { getGameExecutable, getGameFolder } from './game'
|
|||||||
|
|
||||||
export async function patchMetadata() {
|
export async function patchMetadata() {
|
||||||
const metadataExists = await invoke('dir_exists', {
|
const metadataExists = await invoke('dir_exists', {
|
||||||
path: await getGameMetadataPath() + '\\global-metadata.dat'
|
path: (await getGameMetadataPath()) + '\\global-metadata.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!metadataExists) {
|
if (!metadataExists) {
|
||||||
@@ -16,9 +16,9 @@ export async function patchMetadata() {
|
|||||||
|
|
||||||
// Copy unpatched metadata to backup location
|
// Copy unpatched metadata to backup location
|
||||||
const copiedMeta = await invoke('copy_file_with_new_name', {
|
const copiedMeta = await invoke('copy_file_with_new_name', {
|
||||||
path: await getGameMetadataPath() + '\\global-metadata.dat',
|
path: (await getGameMetadataPath()) + '\\global-metadata.dat',
|
||||||
newPath: await getBackupMetadataPath(),
|
newPath: await getBackupMetadataPath(),
|
||||||
newName: 'global-metadata-unpatched.dat'
|
newName: 'global-metadata-unpatched.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!copiedMeta) {
|
if (!copiedMeta) {
|
||||||
@@ -42,9 +42,9 @@ export async function patchMetadata() {
|
|||||||
console.log('Replacing unpatched game metadata with patched metadata')
|
console.log('Replacing unpatched game metadata with patched metadata')
|
||||||
|
|
||||||
const replacedMeta = await invoke('copy_file_with_new_name', {
|
const replacedMeta = await invoke('copy_file_with_new_name', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-patched.dat',
|
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
|
||||||
newPath: await getGameMetadataPath(),
|
newPath: await getGameMetadataPath(),
|
||||||
newName: 'global-metadata.dat'
|
newName: 'global-metadata.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!replacedMeta) {
|
if (!replacedMeta) {
|
||||||
@@ -58,7 +58,7 @@ export async function patchMetadata() {
|
|||||||
|
|
||||||
export async function patchGame() {
|
export async function patchGame() {
|
||||||
const backupExists = await invoke('dir_exists', {
|
const backupExists = await invoke('dir_exists', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat'
|
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!backupExists) {
|
if (!backupExists) {
|
||||||
@@ -72,7 +72,7 @@ export async function patchGame() {
|
|||||||
|
|
||||||
// Do we have a patch already?
|
// Do we have a patch already?
|
||||||
const patchedExists = await invoke('dir_exists', {
|
const patchedExists = await invoke('dir_exists', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-patched.dat'
|
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!patchedExists) {
|
if (!patchedExists) {
|
||||||
@@ -82,12 +82,12 @@ export async function patchGame() {
|
|||||||
if (!patched) {
|
if (!patched) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we already patched? If so, that's fine, just continue as normal
|
// Are we already patched? If so, that's fine, just continue as normal
|
||||||
const gameIsPatched = await invoke('are_files_identical', {
|
const gameIsPatched = await invoke('are_files_identical', {
|
||||||
path1: await getBackupMetadataPath() + '\\global-metadata-patched.dat',
|
path1: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
|
||||||
path2: await getGameMetadataPath() + '\\global-metadata.dat'
|
path2: (await getGameMetadataPath()) + '\\global-metadata.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (gameIsPatched) {
|
if (gameIsPatched) {
|
||||||
@@ -96,17 +96,17 @@ export async function patchGame() {
|
|||||||
|
|
||||||
// Is the current backup the same as the games current metadata?
|
// Is the current backup the same as the games current metadata?
|
||||||
const backupIsCurrent = await invoke('are_files_identical', {
|
const backupIsCurrent = await invoke('are_files_identical', {
|
||||||
path1: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat',
|
path1: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||||
path2: await getGameMetadataPath() + '\\global-metadata.dat'
|
path2: (await getGameMetadataPath()) + '\\global-metadata.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
// Game has probably been updated. We need to repatch the game...
|
// Game has probably been updated. We need to repatch the game...
|
||||||
if (!backupIsCurrent) {
|
if (!backupIsCurrent) {
|
||||||
const deletedOldBackup = await invoke('delete_file', {
|
const deletedOldBackup = await invoke('delete_file', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat'
|
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||||
})
|
})
|
||||||
const deletedOldPatched = await invoke('delete_file', {
|
const deletedOldPatched = await invoke('delete_file', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-patched.dat'
|
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
// It's fine if these deletes fail. The game will be replaced anyway.
|
// It's fine if these deletes fail. The game will be replaced anyway.
|
||||||
@@ -134,9 +134,9 @@ export async function patchGame() {
|
|||||||
|
|
||||||
// Finally, replace the unpatched metadata with the patched one
|
// Finally, replace the unpatched metadata with the patched one
|
||||||
const replaced = await invoke('copy_file_with_new_name', {
|
const replaced = await invoke('copy_file_with_new_name', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-patched.dat',
|
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
|
||||||
newPath: await getGameMetadataPath(),
|
newPath: await getGameMetadataPath(),
|
||||||
newName: 'global-metadata.dat'
|
newName: 'global-metadata.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!replaced) {
|
if (!replaced) {
|
||||||
@@ -148,7 +148,7 @@ export async function patchGame() {
|
|||||||
|
|
||||||
export async function unpatchGame() {
|
export async function unpatchGame() {
|
||||||
const backupExists = await invoke('dir_exists', {
|
const backupExists = await invoke('dir_exists', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat'
|
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!backupExists) {
|
if (!backupExists) {
|
||||||
@@ -157,9 +157,9 @@ export async function unpatchGame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const replaced = await invoke('copy_file_with_new_name', {
|
const replaced = await invoke('copy_file_with_new_name', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat',
|
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||||
newPath: await getGameMetadataPath(),
|
newPath: await getGameMetadataPath(),
|
||||||
newName: 'global-metadata.dat'
|
newName: 'global-metadata.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
return replaced
|
return replaced
|
||||||
@@ -172,21 +172,27 @@ export async function getGameMetadataPath() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await getGameFolder() + '\\' + gameExec.replace('.exe', '_Data') + '\\Managed\\Metadata').replace(/\\/g, '/')
|
return ((await getGameFolder()) + '\\' + gameExec.replace('.exe', '_Data') + '\\Managed\\Metadata').replace(
|
||||||
|
/\\/g,
|
||||||
|
'/'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBackupMetadataPath() {
|
export async function getBackupMetadataPath() {
|
||||||
return await dataDir() + 'cultivation\\metadata'
|
return (await dataDir()) + 'cultivation\\metadata'
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function globalMetadataLink() {
|
export async function globalMetadataLink() {
|
||||||
const versionAPIUrl = 'https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?channel_id=1&key=gcStgarh&launcher_id=10&sub_channel_id=0'
|
const versionAPIUrl =
|
||||||
|
'https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?channel_id=1&key=gcStgarh&launcher_id=10&sub_channel_id=0'
|
||||||
|
|
||||||
// Get versions from API
|
// Get versions from API
|
||||||
const versions = JSON.parse(await invoke('web_get', {
|
const versions = JSON.parse(
|
||||||
url: versionAPIUrl
|
await invoke('web_get', {
|
||||||
}))
|
url: versionAPIUrl,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
if (!versions || versions.retcode !== 0) {
|
if (!versions || versions.retcode !== 0) {
|
||||||
console.log('Failed to get versions from API')
|
console.log('Failed to get versions from API')
|
||||||
return null
|
return null
|
||||||
@@ -195,7 +201,7 @@ export async function globalMetadataLink() {
|
|||||||
// Get latest version
|
// Get latest version
|
||||||
const latest = versions.data.game.latest
|
const latest = versions.data.game.latest
|
||||||
|
|
||||||
return latest.decompressed_path as string + '/GenshinImpact_Data/Managed/Metadata/global-metadata.dat'
|
return (latest.decompressed_path as string) + '/GenshinImpact_Data/Managed/Metadata/global-metadata.dat'
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restoreMetadata(manager: DownloadHandler) {
|
export async function restoreMetadata(manager: DownloadHandler) {
|
||||||
@@ -208,16 +214,16 @@ export async function restoreMetadata(manager: DownloadHandler) {
|
|||||||
|
|
||||||
// Should make sure metadata path exists since the user may have deleted it
|
// Should make sure metadata path exists since the user may have deleted it
|
||||||
await invoke('dir_create', {
|
await invoke('dir_create', {
|
||||||
path: await getBackupMetadataPath()
|
path: await getBackupMetadataPath(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// It is possible the unpatched backup is mistakenly patched
|
// It is possible the unpatched backup is mistakenly patched
|
||||||
await invoke('delete_file', {
|
await invoke('delete_file', {
|
||||||
path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat'
|
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||||
})
|
})
|
||||||
|
|
||||||
// Download the file
|
// Download the file
|
||||||
manager.addDownload(metaLink, await getBackupMetadataPath() + '\\global-metadata-unpatched.dat', () => {
|
manager.addDownload(metaLink, (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat', () => {
|
||||||
unpatchGame()
|
unpatchGame()
|
||||||
})
|
})
|
||||||
console.log('Restoring backedup metadata')
|
console.log('Restoring backedup metadata')
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ export async function toggleEncryption(path: string) {
|
|||||||
let serverConf
|
let serverConf
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverConf = JSON.parse(await invoke('read_file', {
|
serverConf = JSON.parse(
|
||||||
path,
|
await invoke('read_file', {
|
||||||
}))
|
path,
|
||||||
} catch(e) {
|
})
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
console.log(`Server config at ${path} not found or invalid`)
|
console.log(`Server config at ${path} not found or invalid`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -28,13 +30,15 @@ export async function encryptionEnabled(path: string) {
|
|||||||
let serverConf
|
let serverConf
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverConf = JSON.parse(await invoke('read_file', {
|
serverConf = JSON.parse(
|
||||||
path,
|
await invoke('read_file', {
|
||||||
}))
|
path,
|
||||||
} catch(e) {
|
})
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
console.log(`Server config at ${path} not found or invalid`)
|
console.log(`Server config at ${path} not found or invalid`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverConf.server.http.encryption.useEncryption
|
return serverConf.server.http.encryption.useEncryption
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ export function byteToString(bytes: number) {
|
|||||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10)
|
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10)
|
||||||
if (i === 0) return `${bytes} ${sizes[i]}`
|
if (i === 0) return `${bytes} ${sizes[i]}`
|
||||||
return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`
|
return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,15 +33,15 @@ const defaultTheme = {
|
|||||||
description: 'Default theme',
|
description: 'Default theme',
|
||||||
includes: {
|
includes: {
|
||||||
css: [],
|
css: [],
|
||||||
js: []
|
js: [],
|
||||||
},
|
},
|
||||||
path: 'default'
|
path: 'default',
|
||||||
}
|
}
|
||||||
export async function getThemeList() {
|
export async function getThemeList() {
|
||||||
// Do some invoke to backend to get the theme list
|
// Do some invoke to backend to get the theme list
|
||||||
const themes = await invoke('get_theme_list', {
|
const themes = (await invoke('get_theme_list', {
|
||||||
dataDir: `${await dataDir()}/cultivation`
|
dataDir: `${await dataDir()}/cultivation`,
|
||||||
}) as BackendThemeList[]
|
})) as BackendThemeList[]
|
||||||
const list: ThemeList[] = [
|
const list: ThemeList[] = [
|
||||||
// ALWAYS include default theme
|
// ALWAYS include default theme
|
||||||
{
|
{
|
||||||
@@ -50,13 +50,13 @@ export async function getThemeList() {
|
|||||||
description: 'Default theme',
|
description: 'Default theme',
|
||||||
includes: {
|
includes: {
|
||||||
css: [],
|
css: [],
|
||||||
js: []
|
js: [],
|
||||||
},
|
},
|
||||||
path: 'default'
|
path: 'default',
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
themes.forEach(t => {
|
themes.forEach((t) => {
|
||||||
let obj
|
let obj
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -74,7 +74,7 @@ export async function getThemeList() {
|
|||||||
export async function getTheme(name: string) {
|
export async function getTheme(name: string) {
|
||||||
const themes = await getThemeList()
|
const themes = await getThemeList()
|
||||||
|
|
||||||
return themes.find(t => t.name === name) || defaultTheme
|
return themes.find((t) => t.name === name) || defaultTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadTheme(theme: ThemeList, document: Document) {
|
export async function loadTheme(theme: ThemeList, document: Document) {
|
||||||
@@ -89,7 +89,7 @@ export async function loadTheme(theme: ThemeList, document: Document) {
|
|||||||
const jsIncludes = theme.includes.js
|
const jsIncludes = theme.includes.js
|
||||||
|
|
||||||
// Load CSS files
|
// Load CSS files
|
||||||
cssIncludes.forEach(css => {
|
cssIncludes.forEach((css) => {
|
||||||
if (!css) return
|
if (!css) return
|
||||||
|
|
||||||
const link = document.createElement('link')
|
const link = document.createElement('link')
|
||||||
@@ -100,7 +100,7 @@ export async function loadTheme(theme: ThemeList, document: Document) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Load JS files
|
// Load JS files
|
||||||
jsIncludes.forEach(js => {
|
jsIncludes.forEach((js) => {
|
||||||
if (!js) return
|
if (!js) return
|
||||||
|
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
@@ -125,7 +125,7 @@ export async function loadTheme(theme: ThemeList, document: Document) {
|
|||||||
// Save the background to our data dir
|
// Save the background to our data dir
|
||||||
await invoke('copy_file', {
|
await invoke('copy_file', {
|
||||||
path: theme.path + '/' + theme.customBackgroundPath,
|
path: theme.path + '/' + theme.customBackgroundPath,
|
||||||
newPath: bgPath
|
newPath: bgPath,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set the background
|
// Set the background
|
||||||
@@ -139,4 +139,4 @@ export async function loadTheme(theme: ThemeList, document: Document) {
|
|||||||
await setConfigOption('customBackground', config.customBackground)
|
await setConfigOption('customBackground', config.customBackground)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ export function unzip(file: string, dest: string, onFinish?: () => void) {
|
|||||||
destpath: dest,
|
destpath: dest,
|
||||||
})
|
})
|
||||||
|
|
||||||
listen('extract_end', ({payload}) => {
|
listen('extract_end', ({ payload }) => {
|
||||||
if (payload === file && onFinish) {
|
if (payload === file && onFinish) {
|
||||||
onFinish()
|
onFinish()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@@ -20,7 +16,5 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src"]
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user