mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 19:50:28 +00:00
Compare commits
443 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f85d96488 | ||
|
|
c8a382c08f | ||
|
|
0f131203f2 | ||
|
|
bfbad50772 | ||
|
|
dc9816eb9e | ||
|
|
c6a0f5b8c0 | ||
|
|
91046e7f5e | ||
|
|
678f8d8cd7 | ||
|
|
6263a0a5e6 | ||
|
|
45c0476636 | ||
|
|
d86d454f52 | ||
|
|
2be5441cf4 | ||
|
|
a673f70862 | ||
|
|
1d59acff2d | ||
|
|
724f7d8242 | ||
|
|
3135edad8e | ||
|
|
bba2dbd360 | ||
|
|
66ead87d47 | ||
|
|
1a1f72a596 | ||
|
|
062014f24a | ||
|
|
4078395a89 | ||
|
|
dd762d53b2 | ||
|
|
eb42c7101d | ||
|
|
9ec72c29ca | ||
|
|
bb6d63efe2 | ||
|
|
380eb1e77f | ||
|
|
e9021117ff | ||
|
|
e56f7c5294 | ||
|
|
32cffea018 | ||
|
|
4b2ac25c1f | ||
|
|
00e8b0c303 | ||
|
|
dc163d790c | ||
|
|
750f3b1fee | ||
|
|
e3bdf33eec | ||
|
|
c5f54e04b5 | ||
|
|
b62c603b01 | ||
|
|
0186b5f9f0 | ||
|
|
70c47a8b85 | ||
|
|
6ec973144e | ||
|
|
9a28257f4a | ||
|
|
07f476e324 | ||
|
|
28efaf0685 | ||
|
|
f69b65f233 | ||
|
|
56c1ec98c5 | ||
|
|
a05737a4f4 | ||
|
|
f37208a383 | ||
|
|
3f6a9f44d4 | ||
|
|
13875f850f | ||
|
|
b787898e1f | ||
|
|
50c8d294f9 | ||
|
|
cf3e67838d | ||
|
|
c8962d509c | ||
|
|
39d571960b | ||
|
|
a132c85b8c | ||
|
|
f68b7a4380 | ||
|
|
138be64c0d | ||
|
|
3fd020b8b0 | ||
|
|
83c0fa6a26 | ||
|
|
0126ae811e | ||
|
|
23e909e330 | ||
|
|
c172b623d7 | ||
|
|
da0bf8f4e7 | ||
|
|
df025319aa | ||
|
|
c456a2ac47 | ||
|
|
a813617689 | ||
|
|
9872823aff | ||
|
|
8c4c11df6a | ||
|
|
fddaa99779 | ||
|
|
fbf40580cf | ||
|
|
8ea5373dec | ||
|
|
8898569067 | ||
|
|
3c60b5a2c1 | ||
|
|
c77684ddb8 | ||
|
|
d750b91f61 | ||
|
|
3f5aaaecd9 | ||
|
|
53ffab783f | ||
|
|
39ddea7254 | ||
|
|
289691896d | ||
|
|
4315e36cbe | ||
|
|
c5de9e2252 | ||
|
|
fe141d98c2 | ||
|
|
62b8718dbd | ||
|
|
f6676ed3d8 | ||
|
|
855ea1fe8f | ||
|
|
dcb288597f | ||
|
|
79e1256759 | ||
|
|
b11603d68c | ||
|
|
6f0c760ab4 | ||
|
|
125fedbc84 | ||
|
|
19488507b0 | ||
|
|
bf246e469f | ||
|
|
1c4eafccca | ||
|
|
aaa97429b9 | ||
|
|
f3fab62291 | ||
|
|
e1ed30b6e6 | ||
|
|
e3c3986379 | ||
|
|
a0290cf28a | ||
|
|
6c3d9d05be | ||
|
|
0f24b506fa | ||
|
|
9704ddc211 | ||
|
|
fc6063866c | ||
|
|
db89100402 | ||
|
|
db024f19bd | ||
|
|
5ccd2def37 | ||
|
|
28789792ce | ||
|
|
7d6f7879c4 | ||
|
|
9959ae8c8f | ||
|
|
f744acbad5 | ||
|
|
da7c4b958d | ||
|
|
3c07d12ee9 | ||
|
|
825b399ab1 | ||
|
|
509f65b165 | ||
|
|
f9c06c0980 | ||
|
|
4718b4627b | ||
|
|
078eae8e52 | ||
|
|
2f680b98e9 | ||
|
|
c26d9cdc42 | ||
|
|
69b87620d6 | ||
|
|
c1c8bebec7 | ||
|
|
7db8f01009 | ||
|
|
967c46e6eb | ||
|
|
5d1443b472 | ||
|
|
a0382a1d2a | ||
|
|
5c668cba2c | ||
|
|
19034a04d5 | ||
|
|
3e9f82ecdf | ||
|
|
f551ccfbde | ||
|
|
1c40fac522 | ||
|
|
a199ba745a | ||
|
|
7a38d0d386 | ||
|
|
d1f19bae4e | ||
|
|
b65187d4f0 | ||
|
|
f6340337db | ||
|
|
6fda5628ff | ||
|
|
2ffb02e62d | ||
|
|
a6a5a117bc | ||
|
|
0983ebe21f | ||
|
|
aaaee30368 | ||
|
|
fd2291a85d | ||
|
|
f98235f2f3 | ||
|
|
77c1b4f51a | ||
|
|
af15d106f0 | ||
|
|
31d867bf3b | ||
|
|
3f660c7d5e | ||
|
|
d82cfb41aa | ||
|
|
3219129094 | ||
|
|
58da84c0a6 | ||
|
|
028660cbba | ||
|
|
857e471f28 | ||
|
|
fba1e49600 | ||
|
|
fc4b5c590b | ||
|
|
90fef6bcba | ||
|
|
cabe05eb03 | ||
|
|
e212928f57 | ||
|
|
95f1cc6481 | ||
|
|
4c7f7c5b2e | ||
|
|
a6942ca396 | ||
|
|
88dd4bdf8b | ||
|
|
64d5e27a72 | ||
|
|
80f87a8337 | ||
|
|
6c940ce6f3 | ||
|
|
c2a512699c | ||
|
|
8d635827c4 | ||
|
|
2f2a3c3889 | ||
|
|
df437b222d | ||
|
|
4822d0c76d | ||
|
|
04e06c18c9 | ||
|
|
e70edb7bab | ||
|
|
d8af51db28 | ||
|
|
bafcf42dbc | ||
|
|
bb96740607 | ||
|
|
5412382a59 | ||
|
|
31626efa9f | ||
|
|
44b4c4be82 | ||
|
|
5e1a9750f8 | ||
|
|
9ea6e09846 | ||
|
|
830ac75f08 | ||
|
|
107ad46a53 | ||
|
|
e26ea0d7d9 | ||
|
|
a6dc5a659e | ||
|
|
f9466dc3a1 | ||
|
|
ca2e971c6d | ||
|
|
99104527d4 | ||
|
|
44bf771f4c | ||
|
|
6119505426 | ||
|
|
998213bc69 | ||
|
|
8bfea81ee6 | ||
|
|
3c1e71891b | ||
|
|
92d65e69da | ||
|
|
86b2411fe6 | ||
|
|
c39412b099 | ||
|
|
7ebfb5e39d | ||
|
|
4e89578695 | ||
|
|
7a921e6d38 | ||
|
|
34ea335b3f | ||
|
|
07ca125c5b | ||
|
|
301c002c90 | ||
|
|
ea0e046bee | ||
|
|
277c193d73 | ||
|
|
0df2fe7580 | ||
|
|
f63a2f833a | ||
|
|
b1b6b358f5 | ||
|
|
4e37b87d59 | ||
|
|
2463bb1560 | ||
|
|
f328d981c8 | ||
|
|
488ad2a485 | ||
|
|
4679d559d9 | ||
|
|
d257663b27 | ||
|
|
5d735632bf | ||
|
|
2139bfb652 | ||
|
|
4d4c9db816 | ||
|
|
2d7e309395 | ||
|
|
b4dc6e34eb | ||
|
|
f958edc5c7 | ||
|
|
2f74fef969 | ||
|
|
b276043c31 | ||
|
|
9cc7a7684c | ||
|
|
3479d09ba5 | ||
|
|
27a05414b8 | ||
|
|
af7074b37b | ||
|
|
05a1b62b8a | ||
|
|
db1b8141d3 | ||
|
|
8a7ac8eb8d | ||
|
|
80456079a3 | ||
|
|
86b35ebd3d | ||
|
|
c4d3d099cd | ||
|
|
e887986854 | ||
|
|
debc31a6ed | ||
|
|
08ddaa897e | ||
|
|
edd49cb55f | ||
|
|
b181d918bd | ||
|
|
df225b9850 | ||
|
|
055421f22f | ||
|
|
184290aaf4 | ||
|
|
14b16f3a78 | ||
|
|
b2ec1e93a8 | ||
|
|
cb35c400f9 | ||
|
|
3c061f995c | ||
|
|
03c941c970 | ||
|
|
a2de310d84 | ||
|
|
85bc6e487b | ||
|
|
033e2824a4 | ||
|
|
3b940ead08 | ||
|
|
01f16201a9 | ||
|
|
251fe08139 | ||
|
|
88f9a6eccc | ||
|
|
404b0ec958 | ||
|
|
fc59eb3879 | ||
|
|
fef65f6c2a | ||
|
|
5728ed8379 | ||
|
|
368bf04a9a | ||
|
|
48ab2e28b2 | ||
|
|
1343ac650f | ||
|
|
16dab8236b | ||
|
|
72f3cbd2d7 | ||
|
|
a75a580b2d | ||
|
|
320ce177b9 | ||
|
|
a6de0364c1 | ||
|
|
5b8314411b | ||
|
|
ac72bcb554 | ||
|
|
71844945f2 | ||
|
|
fdc4060ac0 | ||
|
|
8dd7ef53a1 | ||
|
|
c02c98a19e | ||
|
|
4b875b7898 | ||
|
|
ac959a9faa | ||
|
|
26e4908101 | ||
|
|
41b66ca33f | ||
|
|
bea907b817 | ||
|
|
f5f3e4753c | ||
|
|
b18024a8ce | ||
|
|
b830b2b863 | ||
|
|
cb77b69fa1 | ||
|
|
722b0693fd | ||
|
|
6a90930bca | ||
|
|
d747294662 | ||
|
|
ebc469f3c1 | ||
|
|
6a8614406c | ||
|
|
dd8325e78b | ||
|
|
721ba6a3c2 | ||
|
|
d65eb82ece | ||
|
|
a902bf82a0 | ||
|
|
b091022799 | ||
|
|
0afa8c0386 | ||
|
|
53e28d7272 | ||
|
|
d2f21661b8 | ||
|
|
580498a71a | ||
|
|
69ee1bcc9e | ||
|
|
258405bcf5 | ||
|
|
026933c25f | ||
|
|
3b50830dab | ||
|
|
73ad2ad023 | ||
|
|
96aea196a6 | ||
|
|
f45e9849a0 | ||
|
|
b881e825a9 | ||
|
|
ac36c0c248 | ||
|
|
8a679a0a66 | ||
|
|
7be9bdf7c3 | ||
|
|
cfa6f18a58 | ||
|
|
43d0d8454c | ||
|
|
33f8866bb9 | ||
|
|
a0d4d8b32d | ||
|
|
c112a920ec | ||
|
|
f415ee3469 | ||
|
|
fe4f330e5e | ||
|
|
ddcd6d8f16 | ||
|
|
16e50e6d75 | ||
|
|
3ce485df5c | ||
|
|
61ee05f5b2 | ||
|
|
8249d577be | ||
|
|
5ebe438584 | ||
|
|
2a009a1a51 | ||
|
|
4b70ec1914 | ||
|
|
4c6a5f7565 | ||
|
|
82943020f7 | ||
|
|
4297b2f530 | ||
|
|
772f329407 | ||
|
|
f8a846e706 | ||
|
|
cd788b6fe5 | ||
|
|
9e4523c891 | ||
|
|
3dd68c7c87 | ||
|
|
a9a85607b0 | ||
|
|
11738da6a5 | ||
|
|
9789b9e1a9 | ||
|
|
56208e1909 | ||
|
|
472fdb08fb | ||
|
|
1681e2407b | ||
|
|
49cef9ed90 | ||
|
|
9f7e0dad52 | ||
|
|
281f6ee995 | ||
|
|
8d07af2253 | ||
|
|
8a58719908 | ||
|
|
0577b54f9c | ||
|
|
3c59a2ca57 | ||
|
|
0c03050f5c | ||
|
|
7e1e96707c | ||
|
|
c6ced405ef | ||
|
|
11a54501c0 | ||
|
|
74527178b7 | ||
|
|
1dec934d6a | ||
|
|
94ffcb8118 | ||
|
|
e9708827df | ||
|
|
75d9fc7e5e | ||
|
|
eb1ee49085 | ||
|
|
5fb0df38b0 | ||
|
|
5cb9e931c7 | ||
|
|
57c6dd7fd1 | ||
|
|
f969a084ae | ||
|
|
15773e6639 | ||
|
|
c9114bb202 | ||
|
|
9713c62c46 | ||
|
|
5822dec69e | ||
|
|
ae920fca8f | ||
|
|
bbca12e3a2 | ||
|
|
e31902d67e | ||
|
|
e37acfdaed | ||
|
|
f81aae82ab | ||
|
|
72cdf77a74 | ||
|
|
6df16058ac | ||
|
|
abfd31e001 | ||
|
|
53087306a8 | ||
|
|
197053380d | ||
|
|
495dfd2c94 | ||
|
|
968a2a957b | ||
|
|
3757c27939 | ||
|
|
2a940b9312 | ||
|
|
2f17dba2fa | ||
|
|
6e1e50f34e | ||
|
|
bfc0c5d3d7 | ||
|
|
8c6c980bb8 | ||
|
|
6a6360540b | ||
|
|
d905f8c13e | ||
|
|
0d70cb711a | ||
|
|
f283f4db43 | ||
|
|
3ce635075c | ||
|
|
ffd32ec7ff | ||
|
|
ba102bbab8 | ||
|
|
79d56dd267 | ||
|
|
134b4f0983 | ||
|
|
6090d772fe | ||
|
|
3da318ec71 | ||
|
|
f453821e15 | ||
|
|
de955f1226 | ||
|
|
636f5cdb53 | ||
|
|
340e38a8e7 | ||
|
|
f0a3b527cf | ||
|
|
d1c7f2b72b | ||
|
|
e5d83aea04 | ||
|
|
cf7230f980 | ||
|
|
44ec80cc73 | ||
|
|
53e44db817 | ||
|
|
41f10e408f | ||
|
|
bc25c92bba | ||
|
|
8fee9dba05 | ||
|
|
6ff0b7c902 | ||
|
|
79239de308 | ||
|
|
7def53649a | ||
|
|
4dd47d02eb | ||
|
|
4d36b5d7c1 | ||
|
|
bbd1aa91ff | ||
|
|
26e28799ff | ||
|
|
83adfbc303 | ||
|
|
a9980208d3 | ||
|
|
f88f73d3a8 | ||
|
|
ae14a26f08 | ||
|
|
c6720baa5e | ||
|
|
d1716bd873 | ||
|
|
853b9034c0 | ||
|
|
c7d70084d8 | ||
|
|
2e69068209 | ||
|
|
38a1ddab72 | ||
|
|
6265acb2ad | ||
|
|
cd812cac66 | ||
|
|
5cf8d5a0a0 | ||
|
|
a94f49eca2 | ||
|
|
62289b3b30 | ||
|
|
536daa176a | ||
|
|
cb69211a78 | ||
|
|
222b9e8fef | ||
|
|
8913fe167f | ||
|
|
8b78c6d052 | ||
|
|
f413d924ad | ||
|
|
ebbce1ed57 | ||
|
|
dc5392258f | ||
|
|
b530742634 | ||
|
|
20227fbd4b | ||
|
|
56d4988cfc | ||
|
|
47c6f2205a | ||
|
|
003a6d1cb1 | ||
|
|
f7dfe108c9 | ||
|
|
638f8760b7 | ||
|
|
3f7ee82d45 | ||
|
|
ad70e21e2c | ||
|
|
976d3474f9 | ||
|
|
5f0b11d6ef | ||
|
|
a114f052b1 | ||
|
|
32b7f2a9fc | ||
|
|
858479f28e | ||
|
|
c90bc803b5 | ||
|
|
40dec20b89 | ||
|
|
725277eef7 | ||
|
|
5eb0c6dda8 | ||
|
|
6f146ed84b |
258
.github/copilot-instructions.md
vendored
Normal file
258
.github/copilot-instructions.md
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
# StarCitizenToolBox Flutter/Rust Desktop Application
|
||||
|
||||
StarCitizenToolBox is a Flutter desktop application with Rust native bindings designed for Star Citizen players. It provides localization management, game diagnostics, website translations, performance optimization, and various tools for the Star Citizen gaming experience. The app supports Windows, macOS, and Linux platforms using Fluent UI design.
|
||||
|
||||
Always reference these instructions first and fallback to additional search or context gathering only when information in these instructions is incomplete or found to be in error.
|
||||
|
||||
## Working Effectively
|
||||
|
||||
### Initial Setup and Dependencies
|
||||
|
||||
Install required dependencies in this exact order:
|
||||
|
||||
1. **Install Rust toolchain:**
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
source ~/.cargo/env
|
||||
```
|
||||
|
||||
2. **Install Flutter SDK (stable channel):**
|
||||
```bash
|
||||
# Clone Flutter repository
|
||||
cd /tmp
|
||||
git clone https://github.com/flutter/flutter.git -b stable --depth 1
|
||||
export PATH="/tmp/flutter/bin:$PATH"
|
||||
flutter --version
|
||||
```
|
||||
Note: If Flutter download fails due to network issues, the build can still proceed with existing generated code.
|
||||
|
||||
3. **Install essential Rust tools:**
|
||||
```bash
|
||||
cargo install cargo-expand
|
||||
cargo install 'flutter_rust_bridge_codegen@^2.0.0-dev.0'
|
||||
```
|
||||
|
||||
### Build Process - NEVER CANCEL LONG-RUNNING COMMANDS
|
||||
|
||||
Execute these commands in sequence. **NEVER CANCEL** - builds can take 45+ minutes:
|
||||
|
||||
1. **Install Flutter dependencies:** (2-3 minutes)
|
||||
```bash
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
2. **Generate Dart code:** (3-5 minutes)
|
||||
```bash
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
```
|
||||
|
||||
3. **Update Rust dependencies:** (10-15 minutes first time, 2-3 minutes subsequent)
|
||||
```bash
|
||||
cd rust && cargo update
|
||||
```
|
||||
|
||||
4. **Generate Rust-Dart bridge code:** (5-8 minutes)
|
||||
```bash
|
||||
flutter_rust_bridge_codegen generate
|
||||
```
|
||||
|
||||
5. **Generate localization files:** (1-2 minutes)
|
||||
```bash
|
||||
flutter pub global activate intl_utils
|
||||
flutter pub global run intl_utils:generate
|
||||
```
|
||||
|
||||
6. **Build Rust components:** (60-90 seconds debug, 110-120 seconds release)
|
||||
```bash
|
||||
cd rust
|
||||
cargo build --release # NEVER CANCEL: Takes ~2 minutes
|
||||
```
|
||||
|
||||
7. **Build Flutter application:** (15-25 minutes - NEVER CANCEL)
|
||||
```bash
|
||||
flutter build windows -v # Set timeout to 30+ minutes minimum
|
||||
```
|
||||
|
||||
**CRITICAL BUILD TIMING:**
|
||||
- Complete full build from scratch: **45-60 minutes** - NEVER CANCEL
|
||||
- Rust release build: **2 minutes** - NEVER CANCEL
|
||||
- Flutter application build: **15-25 minutes** - NEVER CANCEL
|
||||
- Code generation steps: **10-15 minutes total** - NEVER CANCEL
|
||||
|
||||
### Platform-Specific Build Requirements
|
||||
|
||||
**Windows (Primary Platform):**
|
||||
- MSBuild tools
|
||||
- Visual Studio Build Tools or Visual Studio Community
|
||||
- Windows SDK
|
||||
- LLVM/Clang (version 18+ recommended)
|
||||
|
||||
**Linux (Development/Testing):**
|
||||
- Basic build tools: `build-essential`, `cmake`, `ninja-build`
|
||||
- GTK development libraries
|
||||
- Limited functionality (desktop app designed primarily for Windows)
|
||||
|
||||
**macOS:**
|
||||
- Xcode Command Line Tools
|
||||
- CMake
|
||||
|
||||
### Testing and Validation
|
||||
|
||||
**Rust Component Testing:**
|
||||
```bash
|
||||
cd rust
|
||||
cargo check # Fast syntax check (1-2 minutes)
|
||||
cargo test # Run Rust unit tests (5-10 minutes - NEVER CANCEL)
|
||||
```
|
||||
|
||||
**Flutter Testing:**
|
||||
```bash
|
||||
flutter test # Run Flutter widget tests (2-5 minutes)
|
||||
```
|
||||
|
||||
**Linting and Code Quality:**
|
||||
```bash
|
||||
flutter analyze # Dart/Flutter linting (30-60 seconds)
|
||||
cd rust && cargo clippy # Rust linting (2-3 minutes)
|
||||
```
|
||||
|
||||
### Manual Validation Requirements
|
||||
|
||||
After building, always perform these validation steps:
|
||||
|
||||
1. **Verify application starts:**
|
||||
```bash
|
||||
# Windows
|
||||
./build/windows/x64/runner/Release/starcitizen_doctor.exe
|
||||
|
||||
# Linux (limited functionality)
|
||||
./build/linux/x64/release/bundle/starcitizen_doctor
|
||||
```
|
||||
|
||||
2. **Test core functionality:**
|
||||
- Application launches without crashes
|
||||
- Main navigation works
|
||||
- Settings panel accessible
|
||||
- Localization switching functional
|
||||
|
||||
**Note:** Full UI testing requires Windows environment. Linux builds have limited functionality.
|
||||
|
||||
## Project Structure and Key Files
|
||||
|
||||
### Entry Points and Main Code
|
||||
- **Main application:** `lib/main.dart` - Flutter app entry point with multi-window support
|
||||
- **App configuration:** `lib/app.dart` - Application state, routing, theming, and localization
|
||||
- **Rust bridge:** `lib/common/rust/` - Generated Rust-Dart interop code
|
||||
- **UI modules:** `lib/ui/` - All user interface components organized by feature
|
||||
|
||||
### Build Configuration
|
||||
- **Flutter config:** `pubspec.yaml` - Dependencies, assets, build settings
|
||||
- **Rust config:** `rust/Cargo.toml` - Native dependencies and build settings
|
||||
- **Rust bridge:** `flutter_rust_bridge.yaml` - Bridge code generation settings
|
||||
- **Analysis:** `analysis_options.yaml` - Dart/Flutter linting rules
|
||||
|
||||
### Build System Files
|
||||
- **Rust builder:** `rust_builder/` - Custom Rust compilation integration with cargokit
|
||||
- **Platform builds:** `windows/`, `linux/`, `macos/` - Platform-specific build configurations
|
||||
- **CI/CD:** `.github/workflows/windows_nightly.yml` - Automated build pipeline
|
||||
|
||||
### Generated Code (DO NOT EDIT)
|
||||
- `lib/generated/` - Auto-generated localization files
|
||||
- `lib/**/*.g.dart` - Generated JSON serialization code
|
||||
- `lib/**/*.freezed.dart` - Generated immutable data classes
|
||||
- `lib/common/rust/` - Generated Rust-Dart bridge code
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Making Changes
|
||||
|
||||
1. **For Dart/Flutter changes:**
|
||||
```bash
|
||||
# After UI or logic changes
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
flutter analyze
|
||||
```
|
||||
|
||||
2. **For Rust changes:**
|
||||
```bash
|
||||
cd rust
|
||||
cargo check # Quick validation
|
||||
flutter_rust_bridge_codegen generate # Regenerate bridge if API changed
|
||||
```
|
||||
|
||||
3. **For localization changes:**
|
||||
```bash
|
||||
flutter pub global run intl_utils:generate
|
||||
```
|
||||
|
||||
### Pre-commit Validation
|
||||
|
||||
Always run before committing changes:
|
||||
```bash
|
||||
flutter analyze # Dart linting (required for CI)
|
||||
cd rust && cargo clippy # Rust linting
|
||||
flutter test # Widget tests
|
||||
cd rust && cargo test # Rust tests
|
||||
```
|
||||
|
||||
**CI Pipeline will fail if:**
|
||||
- Flutter analyze reports errors
|
||||
- Rust clippy reports errors
|
||||
- Any tests fail
|
||||
- Build process fails
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Flutter/Dart Issues
|
||||
- **Build runner conflicts:** Delete generated files and rebuild with `--delete-conflicting-outputs`
|
||||
- **Dependency conflicts:** Run `flutter pub deps` to check for version conflicts
|
||||
- **Localization missing:** Ensure `flutter pub global run intl_utils:generate` completed successfully
|
||||
|
||||
### Rust Issues
|
||||
- **Bridge generation fails:** Verify flutter_rust_bridge_codegen is installed and Rust code compiles
|
||||
- **Link errors:** Ensure all required system libraries are installed for target platform
|
||||
- **API changes:** Regenerate bridge code after modifying Rust API signatures
|
||||
|
||||
### Build Performance
|
||||
- Use `cargo check` instead of `cargo build` for quick Rust validation
|
||||
- Use `flutter analyze` instead of full build for quick Dart validation
|
||||
- Clean build artifacts if experiencing issues: `flutter clean && cd rust && cargo clean`
|
||||
|
||||
## Application Architecture
|
||||
|
||||
**UI Framework:** Flutter with Fluent UI design system for Windows-native appearance
|
||||
**State Management:** Riverpod with code generation for type-safe state management
|
||||
**Routing:** GoRouter for navigation with nested routes
|
||||
**Localization:** Flutter i18n with custom tooling for community translations
|
||||
**Data Layer:** Hive for local storage, HTTP client for network requests
|
||||
**Native Integration:** Rust via flutter_rust_bridge for system operations and performance-critical code
|
||||
|
||||
## Key Development Commands Reference
|
||||
|
||||
```bash
|
||||
# Quick development validation (5-10 minutes total)
|
||||
flutter analyze && cd rust && cargo check
|
||||
|
||||
# Full build validation (45-60 minutes - NEVER CANCEL)
|
||||
flutter pub get && \
|
||||
dart run build_runner build --delete-conflicting-outputs && \
|
||||
cd rust && cargo update && cd .. && \
|
||||
flutter_rust_bridge_codegen generate && \
|
||||
flutter pub global run intl_utils:generate && \
|
||||
flutter build windows -v
|
||||
|
||||
# Code generation only (5-10 minutes)
|
||||
dart run build_runner build --delete-conflicting-outputs && \
|
||||
flutter_rust_bridge_codegen generate && \
|
||||
flutter pub global run intl_utils:generate
|
||||
|
||||
# Test execution (10-15 minutes total - NEVER CANCEL)
|
||||
flutter test && cd rust && cargo test
|
||||
```
|
||||
|
||||
**CRITICAL REMINDERS:**
|
||||
- NEVER CANCEL builds or long-running commands - they can take 45+ minutes
|
||||
- Always set command timeouts to 60+ minutes for build operations
|
||||
- Always set command timeouts to 30+ minutes for test operations
|
||||
- Validate changes with both quick checks and full builds before committing
|
||||
- Use Windows environment for complete functionality testing
|
||||
77
.github/workflows/linux_nightly.yml
vendored
Normal file
77
.github/workflows/linux_nightly.yml
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
name: "Linux Nightly Build"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # every day at midnight
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
# libwebkit2gtk-4.1-dev is required for wry webview (4.0 is not available in Ubuntu 24.04+)
|
||||
sudo apt-get install -y \
|
||||
clang cmake ninja-build pkg-config nasm \
|
||||
libgtk-3-dev liblzma-dev \
|
||||
libsecret-1-dev libjsoncpp-dev \
|
||||
libnotify-dev libayatana-appindicator3-dev \
|
||||
libwebkit2gtk-4.1-dev
|
||||
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
cache: true
|
||||
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' # optional, change this to force refresh cache
|
||||
cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' # optional, change this to specify the cache path
|
||||
|
||||
- run: flutter --version
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Rust Version
|
||||
run: |
|
||||
rustup --version
|
||||
cargo --version
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "rust"
|
||||
cache-all-crates: true
|
||||
|
||||
- name: Flutter pub get
|
||||
run: flutter pub get
|
||||
- name: Flutter build runner
|
||||
run: dart run build_runner build --delete-conflicting-outputs
|
||||
- name: Rust cargo update
|
||||
run: cargo update
|
||||
working-directory: rust
|
||||
|
||||
- name: Set up Flutter rust bridge
|
||||
run: |
|
||||
cargo install cargo-expand
|
||||
cargo install 'flutter_rust_bridge_codegen@^2.0.0-dev.0'
|
||||
|
||||
- name: Flutter Rust bridge generate
|
||||
run: flutter_rust_bridge_codegen generate
|
||||
- name: flutter gen l10n
|
||||
run: |
|
||||
flutter pub global activate intl_utils
|
||||
flutter pub global run intl_utils:generate
|
||||
- name: Flutter build Linux
|
||||
run: flutter build linux -v
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux
|
||||
path: build/linux/x64/release/bundle
|
||||
6
.github/workflows/windows_nightly.yml
vendored
6
.github/workflows/windows_nightly.yml
vendored
@ -40,7 +40,8 @@ jobs:
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: "18"
|
||||
|
||||
- name: Set up NASM
|
||||
uses: ilammy/setup-nasm@v1
|
||||
- name: Flutter pub get
|
||||
run: flutter pub get
|
||||
- name: Flutter build runner
|
||||
@ -61,11 +62,10 @@ jobs:
|
||||
flutter pub global activate intl_utils
|
||||
flutter pub global run intl_utils:generate
|
||||
- name: Flutter build Windows
|
||||
run: flutter build windows
|
||||
run: flutter build windows -v
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: build/windows/x64/runner/Release
|
||||
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -5,9 +5,11 @@
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.build/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
.swiftpm/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
@ -42,8 +44,9 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
/pubspec.lock
|
||||
/rust/target/
|
||||
/rust/Cargo.lock
|
||||
/lib/generated/l10n_temp.json
|
||||
/lib/generated/l10n_temp_fix.json
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
29
.metadata
29
.metadata
@ -1,11 +1,11 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
channel: stable
|
||||
revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
@ -13,11 +13,26 @@ project_type: app
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
- platform: android
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
- platform: ios
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
- platform: linux
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
- platform: macos
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
- platform: web
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
- platform: windows
|
||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
|
||||
|
||||
# User provided section
|
||||
|
||||
|
||||
65
.vscode/launch.json
vendored
Normal file
65
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "StarCitizenToolBox",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
},
|
||||
{
|
||||
"name": "StarCitizenToolBox (MSE mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"args": [
|
||||
"--dart-define=MSE=true"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "StarCitizenToolBox (profile mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "profile"
|
||||
},
|
||||
{
|
||||
"name": "StarCitizenToolBox (release mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "release"
|
||||
},
|
||||
{
|
||||
"name": "rust_builder",
|
||||
"cwd": "rust_builder",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
},
|
||||
{
|
||||
"name": "rust_builder (profile mode)",
|
||||
"cwd": "rust_builder",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "profile"
|
||||
},
|
||||
{
|
||||
"name": "rust_builder (release mode)",
|
||||
"cwd": "rust_builder",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "release"
|
||||
},
|
||||
{
|
||||
"name": "sct_dev_tools",
|
||||
"cwd": "packages\\sct_dev_tools",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
},
|
||||
{
|
||||
"name": "build_tool",
|
||||
"cwd": "rust_builder\\cargokit\\build_tool",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"dart.flutterSdkPath": ".fvm/versions/stable",
|
||||
"cmake.ignoreCMakeListsMissing": true
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
该工具为 星际公民玩家 提供 一键诊断,官网及工具网站汉化,游戏汉化,游戏性能优化 等功能,致力于带来更愉快的游戏体验。
|
||||
|
||||
[](https://github.com/StarCitizenToolBox/app/actions/workflows/windows_nightly.yml)
|
||||
[](https://github.com/StarCitizenToolBox/app/actions/workflows/windows_nightly.yml) [](https://github.com/StarCitizenToolBox/app/actions/workflows/windows_nightly.yml) [](http://translate.42kit.com/engage/sctoolbox/)
|
||||
|
||||
[](https://apps.microsoft.com/detail/9NF3SWFWNKL1?launch=true)
|
||||
|
||||
@ -26,6 +26,5 @@
|
||||

|
||||

|
||||
### ❤️ 鸣谢
|
||||

|
||||
|
||||
特别感谢 [JetBrains](https://www.jetbrains.com/?from=SCToolbox) 为开源项目提供免费的 IDE 授权。
|
||||
特别感谢 [Visual Studio Code](https://code.visualstudio.com/) 提供免费的开发工具。
|
||||
|
||||
@ -9,11 +9,14 @@ This tool provides Star Citizen players with one-click diagnosis, official websi
|
||||
|
||||
### ✨ Feature
|
||||
- Localization management: install the localization of the community and switch languages with one click
|
||||
- Advanced localization: You can choose to translate only parts of the game content, or display it in both languages.
|
||||
- Vehicle sorting: Utilizing game mechanics, vehicles are assigned numerical prefixes to help you quickly find your favorite spaceship.
|
||||
- One-click diagnosis: log files from hundreds of guinea pig users, which can handle common problems of Star Citizen
|
||||
- Website Chineseization: Provide manual translation for the Star Citizen official website and Star Citizen tool website (thanks to the Star Citizen Chinese Encyclopedia project), and also provide [Browser Extension (Github)] (https://github.com/xkeyC/StarCitizenBoxBrowserEx).
|
||||
- Website Localization: Provide manual translation for the Star Citizen official website and Star Citizen tool website (thanks to the Star Citizen Chinese Encyclopedia project), and also provide [Browser Extension (Github)] (https://github.com/xkeyC/StarCitizenBoxBrowserEx).
|
||||
- Performance optimization: Add more detailed performance parameter control to the Star Citizen game, which can be used to optimize performance and obtain better image quality.
|
||||
- Server status indicator: The server status indicator function is added a few hours earlier than the official website launcher, and the indication is more detailed.
|
||||
- Other commonly used tools: a toolbox including p4k offload downloads, cleaning shaders, reinstalling EAC and other functions.
|
||||
- unp4k_rs: Uses our own powerful engine to quickly unpack games and perform data mining.
|
||||
|
||||
### 📸 Screenshot
|
||||

|
||||
@ -27,6 +30,5 @@ This tool provides Star Citizen players with one-click diagnosis, official websi
|
||||

|
||||
|
||||
### ❤️ Thanks
|
||||

|
||||
|
||||
Special thanks to [JetBrains](https://www.jetbrains.com/?from=SCToolbox) for providing free IDE licenses for open source projects.
|
||||
Special thanks to [Visual Studio Code](https://code.visualstudio.com/) for providing free development tools.
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
# SC 工具箱
|
||||
# SC 工具箱 / SCToolBox
|
||||
|
||||
[简体中文](https://github.com/StarCitizenToolBox/app/blob/main/README.md) / [繁體中文](https://github.com/StarCitizenToolBox/app/blob/main/README_zh-TW.md)
|
||||
[简体中文](https://github.com/StarCitizenToolBox/app/blob/main/README.md) / [繁體中文](https://github.com/StarCitizenToolBox/app/blob/main/README_zh-TW.md) / [English](https://github.com/StarCitizenToolBox/app/blob/main/README_en.md)
|
||||
|
||||
此工具為星際公民玩家提供疑難排解、官網及工具網站中文翻譯、遊戲翻譯、效能最佳化/畫面改善等功能,帶給您更愉快的遊戲體驗。
|
||||
這個工具能為星際公民玩家提供疑難排解、官方網站及工具網站的中文翻譯、遊戲文本在地化翻譯、效能最佳化/畫面改善等功能,讓您獲得更愉快的遊戲體驗。
|
||||
|
||||
[](https://github.com/StarCitizenToolBox/app/actions/workflows/windows_nightly.yml)
|
||||
[](https://github.com/StarCitizenToolBox/app/actions/workflows/windows_nightly.yml) [](http://translate.42kit.com/engage/sctoolbox/)
|
||||
|
||||
[](https://apps.microsoft.com/detail/9NF3SWFWNKL1?launch=true)
|
||||
|
||||
|
||||
### ✨ 功能
|
||||
- 本地化社群翻譯管理:一鍵快速安裝社群翻譯與切換遊戲語言
|
||||
- 疑難排解:來自上百名小白鼠使用者的日誌文件,可處理常見星際公民問題
|
||||
- 網站中文翻譯:為星際公民官網,星際公民工具網站提供人工精翻 (感謝星際公民中文百科計畫),亦提供[瀏覽器擴充套件 (Github)](https://github.com/xkeyC/StarCitizenBoxBrowserEx )。
|
||||
- 效能最佳化/畫面改善:為星際公民遊戲增加更細緻的效能參數控制,可用於最佳化效能,也可用於獲得更好的畫質。
|
||||
- 伺服器狀態指示器:比官網啟動器早了幾個小時增加了伺服器狀態指示功能,且指示的更為細緻。
|
||||
- 其他常用工具:包括 p4k分流下載,清除著色器快取,重新安裝EAC等多種功能在內的工具箱。
|
||||
- 社群在地化翻譯管理:一鍵即可快速完成社群翻譯安裝與切換遊戲語言
|
||||
- 疑難排解:來自上百名小白鼠使用者的日誌文件,可處理常見的星際公民遊戲問題
|
||||
- 網站中文翻譯:為星際公民官方網站及工具網站提供人工精翻 (感謝星際公民中文百科計畫),亦提供[瀏覽器擴充套件 (Github)](https://github.com/xkeyC/StarCitizenBoxBrowserEx )。
|
||||
- 效能最佳化/畫面改善:為星際公民遊戲增加更細緻的效能參數調整,能夠用於改善遊戲效能,也可以用於獲得更好的畫質。
|
||||
- 伺服器狀態指示器:比官方啟動器早了幾個小時增加了伺服器狀態指示功能,且指示的更為細緻。
|
||||
- 其他常用工具:包括 p4k 分流下載,著色器快取清理,重新安裝 EAC 反外掛軟體等多種功能在內的工具箱。
|
||||
|
||||
### 📸 螢幕截圖
|
||||

|
||||
@ -27,6 +27,5 @@
|
||||

|
||||
|
||||
### ❤️ 鳴謝
|
||||

|
||||
|
||||
特別感謝 [JetBrains](https://www.jetbrains.com/?from=SCToolbox) 為開源專案提供免費的 IDE 授權。
|
||||
特別感謝 [Visual Studio Code](https://code.visualstudio.com/) 提供的免費開發工具。
|
||||
|
||||
@ -29,6 +29,12 @@ linter:
|
||||
analyzer:
|
||||
plugins:
|
||||
- custom_lint
|
||||
|
||||
exclude:
|
||||
- "**/*.g.dart"
|
||||
- "**/*.freezed.dart"
|
||||
- "**/generated/*.dart"
|
||||
- "**/generated/**/*.dart"
|
||||
errors:
|
||||
invalid_annotation_target: ignore
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/countdown/bar_citizen.png
Normal file
BIN
assets/countdown/bar_citizen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB |
BIN
assets/ic_hk_fps.png
Normal file
BIN
assets/ic_hk_fps.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
53
assets/web/input_method/index.html
Normal file
53
assets/web/input_method/index.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no"/>
|
||||
<meta name="renderer" content="webkit"/>
|
||||
|
||||
<link rel="stylesheet" href="style/mdui2.css">
|
||||
<link href="style/google_icons.css" rel="stylesheet">
|
||||
<script src="js/mdui2.global.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<title>SCToolBox Community Input Method Web</title>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mdui-theme-light" style="position: relative;overflow: hidden">
|
||||
<mdui-top-app-bar
|
||||
scroll-behavior="shrink"
|
||||
scroll-threshold="30"
|
||||
scroll-target=".scroll-behavior-shrink">
|
||||
|
||||
<mdui-top-app-bar-title>SC汉化盒子社区输入法</mdui-top-app-bar-title>
|
||||
<div style="flex-grow: 1"></div>
|
||||
|
||||
<mdui-button-icon icon="help" onclick="onShowHelp()"></mdui-button-icon>
|
||||
|
||||
|
||||
</mdui-top-app-bar>
|
||||
|
||||
<div class="scroll-behavior-shrink" style="overflow: auto;">
|
||||
|
||||
<mdui-text-field id="input_message" style="padding-top: 6pt;padding-bottom: 6pt" rows="6" variant="outlined"
|
||||
label="输入消息..."></mdui-text-field>
|
||||
|
||||
<div style="text-align: end">
|
||||
<mdui-checkbox id="auto_copy" checked>自动复制</mdui-checkbox>
|
||||
</div>
|
||||
|
||||
<mdui-button id="send_button" icon="send" onclick="onSendMessage()" full-width>发送</mdui-button>
|
||||
|
||||
</div>
|
||||
|
||||
<mdui-snackbar id="snackbar_message" auto-close-delay="1000">Text</mdui-snackbar>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
init();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
56
assets/web/input_method/js/main.js
Normal file
56
assets/web/input_method/js/main.js
Normal file
@ -0,0 +1,56 @@
|
||||
async function init() {
|
||||
try {
|
||||
let response = await fetch("/api");
|
||||
let responseJson = await response.json();
|
||||
if (responseJson.status === "ok") {
|
||||
showMessage("服务连接成功!");
|
||||
} else {
|
||||
showMessage("服务连接失败!" + responseJson);
|
||||
}
|
||||
} catch (e) {
|
||||
showMessage("服务连接失败!" + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function onSendMessage() {
|
||||
let send_button = document.getElementById("send_button");
|
||||
let input = document.getElementById("input_message");
|
||||
let isAutoCopy = document.getElementById("auto_copy").checked;
|
||||
let messageJson = {
|
||||
"text": input.value,
|
||||
"autoCopy": isAutoCopy,
|
||||
"autoInput": false
|
||||
};
|
||||
send_button.loading = true;
|
||||
try {
|
||||
let response = await fetch("/api/send", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(messageJson)
|
||||
});
|
||||
let responseJson = await response.json();
|
||||
console.log(responseJson);
|
||||
showMessage(responseJson.message);
|
||||
if (response.ok) {
|
||||
input.value = "";
|
||||
}
|
||||
} catch (e) {
|
||||
showMessage("发送失败!" + e);
|
||||
}
|
||||
send_button.loading = false;
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
let snack = document.getElementById("snackbar_message");
|
||||
snack.open = false;
|
||||
snack.innerText = message;
|
||||
snack.open = true;
|
||||
}
|
||||
|
||||
function onShowHelp() {
|
||||
alert("在浏览器中输入文本,将发送给汉化盒子转码。" +
|
||||
"\n\n自动复制:勾选后自动复制转码结果到剪贴板。");
|
||||
}
|
||||
22
assets/web/input_method/js/mdui2.global.js
Normal file
22
assets/web/input_method/js/mdui2.global.js
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/web/input_method/style/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
Normal file
BIN
assets/web/input_method/style/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
Normal file
Binary file not shown.
23
assets/web/input_method/style/google_icons.css
Normal file
23
assets/web/input_method/style/google_icons.css
Normal file
@ -0,0 +1,23 @@
|
||||
/* fallback */
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 1.5rem; /* 24px */
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
1
assets/web/input_method/style/mdui2.css
Normal file
1
assets/web/input_method/style/mdui2.css
Normal file
File diff suppressed because one or more lines are too long
@ -197,8 +197,6 @@ function GetSCLocalizationTranslateString(txtSrc) {
|
||||
return txtSrc
|
||||
}
|
||||
|
||||
InitWebLocalization();
|
||||
|
||||
function ReportUnTranslate(k, v) {
|
||||
|
||||
if (enable_webview_localization_capture) {
|
||||
@ -209,7 +207,7 @@ function ReportUnTranslate(k, v) {
|
||||
const jsRegex = /(?:^|[^<])<script[^>]*>[\s\S]*?<\/script>(?:[^>]|$)/i;
|
||||
if (k.trim() !== "" && !cnPattern.test(k) && !htmlPattern.test(k) && !cssRegex.test(k) && !jsRegex.test(k)
|
||||
&& enPattern.test(k) && !k.startsWith("http://") && !k.startsWith("https://")) {
|
||||
window.chrome.webview.postMessage({action: 'webview_localization_capture', key: k, value: v});
|
||||
window.ipc.postMessage(JSON.stringify({ action: 'webview_localization_capture', key: k, value: v }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,47 +217,90 @@ InitWebLocalization();
|
||||
|
||||
/// ----- Login Script ----
|
||||
async function getRSILauncherToken(channelId) {
|
||||
if (!window.location.href.includes("robertsspaceindustries.com")) return;
|
||||
console.log('[SCToolbox] getRSILauncherToken called with channel:', channelId);
|
||||
|
||||
let loginBodyElement = $(".c-form.c-signIn");
|
||||
loginBodyElement.hide();
|
||||
// check login
|
||||
let r = await fetch("api/launcher/v3/account/check", {
|
||||
try {
|
||||
if (!window.location.href.includes("robertsspaceindustries.com")) {
|
||||
console.log('[SCToolbox] Not on RSI site, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// check if logged in and fix redirect
|
||||
if (window.location.href.endsWith('/connect?jumpto=/account/dashboard')) {
|
||||
if (document.body.textContent.trim() === "/account/dashboard") {
|
||||
window.location.href = "https://robertsspaceindustries.com/account/dashboard";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for jQuery to be ready
|
||||
let waitCount = 0;
|
||||
while (typeof $ === 'undefined' && waitCount < 50) {
|
||||
console.log('[SCToolbox] Waiting for jQuery... attempt', waitCount);
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
waitCount++;
|
||||
}
|
||||
|
||||
if (typeof $ === 'undefined') {
|
||||
console.error('[SCToolbox] jQuery not available after waiting');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get RSI token from cookie (don't rely on $.cookie plugin)
|
||||
function getCookie(name) {
|
||||
const value = `; ${document.cookie}`;
|
||||
const parts = value.split(`; ${name}=`);
|
||||
if (parts.length === 2) return parts.pop().split(';').shift();
|
||||
return null;
|
||||
}
|
||||
|
||||
const rsiToken = getCookie('Rsi-Token');
|
||||
console.log('[SCToolbox] RSI Token available:', !!rsiToken);
|
||||
|
||||
if (!rsiToken) {
|
||||
console.log('[SCToolbox] No RSI token, showing login window');
|
||||
let loginBodyElement = $(".c-form authenticationForm sign_in");
|
||||
loginBodyElement.show();
|
||||
window.ipc.postMessage(JSON.stringify({ action: 'webview_rsi_login_show_window' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// get claims
|
||||
console.log('[SCToolbox] Fetching claims...');
|
||||
let claimsR = await fetch("https://robertsspaceindustries.com/api/launcher/v3/games/claims", {
|
||||
method: 'POST', headers: {
|
||||
'x-rsi-token': $.cookie('Rsi-Token'),
|
||||
'x-rsi-token': rsiToken,
|
||||
},
|
||||
});
|
||||
if (r.status !== 200) {
|
||||
loginBodyElement.show();
|
||||
// wait login
|
||||
window.chrome.webview.postMessage({action: 'webview_rsi_login_show_window'});
|
||||
|
||||
console.log('[SCToolbox] Claims response status:', claimsR.status);
|
||||
if (claimsR.status !== 200) {
|
||||
console.error('[SCToolbox] Claims request failed');
|
||||
return;
|
||||
}
|
||||
|
||||
SCTShowToast("登录游戏中...");
|
||||
|
||||
// get claims
|
||||
let claimsR = await fetch("api/launcher/v3/games/claims", {
|
||||
method: 'POST', headers: {
|
||||
'x-rsi-token': $.cookie('Rsi-Token'),
|
||||
},
|
||||
});
|
||||
if (claimsR.status !== 200) return;
|
||||
let claimsData = (await claimsR.json())["data"];
|
||||
console.log('[SCToolbox] Claims data received');
|
||||
|
||||
let tokenFormData = new FormData();
|
||||
tokenFormData.append('claims', claimsData);
|
||||
tokenFormData.append('gameId', 'SC');
|
||||
let tokenR = await fetch("api/launcher/v3/games/token", {
|
||||
let tokenR = await fetch("https://robertsspaceindustries.com/api/launcher/v3/games/token", {
|
||||
method: 'POST', headers: {
|
||||
'x-rsi-token': $.cookie('Rsi-Token'),
|
||||
'x-rsi-token': rsiToken,
|
||||
},
|
||||
body: tokenFormData
|
||||
});
|
||||
|
||||
if (tokenR.status !== 200) return;
|
||||
console.log('[SCToolbox] Token response status:', tokenR.status);
|
||||
if (tokenR.status !== 200) {
|
||||
console.error('[SCToolbox] Token request failed');
|
||||
return;
|
||||
}
|
||||
let TokenData = (await tokenR.json())["data"]["token"];
|
||||
console.log(TokenData);
|
||||
console.log('[SCToolbox] Token received');
|
||||
|
||||
// get release Data
|
||||
let releaseFormData = new FormData();
|
||||
@ -267,46 +308,58 @@ async function getRSILauncherToken(channelId) {
|
||||
releaseFormData.append("claims", claimsData);
|
||||
releaseFormData.append("gameId", "SC");
|
||||
releaseFormData.append("platformId", "prod");
|
||||
let releaseR = await fetch("api/launcher/v3/games/release", {
|
||||
let releaseR = await fetch("https://robertsspaceindustries.com/api/launcher/v3/games/release", {
|
||||
method: 'POST', headers: {
|
||||
'x-rsi-token': $.cookie('Rsi-Token'),
|
||||
},
|
||||
body: releaseFormData
|
||||
});
|
||||
if (releaseR.status !== 200) return;
|
||||
let releaseDataJson = (await releaseR.json())['data'];
|
||||
console.log(releaseDataJson);
|
||||
// get game library
|
||||
let libraryR = await fetch("api/launcher/v3/games/library", {
|
||||
method: 'POST', headers: {
|
||||
'x-rsi-token': $.cookie('Rsi-Token'),
|
||||
'x-rsi-token': rsiToken,
|
||||
},
|
||||
body: releaseFormData
|
||||
});
|
||||
|
||||
let libraryData = (await libraryR.json())["data"]
|
||||
console.log('[SCToolbox] Release response status:', releaseR.status);
|
||||
if (releaseR.status !== 200) {
|
||||
console.error('[SCToolbox] Release request failed');
|
||||
return;
|
||||
}
|
||||
let releaseDataJson = (await releaseR.json())['data'];
|
||||
console.log('[SCToolbox] Release data received');
|
||||
|
||||
// get game library
|
||||
let libraryR = await fetch("https://robertsspaceindustries.com/api/launcher/v3/games/library", {
|
||||
method: 'POST', headers: {
|
||||
'x-rsi-token': rsiToken,
|
||||
},
|
||||
body: releaseFormData
|
||||
});
|
||||
|
||||
let libraryData = (await libraryR.json())["data"];
|
||||
console.log('[SCToolbox] Library data received');
|
||||
|
||||
// get user avatar
|
||||
let $avatarElement = $(".c-account-sidebar__profile-metas-avatar");
|
||||
let avatarUrl = $avatarElement.css("background-image");
|
||||
let avatarUrl = $(".orion-c-avatar__image").attr("src") || '';
|
||||
console.log('[SCToolbox] Avatar URL:', avatarUrl);
|
||||
|
||||
//post message
|
||||
window.chrome.webview.postMessage({
|
||||
console.log('[SCToolbox] Sending login success message...');
|
||||
window.ipc.postMessage(JSON.stringify({
|
||||
action: 'webview_rsi_login_success', data: {
|
||||
'webToken': $.cookie('Rsi-Token'),
|
||||
'webToken': rsiToken,
|
||||
'claims': claimsData,
|
||||
'authToken': TokenData,
|
||||
'releaseInfo': releaseDataJson,
|
||||
"avatar": avatarUrl,
|
||||
'libraryData': libraryData,
|
||||
}
|
||||
});
|
||||
}));
|
||||
console.log('[SCToolbox] Login success message sent');
|
||||
} catch (error) {
|
||||
console.error('[SCToolbox] Error in getRSILauncherToken:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function SCTShowToast(message) {
|
||||
let m = document.createElement('div');
|
||||
m.innerHTML = message;
|
||||
m.style.cssText = "font-family:siyuan;max-width:60%;min-width: 150px;padding:0 14px;height: 40px;color: rgb(255, 255, 255);line-height: 40px;text-align: center;border-radius: 4px;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 999999;background: rgba(0, 0, 0,.7);font-size: 16px;";
|
||||
m.style.cssText = "font-family:siyuan;max-width:60%;min-width: 9.375rem;padding:0 0.875rem;height: 2.5rem;color: rgb(255, 255, 255);line-height: 2.5rem;text-align: center;border-radius: 0.25rem;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 999999;background: rgba(0, 0, 0,.7);font-size: 1rem;";
|
||||
document.body.appendChild(m);
|
||||
setTimeout(function () {
|
||||
let d = 0.5;
|
||||
|
||||
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
123
docs/AUTH_QUICK_START.md
Normal file
123
docs/AUTH_QUICK_START.md
Normal file
@ -0,0 +1,123 @@
|
||||
# SCToolBox OAuth 认证系统
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 授权流程
|
||||
|
||||
```
|
||||
┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐
|
||||
│ Web App │──1──▶│ Browser │──2──▶│ SCToolBox │──3──▶│ Server │
|
||||
└─────────┘ └──────────┘ └────────────┘ └──────────┘
|
||||
▲ │ │
|
||||
│ ├──4──▶ 验证域名 │
|
||||
│ │ 安全性 │
|
||||
│ │ │
|
||||
│ ├──5──▶ 生成 JWT │
|
||||
│ │ 令牌 │
|
||||
│ │ │
|
||||
└─────────────────6─────────────────┘ │
|
||||
返回令牌 │
|
||||
```
|
||||
|
||||
|
||||
### URL Scheme 格式
|
||||
```
|
||||
sctoolbox://auth/{domain}?callbackUrl={回调地址}
|
||||
```
|
||||
|
||||
### 示例
|
||||
```
|
||||
sctoolbox://auth/example.com?callbackUrl=https%3A%2F%2Fexample.com%2Fauth%2Fcallback
|
||||
```
|
||||
|
||||
### 回调格式
|
||||
```
|
||||
{callbackUrl}#access_token={jwt_token}&token_type=Bearer
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 基于 JWT 的安全认证
|
||||
- ✅ 域名白名单验证
|
||||
- ✅ 跨平台支持(Windows、macOS、Linux)
|
||||
- ✅ 两种授权方式(直接跳转 / 复制链接)
|
||||
- ✅ 符合 OAuth 2.0 Implicit Flow 标准
|
||||
|
||||
## 实现文件
|
||||
|
||||
### 核心文件
|
||||
- `lib/ui/auth/auth_page.dart` - 授权页面 UI
|
||||
- `lib/ui/auth/auth_ui_model.dart` - 授权页面状态管理
|
||||
- `lib/common/utils/url_scheme_handler.dart` - URL Scheme 处理器
|
||||
|
||||
### 平台配置
|
||||
- `macos/Runner/Info.plist` - macOS URL Scheme 配置
|
||||
- `windows/runner/main.cpp` - Windows Deep Link 处理
|
||||
- `linux/my_application.cc` - Linux Deep Link 处理
|
||||
- `linux/sctoolbox.desktop` - Linux MIME 类型注册
|
||||
- `pubspec.yaml` - MSIX 协议激活配置
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 初始化
|
||||
URL Scheme handler 在 `IndexUI` 中自动初始化:
|
||||
|
||||
```dart
|
||||
useEffect(() {
|
||||
UrlSchemeHandler().initialize(context);
|
||||
return () => UrlSchemeHandler().dispose();
|
||||
}, const []);
|
||||
```
|
||||
|
||||
### Web 应用集成
|
||||
|
||||
```javascript
|
||||
// 发起授权
|
||||
const authUrl = `sctoolbox://auth/example.com?callbackUrl=${encodeURIComponent(callbackUrl)}`;
|
||||
window.location.href = authUrl;
|
||||
|
||||
// 处理回调
|
||||
const params = new URLSearchParams(window.location.hash.substring(1));
|
||||
const token = params.get('access_token');
|
||||
```
|
||||
|
||||
## 平台要求
|
||||
|
||||
- **Windows**: 需要使用 MSIX 打包版本
|
||||
- **macOS**: 需要配置 Info.plist
|
||||
- **Linux**: 需要注册 .desktop 文件
|
||||
|
||||
## 安全性
|
||||
|
||||
- ✅ JWT 签名验证
|
||||
- ✅ 域名白名单检查
|
||||
- ✅ 令牌过期时间控制
|
||||
- ✅ 使用 Fragment (#) 传递令牌(更安全)
|
||||
|
||||
## 详细文档
|
||||
|
||||
查看 [完整文档](./AUTH_SYSTEM.md) 了解更多信息,包括:
|
||||
- 详细的授权流程
|
||||
- API 接口说明
|
||||
- Web 应用集成示例
|
||||
- 安全最佳实践
|
||||
- 常见问题解答
|
||||
|
||||
## API 端点
|
||||
|
||||
认证服务提供以下 gRPC 接口:
|
||||
|
||||
- `GenerateToken` - 生成 JWT 令牌
|
||||
- `ValidateToken` - 验证令牌有效性
|
||||
- `GetPublicKey` - 获取公钥用于验证
|
||||
- `GetJWTDomainList` - 获取可信域名列表
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
open "sctoolbox://auth/test.example.com?callbackUrl=https%3A%2F%2Ftest.example.com%2Fcallback"
|
||||
|
||||
# Windows
|
||||
start "sctoolbox://auth/test.example.com?callbackUrl=https%3A%2F%2Ftest.example.com%2Fcallback"
|
||||
```
|
||||
@ -2,3 +2,4 @@ rust_input: rust/src/api/**/*.rs
|
||||
dart_output: lib/common/rust/
|
||||
full_dep: true
|
||||
web: false
|
||||
enable_lifetime: true
|
||||
@ -6,7 +6,7 @@ import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class AnalyticsApi {
|
||||
static touch(String key) async {
|
||||
static Future<void> touch(String key) async {
|
||||
if (kDebugMode || kProfileMode) {
|
||||
dPrint("AnalyticsApi.touch === $key skip");
|
||||
return;
|
||||
|
||||
@ -1,26 +1,25 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/data/app_placard_data.dart';
|
||||
import 'package:starcitizen_doctor/data/app_torrent_data.dart';
|
||||
import 'package:starcitizen_doctor/data/app_version_data.dart';
|
||||
import 'package:starcitizen_doctor/data/countdown_festival_item_data.dart';
|
||||
import 'package:starcitizen_doctor/data/input_method_api_data.dart';
|
||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||
|
||||
class Api {
|
||||
static Future<AppVersionData> getAppVersion() async {
|
||||
return AppVersionData.fromJson(
|
||||
await getRepoJson("sc_doctor", "version.json"));
|
||||
return AppVersionData.fromJson(await getRepoJson("sc_doctor", "version.json"));
|
||||
}
|
||||
|
||||
static Future<AppPlacardData> getAppPlacard() async {
|
||||
return AppPlacardData.fromJson(
|
||||
await getRepoJson("sc_doctor", "placard.json"));
|
||||
return AppPlacardData.fromJson(await getRepoJson("sc_doctor", "placard.json"));
|
||||
}
|
||||
|
||||
static Future<List<CountdownFestivalItemData>>
|
||||
getFestivalCountdownList() async {
|
||||
static Future<List<CountdownFestivalItemData>> getFestivalCountdownList() async {
|
||||
List<CountdownFestivalItemData> l = [];
|
||||
final r = json.decode(await getRepoData("sc_doctor", "countdown.json"));
|
||||
if (r is List) {
|
||||
@ -28,19 +27,15 @@ class Api {
|
||||
l.add(CountdownFestivalItemData.fromJson(element));
|
||||
}
|
||||
}
|
||||
l.sort((a, b) => (a.time ?? 0) - (b.time ?? 0));
|
||||
return l;
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getAppReleaseDataByVersionName(
|
||||
String version) async {
|
||||
final r = await RSHttp.getText(
|
||||
"${URLConf.gitlabApiPath}repos/SCToolBox/Release/releases/tags/$version");
|
||||
static Future<Map<String, dynamic>> getAppReleaseDataByVersionName(String version) async {
|
||||
final r = await RSHttp.getText("${URLConf.gitlabApiPath}repos/SCToolBox/Release/releases/tags/$version");
|
||||
return json.decode(r);
|
||||
}
|
||||
|
||||
static Future<List<ScLocalizationData>> getScLocalizationData(
|
||||
String lang) async {
|
||||
static Future<List<ScLocalizationData>> getScLocalizationData(String lang) async {
|
||||
final data = json.decode(await getRepoData("localizations", "$lang.json"));
|
||||
List<ScLocalizationData> l = [];
|
||||
if (data is List) {
|
||||
@ -51,6 +46,15 @@ class Api {
|
||||
return l;
|
||||
}
|
||||
|
||||
static Future<InputMethodApiData> getCommunityInputMethodIndexData() async {
|
||||
final data = await getCommunityInputMethodData("index.json");
|
||||
return InputMethodApiData.fromJson(json.decode(data));
|
||||
}
|
||||
|
||||
static Future<String> getCommunityInputMethodData(String file) async {
|
||||
return getRepoData("input_method", file);
|
||||
}
|
||||
|
||||
static Future<List<AppTorrentData>> getAppTorrentDataList() async {
|
||||
final data = await getRepoData("sc_doctor", "torrent.json");
|
||||
final dataJson = json.decode(data);
|
||||
@ -69,20 +73,24 @@ class Api {
|
||||
}
|
||||
|
||||
static Future<List> getScServerStatus() async {
|
||||
final r = await RSHttp.getText(
|
||||
"https://status.robertsspaceindustries.com/index.json");
|
||||
final r = await RSHttp.getText("https://status.robertsspaceindustries.com/index.json");
|
||||
final map = json.decode(r);
|
||||
return map["systems"];
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getRepoJson(
|
||||
String dir, String name) async {
|
||||
static Future<Map<String, dynamic>> getRepoJson(String dir, String name) async {
|
||||
final data = await getRepoData(dir, name);
|
||||
return json.decode(data);
|
||||
}
|
||||
|
||||
static Future<String> getRepoData(String dir, String name) async {
|
||||
final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name");
|
||||
final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name", withCustomDns: await isUseInternalDNS());
|
||||
return r;
|
||||
}
|
||||
|
||||
static Future<bool> isUseInternalDNS() async {
|
||||
final userBox = await Hive.openBox("app_conf");
|
||||
final isUseInternalDNS = userBox.get("isUseInternalDNS", defaultValue: false);
|
||||
return isUseInternalDNS;
|
||||
}
|
||||
}
|
||||
|
||||
23
lib/api/news_api.dart
Normal file
23
lib/api/news_api.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../common/conf/url_conf.dart';
|
||||
import '../common/io/rs_http.dart';
|
||||
import '../common/utils/log.dart';
|
||||
import '../data/citizen_news_data.dart';
|
||||
import 'api.dart';
|
||||
|
||||
class NewsApi {
|
||||
static Future<CitizenNewsData?> getLatest() async {
|
||||
try {
|
||||
final data = await RSHttp.getText(
|
||||
"${URLConf.newsApiHome}/api/latest",
|
||||
withCustomDns: await Api.isUseInternalDNS(),
|
||||
);
|
||||
final map = json.decode(data);
|
||||
return CitizenNewsData.fromJson(map);
|
||||
} catch (e) {
|
||||
dPrint("getLatestNews error: $e");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_rss/dart_rss.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
|
||||
class RSSApi {
|
||||
static Future<List<RssItem>> getRssVideo() async {
|
||||
final r = await RSHttp.getText(URLConf.rssVideoUrl);
|
||||
final f = RssFeed.parse(r);
|
||||
return f.items.sublist(0, 8);
|
||||
}
|
||||
|
||||
static Future<List<RssItem>> getRssText() async {
|
||||
final r1 = await RSHttp.getText(URLConf.rssTextUrl1);
|
||||
final r1f = RssFeed.parse(r1);
|
||||
final r2 = await RSHttp.getText(URLConf.rssTextUrl2);
|
||||
final r2f = RssFeed.parse(r2);
|
||||
final items = r1f.items..addAll(r2f.items);
|
||||
items.sort((a, b) {
|
||||
final aDate = HttpDate.parse(a.pubDate ?? "").millisecondsSinceEpoch;
|
||||
final bDate = HttpDate.parse(b.pubDate ?? "").millisecondsSinceEpoch;
|
||||
return bDate - aDate;
|
||||
});
|
||||
return items;
|
||||
}
|
||||
}
|
||||
14
lib/api/udb.dart
Normal file
14
lib/api/udb.dart
Normal file
@ -0,0 +1,14 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/data/nav_api_data.dart';
|
||||
|
||||
class UDBNavApi {
|
||||
static Future<NavApiData> getNavItems({int pageNo = 1}) async {
|
||||
final r = await RSHttp.getText(URLConf.nav42KitUrl);
|
||||
if (r.isEmpty) throw "Network Error";
|
||||
final result = NavApiData.fromJson(jsonDecode(r));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
218
lib/app.dart
218
lib/app.dart
@ -2,15 +2,17 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hexcolor/hexcolor.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/ui/guide/guide_ui.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/performance/performance_ui.dart';
|
||||
import 'package:starcitizen_doctor/ui/splash_ui.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
@ -20,9 +22,10 @@ import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'api/analytics.dart';
|
||||
import 'api/api.dart';
|
||||
import 'common/helper/system_helper.dart';
|
||||
import 'common/conf/url_conf.dart';
|
||||
import 'common/io/rs_http.dart';
|
||||
import 'common/rust/frb_generated.dart';
|
||||
import 'common/rust/api/applinks_api.dart' as applinks;
|
||||
import 'common/rust/api/win32_api.dart' as win32;
|
||||
import 'data/app_version_data.dart';
|
||||
import 'generated/no_l10n_strings.dart';
|
||||
@ -31,6 +34,7 @@ import 'ui/home/game_doctor/game_doctor_ui.dart';
|
||||
import 'ui/home/localization/advanced_localization_ui.dart';
|
||||
import 'ui/index_ui.dart';
|
||||
import 'ui/settings/upgrade_dialog.dart';
|
||||
import 'ui/tools/unp4kc/dcb_viewer_ui.dart';
|
||||
import 'ui/tools/unp4kc/unp4kc_ui.dart';
|
||||
|
||||
part 'app.g.dart';
|
||||
@ -38,7 +42,7 @@ part 'app.g.dart';
|
||||
part 'app.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class AppGlobalState with _$AppGlobalState {
|
||||
abstract class AppGlobalState with _$AppGlobalState {
|
||||
const factory AppGlobalState({
|
||||
String? deviceUUID,
|
||||
String? applicationSupportDir,
|
||||
@ -47,50 +51,50 @@ class AppGlobalState with _$AppGlobalState {
|
||||
@Default(ThemeConf()) ThemeConf themeConf,
|
||||
Locale? appLocale,
|
||||
Box? appConfBox,
|
||||
@Default(10) windowsVersion,
|
||||
}) = _AppGlobalState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
GoRouter router(RouterRef ref) {
|
||||
GoRouter router(Ref ref) {
|
||||
return GoRouter(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const SplashUI()),
|
||||
),
|
||||
GoRoute(path: '/', pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI())),
|
||||
GoRoute(
|
||||
path: '/index',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const IndexUI()),
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const IndexUI()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "downloader",
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const HomeDownloaderUI())),
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'game_doctor',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const HomeGameDoctorUI()),
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomeGameDoctorUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'performance',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const HomePerformanceUI()),
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomePerformanceUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'advanced_localization',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const AdvancedLocalizationUI()))
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI()),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(path: '/tools', builder: (_, __) => const SizedBox(), routes: [
|
||||
GoRoute(
|
||||
path: 'unp4kc',
|
||||
path: '/tools',
|
||||
builder: (_, _) => const SizedBox(),
|
||||
routes: [
|
||||
GoRoute(path: 'unp4kc', pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI())),
|
||||
GoRoute(
|
||||
path: 'dcb_viewer',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const UnP4kcUI()),
|
||||
myPageBuilder(context, state, DcbViewerUI(initialFilePath: (state.extra as Map?)?['path'])),
|
||||
),
|
||||
]),
|
||||
],
|
||||
),
|
||||
GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI())),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -103,6 +107,7 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
const Locale("zh", "TW"): NoL10n.langZHT,
|
||||
const Locale("en"): NoL10n.langEn,
|
||||
const Locale("ja"): NoL10n.langJa,
|
||||
const Locale("ru"): NoL10n.langRU,
|
||||
};
|
||||
|
||||
@override
|
||||
@ -115,36 +120,22 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
Future<void> initApp() async {
|
||||
if (_initialized) return;
|
||||
// init Data
|
||||
final userProfileDir = Platform.environment["USERPROFILE"];
|
||||
final applicationSupportDir =
|
||||
(await getApplicationSupportDirectory()).absolute.path;
|
||||
String? applicationBinaryModuleDir;
|
||||
try {
|
||||
await initDPrintFile(applicationSupportDir);
|
||||
} catch (e) {
|
||||
dPrint("initDPrintFile Error: $e");
|
||||
}
|
||||
if (ConstConf.isMSE && userProfileDir != null) {
|
||||
applicationBinaryModuleDir =
|
||||
"$userProfileDir\\AppData\\Local\\Temp\\SCToolbox\\modules";
|
||||
} else {
|
||||
applicationBinaryModuleDir = "$applicationSupportDir\\modules";
|
||||
}
|
||||
dPrint("applicationSupportDir == $applicationSupportDir");
|
||||
dPrint("applicationBinaryModuleDir == $applicationBinaryModuleDir");
|
||||
state = state.copyWith(
|
||||
applicationSupportDir: applicationSupportDir,
|
||||
applicationBinaryModuleDir: applicationBinaryModuleDir,
|
||||
);
|
||||
final applicationSupportDir = await _initAppDir();
|
||||
|
||||
// init Rust bridge
|
||||
await RustLib.init();
|
||||
await RSHttp.init();
|
||||
dPrint("---- rust bridge init -----");
|
||||
|
||||
// Register URL scheme
|
||||
if ((!ConstConf.isMSE || kDebugMode) && Platform.isWindows) {
|
||||
await _registerUrlScheme();
|
||||
}
|
||||
|
||||
// init Hive
|
||||
try {
|
||||
Hive.init("$applicationSupportDir/db");
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
final box = await Hive.openBox("app_conf");
|
||||
state = state.copyWith(appConfBox: box);
|
||||
if (box.get("install_id", defaultValue: "") == "") {
|
||||
@ -169,14 +160,6 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// init powershell
|
||||
try {
|
||||
await SystemHelper.initPowershellPath();
|
||||
dPrint("---- Powershell init -----");
|
||||
} catch (e) {
|
||||
dPrint("powershell init failed : $e");
|
||||
}
|
||||
|
||||
// get windows info
|
||||
WindowsDeviceInfo? windowsDeviceInfo;
|
||||
try {
|
||||
@ -187,20 +170,25 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
}
|
||||
|
||||
// init windows
|
||||
|
||||
windowManager.waitUntilReadyToShow().then((_) async {
|
||||
await windowManager.setTitle("SCToolBox");
|
||||
await windowManager.setSize(const Size(1280, 810));
|
||||
await windowManager.setMinimumSize(const Size(1280, 810));
|
||||
await windowManager.setSkipTaskbar(false);
|
||||
await windowManager.show();
|
||||
await Window.initialize();
|
||||
await Window.hideWindowControls();
|
||||
if (Platform.isWindows) {
|
||||
if (windowsDeviceInfo?.productName.contains("Windows 11") ?? false) {
|
||||
await Window.setEffect(
|
||||
effect: WindowEffect.acrylic,
|
||||
);
|
||||
// Apply acrylic effect before showing window
|
||||
await Window.setEffect(effect: WindowEffect.acrylic, color: Colors.transparent, dark: true);
|
||||
state = state.copyWith(windowsVersion: 11);
|
||||
dPrint("---- Windows 11 Acrylic Effect applied -----");
|
||||
} else {
|
||||
state = state.copyWith(windowsVersion: 10);
|
||||
await Window.setEffect(effect: WindowEffect.disabled);
|
||||
}
|
||||
} else {
|
||||
state = state.copyWith(windowsVersion: 9);
|
||||
await Window.setEffect(effect: WindowEffect.disabled);
|
||||
}
|
||||
// Show window after acrylic effect is applied
|
||||
await windowManager.show();
|
||||
});
|
||||
|
||||
dPrint("---- Window init -----");
|
||||
@ -212,6 +200,8 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
return "${state.applicationSupportDir}/._upgrade";
|
||||
}
|
||||
|
||||
bool isInOnlineMode() => state.networkVersionData != null;
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future<bool> checkUpdate(BuildContext context) async {
|
||||
if (!ConstConf.isMSE) {
|
||||
@ -225,15 +215,18 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
|
||||
try {
|
||||
final networkVersionData = await Api.getAppVersion();
|
||||
dPrint("networkVersionData == ${networkVersionData.toJson()}");
|
||||
AppConf.setNetworkChannels(networkVersionData.gameChannels);
|
||||
checkActivityThemeColor(networkVersionData);
|
||||
if (ConstConf.isMSE) {
|
||||
dPrint(
|
||||
"lastVersion=${networkVersionData.mSELastVersion} ${networkVersionData.mSELastVersionCode}");
|
||||
dPrint("lastVersion=${networkVersionData.mSELastVersion} ${networkVersionData.mSELastVersionCode}");
|
||||
} else {
|
||||
dPrint(
|
||||
"lastVersion=${networkVersionData.lastVersion} ${networkVersionData.lastVersionCode}");
|
||||
dPrint("lastVersion=${networkVersionData.lastVersion} ${networkVersionData.lastVersionCode}");
|
||||
}
|
||||
state = state.copyWith(networkVersionData: networkVersionData);
|
||||
if (networkVersionData.nav42KitUrl != null) {
|
||||
URLConf.nav42KitUrl = networkVersionData.nav42KitUrl!;
|
||||
}
|
||||
} catch (e) {
|
||||
checkUpdateError = e;
|
||||
dPrint("_checkUpdate Error:$e");
|
||||
@ -244,10 +237,11 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
if (!context.mounted) return false;
|
||||
await showToast(
|
||||
context,
|
||||
S.current.app_common_network_error(
|
||||
ConstConf.appVersionDate, checkUpdateError.toString()));
|
||||
S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!Platform.isWindows) return false;
|
||||
final lastVersion = ConstConf.isMSE
|
||||
? state.networkVersionData?.mSELastVersionCode
|
||||
: state.networkVersionData?.lastVersionCode;
|
||||
@ -258,7 +252,8 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
final r = await showDialog(
|
||||
dismissWithEsc: false,
|
||||
context: context,
|
||||
builder: (context) => const UpgradeDialogUI());
|
||||
builder: (context) => const UpgradeDialogUI(),
|
||||
);
|
||||
|
||||
if (r != true) {
|
||||
if (!context.mounted) return false;
|
||||
@ -272,7 +267,9 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
|
||||
Timer? _activityThemeColorTimer;
|
||||
|
||||
checkActivityThemeColor(AppVersionData networkVersionData) {
|
||||
void checkActivityThemeColor(AppVersionData networkVersionData) {
|
||||
final isDisableAcrylic = state.windowsVersion <= 11;
|
||||
|
||||
if (_activityThemeColorTimer != null) {
|
||||
_activityThemeColorTimer?.cancel();
|
||||
_activityThemeColorTimer = null;
|
||||
@ -285,8 +282,10 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
|
||||
dPrint("now == $now start == $startTime end == $endTime");
|
||||
if (now < startTime) {
|
||||
_activityThemeColorTimer = Timer(Duration(milliseconds: startTime - now),
|
||||
() => checkActivityThemeColor(networkVersionData));
|
||||
_activityThemeColorTimer = Timer(
|
||||
Duration(milliseconds: startTime - now),
|
||||
() => checkActivityThemeColor(networkVersionData),
|
||||
);
|
||||
dPrint("start Timer ....");
|
||||
} else if (now >= startTime && now <= endTime) {
|
||||
dPrint("update Color ....");
|
||||
@ -294,29 +293,30 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
final colorCfg = networkVersionData.activityColors;
|
||||
state = state.copyWith(
|
||||
themeConf: ThemeConf(
|
||||
backgroundColor:
|
||||
HexColor(colorCfg?.background ?? "#132431").withOpacity(.75),
|
||||
menuColor: HexColor(colorCfg?.menu ?? "#132431").withOpacity(.95),
|
||||
backgroundColor: HexColor(colorCfg?.background ?? "#132431").withValues(alpha: isDisableAcrylic ? 1 : .75),
|
||||
menuColor: HexColor(colorCfg?.menu ?? "#132431").withValues(alpha: isDisableAcrylic ? 1 : .95),
|
||||
micaColor: HexColor(colorCfg?.mica ?? "#0A3142"),
|
||||
),
|
||||
);
|
||||
|
||||
// wait for end
|
||||
_activityThemeColorTimer = Timer(Duration(milliseconds: endTime - now),
|
||||
() => checkActivityThemeColor(networkVersionData));
|
||||
_activityThemeColorTimer = Timer(
|
||||
Duration(milliseconds: endTime - now),
|
||||
() => checkActivityThemeColor(networkVersionData),
|
||||
);
|
||||
} else {
|
||||
dPrint("reset Color ....");
|
||||
state = state.copyWith(
|
||||
themeConf: ThemeConf(
|
||||
backgroundColor: HexColor("#132431").withOpacity(.75),
|
||||
menuColor: HexColor("#132431").withOpacity(.95),
|
||||
backgroundColor: HexColor("#132431").withValues(alpha: isDisableAcrylic ? 1 : .75),
|
||||
menuColor: HexColor("#132431").withValues(alpha: isDisableAcrylic ? 1 : .95),
|
||||
micaColor: HexColor("#0A3142"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void changeLocale(value) async {
|
||||
void changeLocale(dynamic value) async {
|
||||
final appConfBox = await Hive.openBox("app_conf");
|
||||
if (value is Locale) {
|
||||
if (value.languageCode == "auto") {
|
||||
@ -332,10 +332,66 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
state = state.copyWith(appLocale: value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register sctoolbox:// URL scheme for non-MSE builds
|
||||
Future<void> _registerUrlScheme() async {
|
||||
try {
|
||||
const scheme = "sctoolbox";
|
||||
const appName = "SCToolBox";
|
||||
final result = await applinks.registerApplinks(scheme: scheme, appName: appName);
|
||||
if (result.success) {
|
||||
if (result.wasModified) {
|
||||
dPrint("URL scheme '$scheme' registered successfully: ${result.message}");
|
||||
} else {
|
||||
dPrint("URL scheme '$scheme' already registered: ${result.message}");
|
||||
}
|
||||
} else {
|
||||
dPrint("URL scheme '$scheme' registration check: ${result.message}");
|
||||
// Even if check fails, the registration might have succeeded
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("Failed to register URL scheme: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _initAppDir() async {
|
||||
if (Platform.isWindows) {
|
||||
final userProfileDir = Platform.environment["USERPROFILE"];
|
||||
final applicationSupportDir = (await getApplicationSupportDirectory()).absolute.path;
|
||||
String? applicationBinaryModuleDir;
|
||||
try {
|
||||
await initDPrintFile(applicationSupportDir);
|
||||
} catch (e) {
|
||||
dPrint("initDPrintFile Error: $e");
|
||||
}
|
||||
if (ConstConf.isMSE && userProfileDir != null) {
|
||||
applicationBinaryModuleDir = "$userProfileDir\\AppData\\Local\\Temp\\SCToolbox\\modules";
|
||||
} else {
|
||||
applicationBinaryModuleDir = "$applicationSupportDir\\modules";
|
||||
}
|
||||
dPrint("applicationSupportDir == $applicationSupportDir");
|
||||
dPrint("applicationBinaryModuleDir == $applicationBinaryModuleDir");
|
||||
state = state.copyWith(
|
||||
applicationSupportDir: applicationSupportDir,
|
||||
applicationBinaryModuleDir: applicationBinaryModuleDir,
|
||||
);
|
||||
return applicationSupportDir;
|
||||
} else {
|
||||
final applicationSupportDir = (await getApplicationSupportDirectory()).absolute.path;
|
||||
final applicationBinaryModuleDir = "$applicationSupportDir/modules";
|
||||
dPrint("applicationSupportDir == $applicationSupportDir");
|
||||
dPrint("applicationBinaryModuleDir == $applicationBinaryModuleDir");
|
||||
state = state.copyWith(
|
||||
applicationSupportDir: applicationSupportDir,
|
||||
applicationBinaryModuleDir: applicationBinaryModuleDir,
|
||||
);
|
||||
return applicationSupportDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ThemeConf with _$ThemeConf {
|
||||
abstract class ThemeConf with _$ThemeConf {
|
||||
const factory ThemeConf({
|
||||
@Default(Color(0xbf132431)) Color backgroundColor,
|
||||
@Default(Color(0xf2132431)) Color menuColor,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
@ -9,441 +9,565 @@ part of 'app.dart';
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AppGlobalState {
|
||||
String? get deviceUUID => throw _privateConstructorUsedError;
|
||||
String? get applicationSupportDir => throw _privateConstructorUsedError;
|
||||
String? get applicationBinaryModuleDir => throw _privateConstructorUsedError;
|
||||
AppVersionData? get networkVersionData => throw _privateConstructorUsedError;
|
||||
ThemeConf get themeConf => throw _privateConstructorUsedError;
|
||||
Locale? get appLocale => throw _privateConstructorUsedError;
|
||||
Box<dynamic>? get appConfBox => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$AppGlobalStateCopyWith<AppGlobalState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox; dynamic get windowsVersion;
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$AppGlobalStateCopyWith<AppGlobalState> get copyWith => _$AppGlobalStateCopyWithImpl<AppGlobalState>(this as AppGlobalState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AppGlobalStateCopyWith<$Res> {
|
||||
factory $AppGlobalStateCopyWith(
|
||||
AppGlobalState value, $Res Function(AppGlobalState) then) =
|
||||
_$AppGlobalStateCopyWithImpl<$Res, AppGlobalState>;
|
||||
abstract mixin class $AppGlobalStateCopyWith<$Res> {
|
||||
factory $AppGlobalStateCopyWith(AppGlobalState value, $Res Function(AppGlobalState) _then) = _$AppGlobalStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? deviceUUID,
|
||||
String? applicationSupportDir,
|
||||
String? applicationBinaryModuleDir,
|
||||
AppVersionData? networkVersionData,
|
||||
ThemeConf themeConf,
|
||||
Locale? appLocale,
|
||||
Box<dynamic>? appConfBox});
|
||||
$Res call({
|
||||
String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
$ThemeConfCopyWith<$Res> get themeConf;
|
||||
}
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$AppGlobalStateCopyWithImpl<$Res, $Val extends AppGlobalState>
|
||||
class _$AppGlobalStateCopyWithImpl<$Res>
|
||||
implements $AppGlobalStateCopyWith<$Res> {
|
||||
_$AppGlobalStateCopyWithImpl(this._value, this._then);
|
||||
_$AppGlobalStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
final AppGlobalState _self;
|
||||
final $Res Function(AppGlobalState) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? deviceUUID = freezed,
|
||||
Object? applicationSupportDir = freezed,
|
||||
Object? applicationBinaryModuleDir = freezed,
|
||||
Object? networkVersionData = freezed,
|
||||
Object? themeConf = null,
|
||||
Object? appLocale = freezed,
|
||||
Object? appConfBox = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
deviceUUID: freezed == deviceUUID
|
||||
? _value.deviceUUID
|
||||
: deviceUUID // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
applicationSupportDir: freezed == applicationSupportDir
|
||||
? _value.applicationSupportDir
|
||||
: applicationSupportDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
applicationBinaryModuleDir: freezed == applicationBinaryModuleDir
|
||||
? _value.applicationBinaryModuleDir
|
||||
: applicationBinaryModuleDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
networkVersionData: freezed == networkVersionData
|
||||
? _value.networkVersionData
|
||||
: networkVersionData // ignore: cast_nullable_to_non_nullable
|
||||
as AppVersionData?,
|
||||
themeConf: null == themeConf
|
||||
? _value.themeConf
|
||||
: themeConf // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeConf,
|
||||
appLocale: freezed == appLocale
|
||||
? _value.appLocale
|
||||
: appLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,
|
||||
appConfBox: freezed == appConfBox
|
||||
? _value.appConfBox
|
||||
: appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box<dynamic>?,
|
||||
) as $Val);
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable
|
||||
as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,applicationBinaryModuleDir: freezed == applicationBinaryModuleDir ? _self.applicationBinaryModuleDir : applicationBinaryModuleDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,networkVersionData: freezed == networkVersionData ? _self.networkVersionData : networkVersionData // ignore: cast_nullable_to_non_nullable
|
||||
as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ThemeConfCopyWith<$Res> get themeConf {
|
||||
return $ThemeConfCopyWith<$Res>(_value.themeConf, (value) {
|
||||
return _then(_value.copyWith(themeConf: value) as $Val);
|
||||
|
||||
return $ThemeConfCopyWith<$Res>(_self.themeConf, (value) {
|
||||
return _then(_self.copyWith(themeConf: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [AppGlobalState].
|
||||
extension AppGlobalStatePatterns on AppGlobalState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppGlobalState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppGlobalState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppGlobalState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState() when $default != null:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState():
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState() when $default != null:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _AppGlobalState implements AppGlobalState {
|
||||
const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox, this.windowsVersion = 10});
|
||||
|
||||
|
||||
@override final String? deviceUUID;
|
||||
@override final String? applicationSupportDir;
|
||||
@override final String? applicationBinaryModuleDir;
|
||||
@override final AppVersionData? networkVersionData;
|
||||
@override@JsonKey() final ThemeConf themeConf;
|
||||
@override final Locale? appLocale;
|
||||
@override final Box? appConfBox;
|
||||
@override@JsonKey() final dynamic windowsVersion;
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$AppGlobalStateCopyWith<_AppGlobalState> get copyWith => __$AppGlobalStateCopyWithImpl<_AppGlobalState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$AppGlobalStateCopyWith<$Res> implements $AppGlobalStateCopyWith<$Res> {
|
||||
factory _$AppGlobalStateCopyWith(_AppGlobalState value, $Res Function(_AppGlobalState) _then) = __$AppGlobalStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
@override $ThemeConfCopyWith<$Res> get themeConf;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$AppGlobalStateCopyWithImpl<$Res>
|
||||
implements _$AppGlobalStateCopyWith<$Res> {
|
||||
__$AppGlobalStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _AppGlobalState _self;
|
||||
final $Res Function(_AppGlobalState) _then;
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_AppGlobalState(
|
||||
deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable
|
||||
as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,applicationBinaryModuleDir: freezed == applicationBinaryModuleDir ? _self.applicationBinaryModuleDir : applicationBinaryModuleDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,networkVersionData: freezed == networkVersionData ? _self.networkVersionData : networkVersionData // ignore: cast_nullable_to_non_nullable
|
||||
as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ThemeConfCopyWith<$Res> get themeConf {
|
||||
|
||||
return $ThemeConfCopyWith<$Res>(_self.themeConf, (value) {
|
||||
return _then(_self.copyWith(themeConf: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AppGlobalStateImplCopyWith<$Res>
|
||||
implements $AppGlobalStateCopyWith<$Res> {
|
||||
factory _$$AppGlobalStateImplCopyWith(_$AppGlobalStateImpl value,
|
||||
$Res Function(_$AppGlobalStateImpl) then) =
|
||||
__$$AppGlobalStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? deviceUUID,
|
||||
String? applicationSupportDir,
|
||||
String? applicationBinaryModuleDir,
|
||||
AppVersionData? networkVersionData,
|
||||
ThemeConf themeConf,
|
||||
Locale? appLocale,
|
||||
Box<dynamic>? appConfBox});
|
||||
|
||||
@override
|
||||
$ThemeConfCopyWith<$Res> get themeConf;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AppGlobalStateImplCopyWithImpl<$Res>
|
||||
extends _$AppGlobalStateCopyWithImpl<$Res, _$AppGlobalStateImpl>
|
||||
implements _$$AppGlobalStateImplCopyWith<$Res> {
|
||||
__$$AppGlobalStateImplCopyWithImpl(
|
||||
_$AppGlobalStateImpl _value, $Res Function(_$AppGlobalStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
mixin _$ThemeConf {
|
||||
|
||||
Color get backgroundColor; Color get menuColor; Color get micaColor;
|
||||
/// Create a copy of ThemeConf
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? deviceUUID = freezed,
|
||||
Object? applicationSupportDir = freezed,
|
||||
Object? applicationBinaryModuleDir = freezed,
|
||||
Object? networkVersionData = freezed,
|
||||
Object? themeConf = null,
|
||||
Object? appLocale = freezed,
|
||||
Object? appConfBox = freezed,
|
||||
}) {
|
||||
return _then(_$AppGlobalStateImpl(
|
||||
deviceUUID: freezed == deviceUUID
|
||||
? _value.deviceUUID
|
||||
: deviceUUID // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
applicationSupportDir: freezed == applicationSupportDir
|
||||
? _value.applicationSupportDir
|
||||
: applicationSupportDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
applicationBinaryModuleDir: freezed == applicationBinaryModuleDir
|
||||
? _value.applicationBinaryModuleDir
|
||||
: applicationBinaryModuleDir // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
networkVersionData: freezed == networkVersionData
|
||||
? _value.networkVersionData
|
||||
: networkVersionData // ignore: cast_nullable_to_non_nullable
|
||||
as AppVersionData?,
|
||||
themeConf: null == themeConf
|
||||
? _value.themeConf
|
||||
: themeConf // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeConf,
|
||||
appLocale: freezed == appLocale
|
||||
? _value.appLocale
|
||||
: appLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,
|
||||
appConfBox: freezed == appConfBox
|
||||
? _value.appConfBox
|
||||
: appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box<dynamic>?,
|
||||
));
|
||||
}
|
||||
}
|
||||
$ThemeConfCopyWith<ThemeConf> get copyWith => _$ThemeConfCopyWithImpl<ThemeConf>(this as ThemeConf, _$identity);
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$AppGlobalStateImpl implements _AppGlobalState {
|
||||
const _$AppGlobalStateImpl(
|
||||
{this.deviceUUID,
|
||||
this.applicationSupportDir,
|
||||
this.applicationBinaryModuleDir,
|
||||
this.networkVersionData,
|
||||
this.themeConf = const ThemeConf(),
|
||||
this.appLocale,
|
||||
this.appConfBox});
|
||||
|
||||
@override
|
||||
final String? deviceUUID;
|
||||
@override
|
||||
final String? applicationSupportDir;
|
||||
@override
|
||||
final String? applicationBinaryModuleDir;
|
||||
@override
|
||||
final AppVersionData? networkVersionData;
|
||||
@override
|
||||
@JsonKey()
|
||||
final ThemeConf themeConf;
|
||||
@override
|
||||
final Locale? appLocale;
|
||||
@override
|
||||
final Box<dynamic>? appConfBox;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AppGlobalStateImpl &&
|
||||
(identical(other.deviceUUID, deviceUUID) ||
|
||||
other.deviceUUID == deviceUUID) &&
|
||||
(identical(other.applicationSupportDir, applicationSupportDir) ||
|
||||
other.applicationSupportDir == applicationSupportDir) &&
|
||||
(identical(other.applicationBinaryModuleDir,
|
||||
applicationBinaryModuleDir) ||
|
||||
other.applicationBinaryModuleDir ==
|
||||
applicationBinaryModuleDir) &&
|
||||
(identical(other.networkVersionData, networkVersionData) ||
|
||||
other.networkVersionData == networkVersionData) &&
|
||||
(identical(other.themeConf, themeConf) ||
|
||||
other.themeConf == themeConf) &&
|
||||
(identical(other.appLocale, appLocale) ||
|
||||
other.appLocale == appLocale) &&
|
||||
(identical(other.appConfBox, appConfBox) ||
|
||||
other.appConfBox == appConfBox));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeConf&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
deviceUUID,
|
||||
applicationSupportDir,
|
||||
applicationBinaryModuleDir,
|
||||
networkVersionData,
|
||||
themeConf,
|
||||
appLocale,
|
||||
appConfBox);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AppGlobalStateImplCopyWith<_$AppGlobalStateImpl> get copyWith =>
|
||||
__$$AppGlobalStateImplCopyWithImpl<_$AppGlobalStateImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _AppGlobalState implements AppGlobalState {
|
||||
const factory _AppGlobalState(
|
||||
{final String? deviceUUID,
|
||||
final String? applicationSupportDir,
|
||||
final String? applicationBinaryModuleDir,
|
||||
final AppVersionData? networkVersionData,
|
||||
final ThemeConf themeConf,
|
||||
final Locale? appLocale,
|
||||
final Box<dynamic>? appConfBox}) = _$AppGlobalStateImpl;
|
||||
|
||||
@override
|
||||
String? get deviceUUID;
|
||||
@override
|
||||
String? get applicationSupportDir;
|
||||
@override
|
||||
String? get applicationBinaryModuleDir;
|
||||
@override
|
||||
AppVersionData? get networkVersionData;
|
||||
@override
|
||||
ThemeConf get themeConf;
|
||||
@override
|
||||
Locale? get appLocale;
|
||||
@override
|
||||
Box<dynamic>? get appConfBox;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$AppGlobalStateImplCopyWith<_$AppGlobalStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ThemeConf {
|
||||
Color get backgroundColor => throw _privateConstructorUsedError;
|
||||
Color get menuColor => throw _privateConstructorUsedError;
|
||||
Color get micaColor => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$ThemeConfCopyWith<ThemeConf> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ThemeConfCopyWith<$Res> {
|
||||
factory $ThemeConfCopyWith(ThemeConf value, $Res Function(ThemeConf) then) =
|
||||
_$ThemeConfCopyWithImpl<$Res, ThemeConf>;
|
||||
@useResult
|
||||
$Res call({Color backgroundColor, Color menuColor, Color micaColor});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ThemeConfCopyWithImpl<$Res, $Val extends ThemeConf>
|
||||
implements $ThemeConfCopyWith<$Res> {
|
||||
_$ThemeConfCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? backgroundColor = null,
|
||||
Object? menuColor = null,
|
||||
Object? micaColor = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
backgroundColor: null == backgroundColor
|
||||
? _value.backgroundColor
|
||||
: backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
menuColor: null == menuColor
|
||||
? _value.menuColor
|
||||
: menuColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
micaColor: null == micaColor
|
||||
? _value.micaColor
|
||||
: micaColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ThemeConfImplCopyWith<$Res>
|
||||
implements $ThemeConfCopyWith<$Res> {
|
||||
factory _$$ThemeConfImplCopyWith(
|
||||
_$ThemeConfImpl value, $Res Function(_$ThemeConfImpl) then) =
|
||||
__$$ThemeConfImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({Color backgroundColor, Color menuColor, Color micaColor});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ThemeConfImplCopyWithImpl<$Res>
|
||||
extends _$ThemeConfCopyWithImpl<$Res, _$ThemeConfImpl>
|
||||
implements _$$ThemeConfImplCopyWith<$Res> {
|
||||
__$$ThemeConfImplCopyWithImpl(
|
||||
_$ThemeConfImpl _value, $Res Function(_$ThemeConfImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? backgroundColor = null,
|
||||
Object? menuColor = null,
|
||||
Object? micaColor = null,
|
||||
}) {
|
||||
return _then(_$ThemeConfImpl(
|
||||
backgroundColor: null == backgroundColor
|
||||
? _value.backgroundColor
|
||||
: backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
menuColor: null == menuColor
|
||||
? _value.menuColor
|
||||
: menuColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
micaColor: null == micaColor
|
||||
? _value.micaColor
|
||||
: micaColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ThemeConfImpl implements _ThemeConf {
|
||||
const _$ThemeConfImpl(
|
||||
{this.backgroundColor = const Color(0xbf132431),
|
||||
this.menuColor = const Color(0xf2132431),
|
||||
this.micaColor = const Color(0xff0a3142)});
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final Color backgroundColor;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Color menuColor;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Color micaColor;
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThemeConf(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $ThemeConfCopyWith<$Res> {
|
||||
factory $ThemeConfCopyWith(ThemeConf value, $Res Function(ThemeConf) _then) = _$ThemeConfCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
Color backgroundColor, Color menuColor, Color micaColor
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$ThemeConfCopyWithImpl<$Res>
|
||||
implements $ThemeConfCopyWith<$Res> {
|
||||
_$ThemeConfCopyWithImpl(this._self, this._then);
|
||||
|
||||
final ThemeConf _self;
|
||||
final $Res Function(ThemeConf) _then;
|
||||
|
||||
/// Create a copy of ThemeConf
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [ThemeConf].
|
||||
extension ThemeConfPatterns on ThemeConf {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ThemeConf value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ThemeConf() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ThemeConf value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ThemeConf():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ThemeConf value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ThemeConf() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Color backgroundColor, Color menuColor, Color micaColor)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ThemeConf() when $default != null:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Color backgroundColor, Color menuColor, Color micaColor) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ThemeConf():
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Color backgroundColor, Color menuColor, Color micaColor)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ThemeConf() when $default != null:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _ThemeConf implements ThemeConf {
|
||||
const _ThemeConf({this.backgroundColor = const Color(0xbf132431), this.menuColor = const Color(0xf2132431), this.micaColor = const Color(0xff0a3142)});
|
||||
|
||||
|
||||
@override@JsonKey() final Color backgroundColor;
|
||||
@override@JsonKey() final Color menuColor;
|
||||
@override@JsonKey() final Color micaColor;
|
||||
|
||||
/// Create a copy of ThemeConf
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$ThemeConfCopyWith<_ThemeConf> get copyWith => __$ThemeConfCopyWithImpl<_ThemeConf>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ThemeConfImpl &&
|
||||
(identical(other.backgroundColor, backgroundColor) ||
|
||||
other.backgroundColor == backgroundColor) &&
|
||||
(identical(other.menuColor, menuColor) ||
|
||||
other.menuColor == menuColor) &&
|
||||
(identical(other.micaColor, micaColor) ||
|
||||
other.micaColor == micaColor));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ThemeConf&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, backgroundColor, menuColor, micaColor);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ThemeConfImplCopyWith<_$ThemeConfImpl> get copyWith =>
|
||||
__$$ThemeConfImplCopyWithImpl<_$ThemeConfImpl>(this, _$identity);
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThemeConf(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor)';
|
||||
}
|
||||
|
||||
abstract class _ThemeConf implements ThemeConf {
|
||||
const factory _ThemeConf(
|
||||
{final Color backgroundColor,
|
||||
final Color menuColor,
|
||||
final Color micaColor}) = _$ThemeConfImpl;
|
||||
|
||||
@override
|
||||
Color get backgroundColor;
|
||||
@override
|
||||
Color get menuColor;
|
||||
@override
|
||||
Color get micaColor;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ThemeConfImplCopyWith<_$ThemeConfImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$ThemeConfCopyWith<$Res> implements $ThemeConfCopyWith<$Res> {
|
||||
factory _$ThemeConfCopyWith(_ThemeConf value, $Res Function(_ThemeConf) _then) = __$ThemeConfCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
Color backgroundColor, Color menuColor, Color micaColor
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$ThemeConfCopyWithImpl<$Res>
|
||||
implements _$ThemeConfCopyWith<$Res> {
|
||||
__$ThemeConfCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _ThemeConf _self;
|
||||
final $Res Function(_ThemeConf) _then;
|
||||
|
||||
/// Create a copy of ThemeConf
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,}) {
|
||||
return _then(_ThemeConf(
|
||||
backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore: cast_nullable_to_non_nullable
|
||||
as Color,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
||||
103
lib/app.g.dart
103
lib/app.g.dart
@ -6,35 +6,98 @@ part of 'app.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$routerHash() => r'4fb9802d06347972b530f17ea7d66724a6ded997';
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
/// See also [router].
|
||||
@ProviderFor(router)
|
||||
final routerProvider = AutoDisposeProvider<GoRouter>.internal(
|
||||
router,
|
||||
final routerProvider = RouterProvider._();
|
||||
|
||||
final class RouterProvider
|
||||
extends $FunctionalProvider<GoRouter, GoRouter, GoRouter>
|
||||
with $Provider<GoRouter> {
|
||||
RouterProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'routerProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$routerHash,
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
|
||||
String _$appGlobalModelHash() => r'9dccbb898714695ef8b640a5b5dfb361783a0f45';
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$routerHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<GoRouter> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
GoRouter create(Ref ref) {
|
||||
return router(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(GoRouter value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<GoRouter>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$routerHash() => r'e89f3f0277879147cdce5373cbe2554821e9cd31';
|
||||
|
||||
/// See also [AppGlobalModel].
|
||||
@ProviderFor(AppGlobalModel)
|
||||
final appGlobalModelProvider =
|
||||
AutoDisposeNotifierProvider<AppGlobalModel, AppGlobalState>.internal(
|
||||
AppGlobalModel.new,
|
||||
final appGlobalModelProvider = AppGlobalModelProvider._();
|
||||
|
||||
final class AppGlobalModelProvider
|
||||
extends $NotifierProvider<AppGlobalModel, AppGlobalState> {
|
||||
AppGlobalModelProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'appGlobalModelProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appGlobalModelHash,
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppGlobalModel = AutoDisposeNotifier<AppGlobalState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$appGlobalModelHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AppGlobalModel create() => AppGlobalModel();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(AppGlobalState value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<AppGlobalState>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$appGlobalModelHash() => r'74128d2194d00a0e3dbb000dcaf6452e0b966d9c';
|
||||
|
||||
abstract class _$AppGlobalModel extends $Notifier<AppGlobalState> {
|
||||
AppGlobalState build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<AppGlobalState, AppGlobalState>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AppGlobalState, AppGlobalState>,
|
||||
AppGlobalState,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class BinaryModuleConf {
|
||||
static const _modules = {
|
||||
"aria2c": "0",
|
||||
"unp4kc": "1",
|
||||
};
|
||||
// aria2c has been replaced by rqbit (Rust-based torrent library)
|
||||
static const _modules = <String, String>{};
|
||||
|
||||
static Future extractModule(List<String> modules, String workingDir) async {
|
||||
for (var m in _modules.entries) {
|
||||
@ -18,11 +16,8 @@ class BinaryModuleConf {
|
||||
final version = m.value;
|
||||
final dir = "$workingDir\\$name";
|
||||
final versionFile = File("$dir\\version");
|
||||
if (kReleaseMode &&
|
||||
await versionFile.exists() &&
|
||||
(await versionFile.readAsString()).trim() == version) {
|
||||
dPrint(
|
||||
"BinaryModuleConf.extractModule skip $name version == $version");
|
||||
if (kReleaseMode && await versionFile.exists() && (await versionFile.readAsString()).trim() == version) {
|
||||
dPrint("BinaryModuleConf.extractModule skip $name version == $version");
|
||||
continue;
|
||||
}
|
||||
// write model file
|
||||
|
||||
31
lib/common/conf/conf.dart
Normal file
31
lib/common/conf/conf.dart
Normal file
@ -0,0 +1,31 @@
|
||||
import 'dart:io';
|
||||
|
||||
class ConstConf {
|
||||
static const String appVersion = "3.0.0 Beta10";
|
||||
static const int appVersionCode = 79;
|
||||
static const String appVersionDate = "2025-12-27";
|
||||
static const _gameChannels = ["LIVE", "4.0_PREVIEW", "PTU", "EPTU", "TECH-PREVIEW", "HOTFIX"];
|
||||
static const isMSE = String.fromEnvironment("MSE", defaultValue: "false") == "true";
|
||||
static const win32AppId = isMSE
|
||||
? "56575xkeyC.MSE_bsn1nexg8e4qe!starcitizendoctor"
|
||||
: "{6D809377-6AF0-444B-8957-A3773F02200E}\\Starcitizen_Doctor\\starcitizen_doctor.exe";
|
||||
static const dohAddress = "https://223.6.6.6/resolve";
|
||||
static const inputMethodServerPort = 59399;
|
||||
}
|
||||
|
||||
class AppConf {
|
||||
static List<String>? _networkGameChannels;
|
||||
|
||||
static void setNetworkChannels(List<String>? channels) {
|
||||
_networkGameChannels = channels;
|
||||
}
|
||||
|
||||
static List<String> get gameChannels {
|
||||
final baseChannels = _networkGameChannels ?? ConstConf._gameChannels;
|
||||
// On Linux, add lowercase variants for case-sensitive filesystem
|
||||
if (Platform.isLinux) {
|
||||
return [...baseChannels, ...baseChannels.map((c) => c.toLowerCase())];
|
||||
}
|
||||
return baseChannels;
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
class ConstConf {
|
||||
static const String appVersion = "2.11.0 Beta";
|
||||
static const int appVersionCode = 50;
|
||||
static const String appVersionDate = "2024-5-07";
|
||||
static const gameChannels = ["LIVE", "PTU", "EPTU", "TECH-PREVIEW"];
|
||||
static const isMSE =
|
||||
String.fromEnvironment("MSE", defaultValue: "false") == "true";
|
||||
}
|
||||
@ -1,92 +1,75 @@
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/http_package.dart';
|
||||
import 'package:starcitizen_doctor/api/api.dart';
|
||||
import 'package:starcitizen_doctor/common/io/doh_client.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/http_api.dart' as rust_http;
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class URLConf {
|
||||
/// HOME API
|
||||
static String gitApiHome = "https://git.sctoolbox.sccsgo.com";
|
||||
static String rssApiHome = "https://rss.sctoolbox.sccsgo.com";
|
||||
static String gitApiHome = "https://git.scbox.xkeyc.cn";
|
||||
static String newsApiHome = "https://scbox.citizenwiki.cn";
|
||||
static const String analyticsApiHome = "https://scbox.org";
|
||||
|
||||
/// PartyRoom Server
|
||||
// static const String partyRoomServerAddress = "localhost";
|
||||
// static const int partyRoomServerPort = 50051;
|
||||
static const String partyRoomServerAddress = "ecdn.partyroom.grpc.scbox.xkeyc.cn";
|
||||
static const int partyRoomServerPort = 443;
|
||||
|
||||
static bool isUrlCheckPass = false;
|
||||
|
||||
/// URLS
|
||||
static String get giteaAttachmentsUrl => "$gitApiHome/SCToolBox/Release";
|
||||
|
||||
static String get gitlabLocalizationUrl =>
|
||||
"$gitApiHome/SCToolBox/LocalizationData";
|
||||
static String get gitlabLocalizationUrl => "$gitApiHome/SCToolBox/LocalizationData";
|
||||
|
||||
static String get gitApiRSILauncherEnhanceUrl =>
|
||||
"$gitApiHome/SCToolBox/RSILauncherEnhance";
|
||||
static String get gitApiRSILauncherEnhanceUrl => "$gitApiHome/SCToolBox/RSILauncherEnhance";
|
||||
|
||||
static String get apiRepoPath => "$gitApiHome/SCToolBox/api/raw/branch/main";
|
||||
|
||||
static String get gitlabApiPath => "$gitApiHome/api/v1/";
|
||||
|
||||
static String get webTranslateHomeUrl =>
|
||||
"$gitApiHome/SCToolBox/ScWeb_Chinese_Translate/raw/branch/main/json/locales";
|
||||
static String get webTranslateHomeUrl => "$gitApiHome/SCToolBox/ScWeb_Chinese_Translate/raw/branch/main/json/locales";
|
||||
|
||||
static String get rssVideoUrl =>
|
||||
"$rssApiHome/bilibili/user/channel/27976358/290653";
|
||||
|
||||
static String get rssTextUrl1 => "$rssApiHome/bilibili/user/article/40102960";
|
||||
|
||||
static String get rssTextUrl2 =>
|
||||
"$rssApiHome/baidu/tieba/user/%E7%81%AC%E7%81%ACG%E7%81%AC%E7%81%AC&";
|
||||
|
||||
static const feedbackUrl = "https://txc.qq.com/products/614843";
|
||||
static const feedbackUrl = "https://support.citizenwiki.cn/all";
|
||||
static const feedbackFAQUrl = "https://support.citizenwiki.cn/t/sc-toolbox";
|
||||
static String nav42KitUrl =
|
||||
"https://payload.citizenwiki.cn/api/community-navs?sort=is_sponsored&depth=2&page=1&limit=1000";
|
||||
|
||||
static String get devReleaseUrl => "$gitApiHome/SCToolBox/Release/releases";
|
||||
|
||||
/// RSI Avatar Base URL
|
||||
static const String rsiAvatarBaseUrl = "https://robertsspaceindustries.com";
|
||||
|
||||
static Future<bool> checkHost() async {
|
||||
// 使用 DNS 获取可用列表
|
||||
final gitApiList =
|
||||
_genFinalList(await RSHttp.dnsLookupTxt("git.dns.scbox.org"));
|
||||
final gitApiList = _genFinalList(await dnsLookupTxt("git.dns.scbox.org"));
|
||||
dPrint("DNS gitApiList ==== $gitApiList");
|
||||
final fasterGit = await getFasterUrl(gitApiList);
|
||||
final fasterGit = await rust_http.getFasterUrl(
|
||||
urls: gitApiList,
|
||||
pathSuffix: "/SCToolBox/Api/raw/branch/main/sc_doctor/version.json",
|
||||
);
|
||||
dPrint("gitApiList.Faster ==== $fasterGit");
|
||||
if (fasterGit != null) {
|
||||
gitApiHome = fasterGit;
|
||||
}
|
||||
final rssApiList =
|
||||
_genFinalList(await RSHttp.dnsLookupTxt("rss.dns.scbox.org"));
|
||||
final fasterRss = await getFasterUrl(rssApiList);
|
||||
dPrint("DNS rssApiList ==== $rssApiList");
|
||||
dPrint("rssApiList.Faster ==== $fasterRss");
|
||||
if (fasterRss != null) {
|
||||
rssApiHome = fasterRss;
|
||||
final newsApiList = _genFinalList(await dnsLookupTxt("news.dns.scbox.org"));
|
||||
final fasterNews = await rust_http.getFasterUrl(urls: newsApiList, pathSuffix: "/api/latest");
|
||||
dPrint("DNS newsApiList ==== $newsApiList");
|
||||
dPrint("newsApiList.Faster ==== $fasterNews");
|
||||
if (fasterNews != null) {
|
||||
newsApiHome = fasterNews;
|
||||
}
|
||||
isUrlCheckPass = fasterGit != null && fasterRss != null;
|
||||
isUrlCheckPass = fasterGit != null && fasterNews != null;
|
||||
return isUrlCheckPass;
|
||||
}
|
||||
|
||||
static Future<String?> getFasterUrl(List<String> urls) async {
|
||||
String firstUrl = "";
|
||||
int callLen = 0;
|
||||
|
||||
void onCall(RustHttpResponse? response, String url) {
|
||||
callLen++;
|
||||
if (response != null && response.statusCode == 200 && firstUrl.isEmpty) {
|
||||
firstUrl = url;
|
||||
}
|
||||
}
|
||||
|
||||
for (var value in urls) {
|
||||
RSHttp.head(value).then((resp) => onCall(resp, value), onError: (err) {
|
||||
callLen++;
|
||||
dPrint("RSHttp.head error $err");
|
||||
});
|
||||
}
|
||||
|
||||
while (true) {
|
||||
await Future.delayed(const Duration(milliseconds: 16));
|
||||
if (firstUrl.isNotEmpty) {
|
||||
return firstUrl;
|
||||
}
|
||||
if (callLen == urls.length && firstUrl.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
static Future<List<String>> dnsLookupTxt(String host) async {
|
||||
if (await Api.isUseInternalDNS()) {
|
||||
dPrint("[URLConf] use internal DNS LookupTxt $host");
|
||||
return rust_http.dnsLookupTxt(host: host);
|
||||
}
|
||||
dPrint("[URLConf] use DOH LookupTxt $host");
|
||||
return (await DohClient.resolveTXT(host)) ?? [];
|
||||
}
|
||||
|
||||
static List<String> _genFinalList(List<String> sList) {
|
||||
|
||||
508
lib/common/helper/game_log_analyzer.dart
Normal file
508
lib/common/helper/game_log_analyzer.dart
Normal file
@ -0,0 +1,508 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
|
||||
/// 日志分析结果数据类
|
||||
class LogAnalyzeLineData {
|
||||
final String type;
|
||||
final String title;
|
||||
final String? data;
|
||||
final String? dateTime;
|
||||
final String? tag; // 统计标签,用于定位日志(如 "game_start"),不依赖本地化
|
||||
|
||||
// 格式化后的字段
|
||||
final String? victimId; // 受害者ID (actor_death)
|
||||
final String? location; // 位置信息 (request_location_inventory)
|
||||
final String? area; // 区域信息
|
||||
final String? playerName; // 玩家名称 (player_login)
|
||||
|
||||
const LogAnalyzeLineData({
|
||||
required this.type,
|
||||
required this.title,
|
||||
this.data,
|
||||
this.dateTime,
|
||||
this.tag,
|
||||
this.victimId,
|
||||
this.location,
|
||||
this.area,
|
||||
this.playerName,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LogAnalyzeLineData(type: $type, title: $title, data: $data, dateTime: $dateTime)';
|
||||
}
|
||||
}
|
||||
|
||||
/// 日志分析统计数据
|
||||
class LogAnalyzeStatistics {
|
||||
final String playerName;
|
||||
final int killCount;
|
||||
final int deathCount;
|
||||
final int selfKillCount;
|
||||
final int vehicleDestructionCount;
|
||||
final int vehicleDestructionCountHard;
|
||||
final DateTime? gameStartTime;
|
||||
final int gameCrashLineNumber;
|
||||
final String? latestLocation; // 最新位置信息(全量查找)
|
||||
|
||||
const LogAnalyzeStatistics({
|
||||
required this.playerName,
|
||||
required this.killCount,
|
||||
required this.deathCount,
|
||||
required this.selfKillCount,
|
||||
required this.vehicleDestructionCount,
|
||||
required this.vehicleDestructionCountHard,
|
||||
this.gameStartTime,
|
||||
required this.gameCrashLineNumber,
|
||||
this.latestLocation,
|
||||
});
|
||||
}
|
||||
|
||||
/// 游戏日志分析器
|
||||
class GameLogAnalyzer {
|
||||
static const String unknownValue = "<Unknown>";
|
||||
|
||||
// 正则表达式定义
|
||||
static final _baseRegExp = RegExp(r'\[Notice\]\s+<([^>]+)>');
|
||||
static final _gameLoadingRegExp = RegExp(
|
||||
r'<[^>]+>\s+Loading screen for\s+(\w+)\s+:\s+SC_Frontend closed after\s+(\d+\.\d+)\s+seconds',
|
||||
);
|
||||
static final _logDateTimeRegExp = RegExp(r'<(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)>');
|
||||
static final DateFormat _dateTimeFormatter = DateFormat('yyyy-MM-dd HH:mm:ss:SSS');
|
||||
|
||||
// 致命碰撞解析
|
||||
static final _fatalCollisionPatterns = {
|
||||
'vehicle': RegExp(r'Fatal Collision occured for vehicle\s+(\S+)'),
|
||||
'zone': RegExp(r'Zone:\s*([^,\]]+)'),
|
||||
'player_pilot': RegExp(r'PlayerPilot:\s*(\d)'),
|
||||
'hit_entity': RegExp(r'hitting entity:\s*(\w+)'),
|
||||
'distance': RegExp(r'Distance:\s*([\d.]+)'),
|
||||
};
|
||||
|
||||
// 载具损毁解析
|
||||
static final _vehicleDestructionPattern = RegExp(
|
||||
r"Vehicle\s+'([^']+)'.*?" // 载具型号
|
||||
r"in zone\s+'([^']+)'.*?" // Zone
|
||||
r"destroy level \d+ to (\d+).*?" // 损毁等级
|
||||
r"caused by\s+'([^']+)'", // 责任方
|
||||
);
|
||||
|
||||
// 角色死亡解析
|
||||
static final _actorDeathPattern = RegExp(
|
||||
r"Actor '([^']+)'.*?" // 受害者ID
|
||||
r"ejected from zone '([^']+)'.*?" // 原载具/区域
|
||||
r"to zone '([^']+)'", // 目标区域
|
||||
);
|
||||
|
||||
// 角色名称解析
|
||||
static final _characterNamePattern = RegExp(r"name\s+([^-]+)");
|
||||
|
||||
// 本地库存请求解析
|
||||
static final _requestLocationInventoryPattern = RegExp(r"Player\[([^\]]+)\].*?Location\[([^\]]+)\]");
|
||||
|
||||
// 载具控制解析
|
||||
static final vehicleControlPattern = RegExp(r"granted control token for '([^']+)'\s+\[(\d+)\]");
|
||||
|
||||
/// 公开的日期时间解析方法,供其他模块使用
|
||||
static DateTime? getLogLineDateTime(String line) => _getLogLineDateTime(line);
|
||||
|
||||
/// 公开的日期时间字符串解析方法
|
||||
static String? getLogLineDateTimeString(String line) => _getLogLineDateTimeString(line);
|
||||
|
||||
/// 从载具名称中移除末尾的ID
|
||||
/// 示例: ANVL_Hornet_F7A_Mk2_3467069517923 -> ANVL_Hornet_F7A_Mk2
|
||||
static String removeVehicleId(String vehicleName) {
|
||||
final regex = RegExp(r'_\d+$');
|
||||
return vehicleName.replaceAll(regex, '');
|
||||
}
|
||||
|
||||
/// 分析整个日志文件
|
||||
///
|
||||
/// [logFile] 日志文件
|
||||
/// [startTime] 开始时间,如果提供则只统计此时间之后的数据
|
||||
/// 返回日志分析结果列表和统计数据
|
||||
static Future<(List<LogAnalyzeLineData>, LogAnalyzeStatistics)> analyzeLogFile(
|
||||
File logFile, {
|
||||
DateTime? startTime,
|
||||
}) async {
|
||||
if (!(await logFile.exists())) {
|
||||
return (
|
||||
[LogAnalyzeLineData(type: "error", title: S.current.log_analyzer_no_log_file)],
|
||||
LogAnalyzeStatistics(
|
||||
playerName: "",
|
||||
killCount: 0,
|
||||
deathCount: 0,
|
||||
selfKillCount: 0,
|
||||
vehicleDestructionCount: 0,
|
||||
vehicleDestructionCountHard: 0,
|
||||
gameCrashLineNumber: -1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final logLines = utf8.decode((await logFile.readAsBytes()), allowMalformed: true).split("\n");
|
||||
return _analyzeLogLines(logLines, startTime: startTime);
|
||||
}
|
||||
|
||||
/// 分析日志行列表
|
||||
///
|
||||
/// [logLines] 日志行列表
|
||||
/// [startTime] 开始时间,如果提供则只影响计数统计,不影响 gameStartTime 和位置的全量查找
|
||||
/// 返回日志分析结果列表和统计数据
|
||||
static (List<LogAnalyzeLineData>, LogAnalyzeStatistics) _analyzeLogLines(
|
||||
List<String> logLines, {
|
||||
DateTime? startTime,
|
||||
}) {
|
||||
final results = <LogAnalyzeLineData>[];
|
||||
String playerName = "";
|
||||
int killCount = 0;
|
||||
int deathCount = 0;
|
||||
int selfKillCount = 0;
|
||||
int vehicleDestructionCount = 0;
|
||||
int vehicleDestructionCountHard = 0;
|
||||
DateTime? gameStartTime; // 全量查找,不受 startTime 影响
|
||||
String? latestLocation; // 全量查找最新位置
|
||||
int gameCrashLineNumber = -1;
|
||||
bool shouldCount = startTime == null; // 只影响计数
|
||||
|
||||
for (var i = 0; i < logLines.length; i++) {
|
||||
final line = logLines[i];
|
||||
if (line.isEmpty) continue;
|
||||
|
||||
// 如果设置了 startTime,检查当前行时间
|
||||
if (startTime != null && !shouldCount) {
|
||||
final lineTime = _getLogLineDateTime(line);
|
||||
if (lineTime != null && lineTime.isAfter(startTime)) {
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理游戏开始(全量查找第一次出现)
|
||||
if (gameStartTime == null) {
|
||||
gameStartTime = _getLogLineDateTime(line);
|
||||
if (gameStartTime != null) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "info",
|
||||
title: S.current.log_analyzer_game_start,
|
||||
tag: "game_start", // 使用 tag 标识,不依赖本地化
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 游戏加载时间
|
||||
final gameLoading = _parseGameLoading(line);
|
||||
if (gameLoading != null) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "info",
|
||||
title: S.current.log_analyzer_game_loading,
|
||||
data: S.current.log_analyzer_mode_loading_time(gameLoading.$1, gameLoading.$2),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 基础事件解析
|
||||
final baseEvent = _parseBaseEvent(line);
|
||||
if (baseEvent != null) {
|
||||
LogAnalyzeLineData? data;
|
||||
switch (baseEvent) {
|
||||
case "AccountLoginCharacterStatus_Character":
|
||||
data = _parseCharacterName(line);
|
||||
if (data != null && data.playerName != null) {
|
||||
playerName = data.playerName!; // 全量更新玩家名称
|
||||
}
|
||||
break;
|
||||
case "FatalCollision":
|
||||
data = _parseFatalCollision(line);
|
||||
break;
|
||||
case "Vehicle Destruction":
|
||||
data = _parseVehicleDestruction(line, playerName, shouldCount, (isHard) {
|
||||
if (isHard) {
|
||||
vehicleDestructionCountHard++;
|
||||
} else {
|
||||
vehicleDestructionCount++;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "[ActorState] Dead":
|
||||
data = _parseActorDeath(line, playerName, shouldCount, (isKill, isDeath, isSelfKill) {
|
||||
if (isSelfKill) {
|
||||
selfKillCount++;
|
||||
} else {
|
||||
if (isKill) killCount++;
|
||||
if (isDeath) deathCount++;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "RequestLocationInventory":
|
||||
data = _parseRequestLocationInventory(line);
|
||||
if (data != null && data.location != null) {
|
||||
latestLocation = data.location; // 全量更新最新位置
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (data != null) {
|
||||
results.add(data);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 游戏关闭
|
||||
if (line.contains("[CIG] CCIGBroker::FastShutdown")) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "info",
|
||||
title: S.current.log_analyzer_game_close,
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 游戏崩溃
|
||||
if (line.contains("Cloud Imperium Games public crash handler")) {
|
||||
gameCrashLineNumber = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理崩溃信息
|
||||
if (gameCrashLineNumber > 0) {
|
||||
final lastLineDateTime = gameStartTime != null
|
||||
? _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20")))
|
||||
: null;
|
||||
final crashInfo = logLines.sublist(gameCrashLineNumber);
|
||||
final info = SCLoggerHelper.getGameRunningLogInfo(crashInfo);
|
||||
crashInfo.add(S.current.log_analyzer_one_click_diagnosis_header);
|
||||
if (info != null) {
|
||||
crashInfo.add(info.key);
|
||||
if (info.value.isNotEmpty) {
|
||||
crashInfo.add(S.current.log_analyzer_details_info(info.value));
|
||||
}
|
||||
} else {
|
||||
crashInfo.add(S.current.log_analyzer_no_crash_detected);
|
||||
}
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "game_crash",
|
||||
title: S.current.log_analyzer_game_crash,
|
||||
data: crashInfo.join("\n"),
|
||||
dateTime: lastLineDateTime != null ? _dateTimeFormatter.format(lastLineDateTime) : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 添加统计信息
|
||||
if (killCount > 0 || deathCount > 0) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "statistics",
|
||||
title: S.current.log_analyzer_kill_summary,
|
||||
data: S.current.log_analyzer_kill_death_suicide_count(
|
||||
killCount,
|
||||
deathCount,
|
||||
selfKillCount,
|
||||
vehicleDestructionCount,
|
||||
vehicleDestructionCountHard,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 统计游戏时长
|
||||
if (gameStartTime != null) {
|
||||
final lastLineDateTime = _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20"), orElse: () => ""));
|
||||
if (lastLineDateTime != null) {
|
||||
final duration = lastLineDateTime.difference(gameStartTime);
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "statistics",
|
||||
title: S.current.log_analyzer_play_time,
|
||||
data: S.current.log_analyzer_play_time_format(
|
||||
duration.inHours,
|
||||
duration.inMinutes.remainder(60),
|
||||
duration.inSeconds.remainder(60),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final statistics = LogAnalyzeStatistics(
|
||||
playerName: playerName,
|
||||
killCount: killCount,
|
||||
deathCount: deathCount,
|
||||
selfKillCount: selfKillCount,
|
||||
vehicleDestructionCount: vehicleDestructionCount,
|
||||
vehicleDestructionCountHard: vehicleDestructionCountHard,
|
||||
gameStartTime: gameStartTime,
|
||||
gameCrashLineNumber: gameCrashLineNumber,
|
||||
latestLocation: latestLocation,
|
||||
);
|
||||
|
||||
return (results, statistics);
|
||||
}
|
||||
|
||||
// ==================== 解析辅助方法 ====================
|
||||
|
||||
static String? _parseBaseEvent(String line) {
|
||||
final match = _baseRegExp.firstMatch(line);
|
||||
return match?.group(1);
|
||||
}
|
||||
|
||||
static (String, String)? _parseGameLoading(String line) {
|
||||
final match = _gameLoadingRegExp.firstMatch(line);
|
||||
if (match != null) {
|
||||
return (match.group(1) ?? "-", match.group(2) ?? "-");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static DateTime? _getLogLineDateTime(String line) {
|
||||
final match = _logDateTimeRegExp.firstMatch(line);
|
||||
if (match != null) {
|
||||
final dateTimeString = match.group(1);
|
||||
if (dateTimeString != null) {
|
||||
return DateTime.parse(dateTimeString).toLocal();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? _getLogLineDateTimeString(String line) {
|
||||
final dateTime = _getLogLineDateTime(line);
|
||||
if (dateTime != null) {
|
||||
return _dateTimeFormatter.format(dateTime);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? _safeExtract(RegExp pattern, String line) => pattern.firstMatch(line)?.group(1)?.trim();
|
||||
|
||||
static LogAnalyzeLineData? _parseFatalCollision(String line) {
|
||||
final vehicle = _safeExtract(_fatalCollisionPatterns['vehicle']!, line) ?? unknownValue;
|
||||
final zone = _safeExtract(_fatalCollisionPatterns['zone']!, line) ?? unknownValue;
|
||||
final playerPilot = (_safeExtract(_fatalCollisionPatterns['player_pilot']!, line) ?? '0') == '1';
|
||||
final hitEntity = _safeExtract(_fatalCollisionPatterns['hit_entity']!, line) ?? unknownValue;
|
||||
final distance = double.tryParse(_safeExtract(_fatalCollisionPatterns['distance']!, line) ?? '') ?? 0.0;
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "fatal_collision",
|
||||
title: S.current.log_analyzer_filter_fatal_collision,
|
||||
data: S.current.log_analyzer_collision_details(
|
||||
zone,
|
||||
playerPilot ? '✅' : '❌',
|
||||
hitEntity,
|
||||
vehicle,
|
||||
distance.toStringAsFixed(2),
|
||||
),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
);
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseVehicleDestruction(
|
||||
String line,
|
||||
String playerName,
|
||||
bool shouldCount,
|
||||
void Function(bool isHard) onDestruction,
|
||||
) {
|
||||
final match = _vehicleDestructionPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final vehicleModel = match.group(1) ?? unknownValue;
|
||||
final zone = match.group(2) ?? unknownValue;
|
||||
final destructionLevel = int.tryParse(match.group(3) ?? '') ?? 0;
|
||||
final causedBy = match.group(4) ?? unknownValue;
|
||||
|
||||
final destructionLevelMap = {1: S.current.log_analyzer_soft_death, 2: S.current.log_analyzer_disintegration};
|
||||
|
||||
if (shouldCount && causedBy.trim() == playerName) {
|
||||
onDestruction(destructionLevel == 2);
|
||||
}
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "vehicle_destruction",
|
||||
title: S.current.log_analyzer_filter_vehicle_damaged,
|
||||
data: S.current.log_analyzer_vehicle_damage_details(
|
||||
vehicleModel,
|
||||
zone,
|
||||
destructionLevel.toString(),
|
||||
destructionLevelMap[destructionLevel] ?? unknownValue,
|
||||
causedBy,
|
||||
),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseActorDeath(
|
||||
String line,
|
||||
String playerName,
|
||||
bool shouldCount,
|
||||
void Function(bool isKill, bool isDeath, bool isSelfKill) onDeath,
|
||||
) {
|
||||
final match = _actorDeathPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final victimId = match.group(1) ?? unknownValue;
|
||||
final fromZone = match.group(2) ?? unknownValue;
|
||||
final toZone = match.group(3) ?? unknownValue;
|
||||
|
||||
if (shouldCount) {
|
||||
final isDeath = victimId.trim() == playerName;
|
||||
if (isDeath) {
|
||||
onDeath(false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "actor_death",
|
||||
title: S.current.log_analyzer_filter_character_death,
|
||||
data: S.current.log_analyzer_death_details(victimId, fromZone, toZone),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
location: fromZone,
|
||||
area: toZone,
|
||||
victimId: victimId,
|
||||
playerName: playerName,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseCharacterName(String line) {
|
||||
final match = _characterNamePattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final characterName = match.group(1)?.trim() ?? unknownValue;
|
||||
return LogAnalyzeLineData(
|
||||
type: "player_login",
|
||||
title: S.current.log_analyzer_player_login(characterName),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
playerName: characterName, // 格式化字段
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseRequestLocationInventory(String line) {
|
||||
final match = _requestLocationInventoryPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final playerId = match.group(1) ?? unknownValue;
|
||||
final location = match.group(2) ?? unknownValue;
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "request_location_inventory",
|
||||
title: S.current.log_analyzer_view_local_inventory,
|
||||
data: S.current.log_analyzer_player_location(playerId, location),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
location: location, // 格式化字段
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,22 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class SCLoggerHelper {
|
||||
static Future<String?> getLogFilePath() async {
|
||||
if (!Platform.isWindows) {
|
||||
final wineUserPath = await getWineUserPath();
|
||||
if (wineUserPath == null) return null;
|
||||
// /home/xkeyc/Games/star-citizen/drive_c/users/xkeyc/AppData/Roaming/rsilauncher/
|
||||
final rsiLauncherPath = "$wineUserPath/AppData/Roaming/rsilauncher";
|
||||
dPrint("rsiLauncherPath Wine:$rsiLauncherPath");
|
||||
final jsonLogPath = "$rsiLauncherPath/logs/log.log";
|
||||
return jsonLogPath;
|
||||
}
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final appDataPath = envVars["appdata"];
|
||||
if (appDataPath == null) {
|
||||
@ -19,6 +29,14 @@ class SCLoggerHelper {
|
||||
}
|
||||
|
||||
static Future<String?> getShaderCachePath() async {
|
||||
if (!Platform.isWindows) {
|
||||
final wineUserPath = await getWineUserPath();
|
||||
if (wineUserPath == null) return null;
|
||||
// /home/xkeyc/Games/star-citizen/drive_c/users/xkeyc/AppData/Local/star citizen/
|
||||
final scCachePath = "$wineUserPath/AppData/Local/star citizen";
|
||||
dPrint("getShaderCachePath Wine === $scCachePath");
|
||||
return scCachePath;
|
||||
}
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final appDataPath = envVars["LOCALAPPDATA"];
|
||||
if (appDataPath == null) {
|
||||
@ -29,34 +47,55 @@ class SCLoggerHelper {
|
||||
return scCachePath;
|
||||
}
|
||||
|
||||
static Future<List?> getLauncherLogList() async {
|
||||
final jsonLogPath = await getLogFilePath();
|
||||
if (jsonLogPath == null) return null;
|
||||
var jsonString = utf8.decode(await File(jsonLogPath).readAsBytes());
|
||||
if (jsonString.endsWith("\n")) {
|
||||
jsonString = jsonString.substring(0, jsonString.length - 3);
|
||||
}
|
||||
if (jsonString.endsWith(" ")) {
|
||||
jsonString = jsonString.substring(0, jsonString.length - 3);
|
||||
}
|
||||
if (jsonString.endsWith(",")) {
|
||||
jsonString = jsonString.substring(0, jsonString.length - 3);
|
||||
}
|
||||
return json.decode("[$jsonString]");
|
||||
static Future<String?> getWineUserPath() async {
|
||||
// get game path in hiveBox
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final path = confBox.get("custom_game_path");
|
||||
if (path?.isEmpty ?? true) return null;
|
||||
// path eg: /home/xkeyc/Games/star-citizen/drive_c/Program Files/Roberts Space Industries/StarCitizen/LIVE/
|
||||
// resolve wine c_drive path
|
||||
final wineCDrivePath = path.toString().split('/drive_c/').first;
|
||||
// scan wine user path == current_unix_user
|
||||
final wineUserPath = "$wineCDrivePath/drive_c/users/${Platform.environment['USER']}";
|
||||
// check exists
|
||||
final wineUserDir = Directory(wineUserPath);
|
||||
if (!await wineUserDir.exists()) return null;
|
||||
dPrint("getWineUserPath === $wineUserPath");
|
||||
return wineUserPath;
|
||||
}
|
||||
|
||||
static Future<List<String>> getGameInstallPath(List listData,
|
||||
{bool checkExists = true,
|
||||
List<String> withVersion = const ["LIVE"]}) async {
|
||||
static Future<List?> getLauncherLogList() async {
|
||||
if (!Platform.isWindows) return [];
|
||||
try {
|
||||
final jsonLogPath = await getLogFilePath();
|
||||
if (jsonLogPath == null) throw "no file path";
|
||||
var jsonString = utf8.decode(await File(jsonLogPath).readAsBytes());
|
||||
return jsonString.split("\n");
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<String>> getGameInstallPath(
|
||||
List listData, {
|
||||
bool checkExists = true,
|
||||
List<String> withVersion = const ["LIVE"],
|
||||
}) async {
|
||||
List<String> scInstallPaths = [];
|
||||
|
||||
checkAndAddPath(String path, bool checkExists) async {
|
||||
if (path.isNotEmpty && !scInstallPaths.contains(path)) {
|
||||
// Handle JSON-escaped backslashes (\\\\) -> single backslash (\\)
|
||||
path = path.replaceAll(r'\\', r'\');
|
||||
// Normalize path separators to current platform format
|
||||
path = path.platformPath;
|
||||
|
||||
// Case-insensitive check for existing paths
|
||||
if (path.isNotEmpty && !scInstallPaths.any((p) => p.toLowerCase() == path.toLowerCase())) {
|
||||
if (!checkExists) {
|
||||
dPrint("find installPath == $path");
|
||||
scInstallPaths.add(path);
|
||||
} else if (await File("$path/Bin64/StarCitizen.exe").exists() &&
|
||||
await File("$path/Data.p4k").exists()) {
|
||||
} else if (await File("$path/Bin64/StarCitizen.exe").exists() && await File("$path/Data.p4k").exists()) {
|
||||
dPrint("find installPath == $path");
|
||||
scInstallPaths.add(path);
|
||||
}
|
||||
@ -67,29 +106,50 @@ class SCLoggerHelper {
|
||||
final path = confBox.get("custom_game_path");
|
||||
if (path != null && path != "") {
|
||||
for (var v in withVersion) {
|
||||
await checkAndAddPath("$path\\$v", checkExists);
|
||||
await checkAndAddPath("$path\\$v".platformPath, checkExists);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (var v in withVersion) {
|
||||
// Platform-specific regex patterns for game install path detection
|
||||
// Uses restrictive character class to avoid matching across JSON delimiters
|
||||
String pattern;
|
||||
if (Platform.isWindows) {
|
||||
// Windows: Match paths like C:\...\StarCitizen\LIVE
|
||||
// Path segments can only contain: letters, numbers, space, dot, underscore, hyphen, parentheses
|
||||
// Handles both single backslash, forward slash, and JSON-escaped double backslash
|
||||
pattern =
|
||||
r'([a-zA-Z]:(?:[/\\]|\\\\)(?:[a-zA-Z0-9 ._()-]+(?:[/\\]|\\\\))*StarCitizen(?:[/\\]|\\\\)' + v + r')';
|
||||
} else {
|
||||
// Unix (Wine): Match paths like /home/user/.../StarCitizen/LIVE
|
||||
pattern = r'(/(?:[a-zA-Z0-9 ._()-]+/)*StarCitizen/' + v + r')';
|
||||
}
|
||||
RegExp regExp = RegExp(pattern, caseSensitive: false);
|
||||
for (var i = listData.length - 1; i > 0; i--) {
|
||||
final m = listData[i];
|
||||
final info = m["[browser][info] "];
|
||||
if (info is String) {
|
||||
String installPath = "";
|
||||
if (info.contains("Installing Star Citizen $v")) {
|
||||
installPath = "${info.split(" at ")[1]}\\$v";
|
||||
final line = listData[i];
|
||||
final matches = regExp.allMatches(line);
|
||||
for (var match in matches) {
|
||||
await checkAndAddPath(match.group(0)!, checkExists);
|
||||
}
|
||||
if (info.contains("Verifying Star Citizen $v")) {
|
||||
installPath = "${info.split(" at ")[1]}\\$v";
|
||||
}
|
||||
if (info.contains("Launching Star Citizen $v from")) {
|
||||
installPath = info
|
||||
.replaceAll("Launching Star Citizen $v from (", "")
|
||||
.replaceAll(")", "");
|
||||
}
|
||||
await checkAndAddPath(installPath, checkExists);
|
||||
|
||||
if (scInstallPaths.isNotEmpty) {
|
||||
// 动态检测更多位置
|
||||
for (var fileName in List.from(scInstallPaths)) {
|
||||
for (var v in withVersion) {
|
||||
final suffix = '\\$v'.platformPath.toLowerCase();
|
||||
if (fileName.toString().toLowerCase().endsWith(suffix)) {
|
||||
for (var nv in withVersion) {
|
||||
final basePath = fileName.toString().replaceAll(
|
||||
RegExp('${RegExp.escape(suffix)}\$', caseSensitive: false),
|
||||
'',
|
||||
);
|
||||
final nextName = "$basePath\\$nv".platformPath;
|
||||
await checkAndAddPath(nextName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,28 +158,14 @@ class SCLoggerHelper {
|
||||
if (scInstallPaths.isEmpty) rethrow;
|
||||
}
|
||||
|
||||
if (scInstallPaths.isNotEmpty) {
|
||||
// 动态检测更多位置
|
||||
for (var v in withVersion) {
|
||||
for (var fileName in List.from(scInstallPaths)) {
|
||||
if (fileName.toString().endsWith(v)) {
|
||||
for (var nv in withVersion) {
|
||||
final nextName =
|
||||
"${fileName.toString().replaceAll("\\$v", "")}\\$nv";
|
||||
await checkAndAddPath(nextName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scInstallPaths;
|
||||
}
|
||||
|
||||
static String getGameChannelID(String installPath) {
|
||||
for (var value in ConstConf.gameChannels) {
|
||||
if (installPath.endsWith("\\$value")) {
|
||||
return value;
|
||||
final pathLower = installPath.platformPath.toLowerCase();
|
||||
for (var value in AppConf.gameChannels) {
|
||||
if (pathLower.endsWith('\\${value.toLowerCase()}'.platformPath)) {
|
||||
return value.toUpperCase();
|
||||
}
|
||||
}
|
||||
return "UNKNOWN";
|
||||
@ -130,8 +176,7 @@ class SCLoggerHelper {
|
||||
if (!await logFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
return await logFile.readAsLines(
|
||||
encoding: const Utf8Codec(allowMalformed: true));
|
||||
return await logFile.readAsLines(encoding: const Utf8Codec(allowMalformed: true));
|
||||
}
|
||||
|
||||
static MapEntry<String, String>? getGameRunningLogInfo(List<String> logs) {
|
||||
@ -147,42 +192,47 @@ class SCLoggerHelper {
|
||||
|
||||
static MapEntry<String, String>? _checkRunningLine(String line) {
|
||||
if (line.contains("STATUS_CRYENGINE_OUT_OF_SYSMEM")) {
|
||||
return MapEntry(S.current.doctor_game_error_low_memory,
|
||||
S.current.doctor_game_error_low_memory_info);
|
||||
return MapEntry(S.current.doctor_game_error_low_memory, S.current.doctor_game_error_low_memory_info);
|
||||
}
|
||||
if (line.contains("EXCEPTION_ACCESS_VIOLATION")) {
|
||||
return MapEntry(S.current.doctor_game_error_generic_info,
|
||||
"https://docs.qq.com/doc/DUURxUVhzTmZoY09Z");
|
||||
return MapEntry(S.current.doctor_game_error_generic_info, "https://docs.qq.com/doc/DUURxUVhzTmZoY09Z");
|
||||
}
|
||||
if (line.contains("DXGI_ERROR_DEVICE_REMOVED")) {
|
||||
return MapEntry(S.current.doctor_game_error_gpu_crash,
|
||||
"https://www.bilibili.com/read/cv19335199");
|
||||
return MapEntry(S.current.doctor_game_error_gpu_crash, "https://www.bilibili.com/read/cv19335199");
|
||||
}
|
||||
if (line.contains("Wakeup socket sendto error")) {
|
||||
return MapEntry(S.current.doctor_game_error_socket_error,
|
||||
S.current.doctor_game_error_socket_error_info);
|
||||
return MapEntry(S.current.doctor_game_error_socket_error, S.current.doctor_game_error_socket_error_info);
|
||||
}
|
||||
|
||||
if (line.contains("The requested operation requires elevated")) {
|
||||
return MapEntry(S.current.doctor_game_error_permissions_error,
|
||||
S.current.doctor_game_error_permissions_error_info);
|
||||
return MapEntry(
|
||||
S.current.doctor_game_error_permissions_error,
|
||||
S.current.doctor_game_error_permissions_error_info,
|
||||
);
|
||||
}
|
||||
if (line.contains(
|
||||
"The process cannot access the file because is is being used by another process")) {
|
||||
return MapEntry(S.current.doctor_game_error_game_process_error,
|
||||
S.current.doctor_game_error_game_process_error_info);
|
||||
if (line.contains("The process cannot access the file because is is being used by another process")) {
|
||||
return MapEntry(
|
||||
S.current.doctor_game_error_game_process_error,
|
||||
S.current.doctor_game_error_game_process_error_info,
|
||||
);
|
||||
}
|
||||
if (line.contains("0xc0000043")) {
|
||||
return MapEntry(S.current.doctor_game_error_game_damaged_file,
|
||||
S.current.doctor_game_error_game_damaged_file_info);
|
||||
return MapEntry(
|
||||
S.current.doctor_game_error_game_damaged_file,
|
||||
S.current.doctor_game_error_game_damaged_file_info,
|
||||
);
|
||||
}
|
||||
if (line.contains("option to verify the content of the Data.p4k file")) {
|
||||
return MapEntry(S.current.doctor_game_error_game_damaged_p4k_file,
|
||||
S.current.doctor_game_error_game_damaged_p4k_file_info);
|
||||
return MapEntry(
|
||||
S.current.doctor_game_error_game_damaged_p4k_file,
|
||||
S.current.doctor_game_error_game_damaged_p4k_file_info,
|
||||
);
|
||||
}
|
||||
if (line.contains("OUTOFMEMORY Direct3D could not allocate")) {
|
||||
return MapEntry(S.current.doctor_game_error_low_gpu_memory,
|
||||
S.current.doctor_game_error_low_gpu_memory_info);
|
||||
return MapEntry(S.current.doctor_game_error_low_gpu_memory, S.current.doctor_game_error_low_gpu_memory_info);
|
||||
}
|
||||
if (line.contains("try disabling with r_vulkanDisableLayers = 1 in your user.cfg")) {
|
||||
return MapEntry(S.current.doctor_game_error_gpu_vulkan_crash, S.current.doctor_game_error_gpu_vulkan_crash_info);
|
||||
}
|
||||
|
||||
/// Unknown
|
||||
|
||||
@ -1,82 +1,36 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32;
|
||||
|
||||
class SystemHelper {
|
||||
static String powershellPath = "powershell.exe";
|
||||
|
||||
static initPowershellPath() async {
|
||||
try {
|
||||
var result = await Process.run(powershellPath, ["echo", "ping"]);
|
||||
if (!result.stdout.toString().startsWith("ping") &&
|
||||
powershellPath == "powershell.exe") {
|
||||
throw "powershell check failed";
|
||||
}
|
||||
} catch (e) {
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final systemRoot = envVars["SYSTEMROOT"];
|
||||
if (systemRoot != null) {
|
||||
final autoSearchPath =
|
||||
"$systemRoot\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
|
||||
dPrint("auto search powershell path === $autoSearchPath");
|
||||
powershellPath = autoSearchPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> checkNvmePatchStatus() async {
|
||||
try {
|
||||
var result = await Process.run(SystemHelper.powershellPath, [
|
||||
"Get-ItemProperty",
|
||||
"-Path",
|
||||
"\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"",
|
||||
"-Name",
|
||||
"\"ForcedPhysicalSectorSizeInBytes\""
|
||||
]);
|
||||
dPrint("checkNvmePatchStatus result ==== ${result.stdout}");
|
||||
if (result.stderr == "" &&
|
||||
result.stdout.toString().contains("{* 4095}")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return await win32.checkNvmePatchStatus();
|
||||
} catch (e) {
|
||||
dPrint("checkNvmePatchStatus error: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> addNvmePatch() async {
|
||||
var result = await Process.run(powershellPath, [
|
||||
'New-ItemProperty',
|
||||
"-Path",
|
||||
"\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"",
|
||||
"-Name",
|
||||
"ForcedPhysicalSectorSizeInBytes",
|
||||
"-PropertyType MultiString",
|
||||
"-Force -Value",
|
||||
"\"* 4095\""
|
||||
]);
|
||||
dPrint("nvme_PhysicalBytes result == ${result.stdout}");
|
||||
return result.stderr;
|
||||
try {
|
||||
await win32.addNvmePatch();
|
||||
return "";
|
||||
} catch (e) {
|
||||
dPrint("addNvmePatch error: $e");
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static doRemoveNvmePath() async {
|
||||
static Future<bool> doRemoveNvmePath() async {
|
||||
try {
|
||||
var result = await Process.run(powershellPath, [
|
||||
"Clear-ItemProperty",
|
||||
"-Path",
|
||||
"\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"",
|
||||
"-Name",
|
||||
"\"ForcedPhysicalSectorSizeInBytes\""
|
||||
]);
|
||||
dPrint("doRemoveNvmePath result ==== ${result.stdout}");
|
||||
if (result.stderr == "") {
|
||||
await win32.removeNvmePatch();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("doRemoveNvmePath error: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -87,6 +41,9 @@ class SystemHelper {
|
||||
final path = confBox.get("custom_launcher_path");
|
||||
if (path != null && path != "") {
|
||||
if (await File(path).exists()) {
|
||||
if (skipEXE) {
|
||||
return "${path.toString().replaceAll("\\RSI Launcher.exe".platformPath, "")}\\".platformPath;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@ -97,42 +54,44 @@ class SystemHelper {
|
||||
"$programDataPath\\Microsoft\\Windows\\Start Menu\\Programs\\Roberts Space Industries\\RSI Launcher.lnk";
|
||||
final rsiLinkFile = File(rsiFilePath);
|
||||
if (await rsiLinkFile.exists()) {
|
||||
final r = await Process.run(SystemHelper.powershellPath, [
|
||||
"(New-Object -ComObject WScript.Shell).CreateShortcut(\"$rsiFilePath\").targetpath"
|
||||
]);
|
||||
if (r.stdout.toString().contains("RSI Launcher.exe")) {
|
||||
final start = r.stdout.toString().split("RSI Launcher.exe");
|
||||
try {
|
||||
final targetPath = await win32.resolveShortcut(lnkPath: rsiFilePath);
|
||||
if (targetPath.contains("RSI Launcher.exe")) {
|
||||
final start = targetPath.split("RSI Launcher.exe");
|
||||
if (skipEXE) {
|
||||
return start[0];
|
||||
}
|
||||
return "${start[0]}RSI Launcher.exe";
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("resolveShortcut error: $e");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static killRSILauncher() async {
|
||||
var psr = await Process.run(
|
||||
powershellPath, ["ps", "\"RSI Launcher\"", "|select -expand id"]);
|
||||
if (psr.stderr == "") {
|
||||
for (var value in (psr.stdout ?? "").toString().split("\n")) {
|
||||
dPrint(value);
|
||||
if (value != "") {
|
||||
Process.killPid(int.parse(value));
|
||||
}
|
||||
static Future<void> killRSILauncher() async {
|
||||
var pList = await getPID("RSI Launcher");
|
||||
for (var pid in pList) {
|
||||
try {
|
||||
Process.killPid(pid);
|
||||
} catch (e) {
|
||||
dPrint("killRSILauncher Error: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<String>> getPID(String name) async {
|
||||
final r = await Process.run(powershellPath, ["(ps $name).Id"]);
|
||||
final str = r.stdout.toString().trim();
|
||||
dPrint(str);
|
||||
if (str.isEmpty) return [];
|
||||
return str.split("\n");
|
||||
static Future<List<int>> getPID(String name) async {
|
||||
try {
|
||||
final pList = await win32.getProcessListByName(processName: name);
|
||||
return pList.map((e) => e.pid).toList();
|
||||
} catch (e) {
|
||||
dPrint("getPID Error: $e");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static checkAndLaunchRSILauncher(String path) async {
|
||||
static Future<void> checkAndLaunchRSILauncher(String path) async {
|
||||
// check running and kill
|
||||
await killRSILauncher();
|
||||
// launch
|
||||
@ -140,65 +99,84 @@ class SystemHelper {
|
||||
if (processorAffinity == null) {
|
||||
Process.run(path, []);
|
||||
} else {
|
||||
Process.run("cmd.exe", [
|
||||
'/C',
|
||||
'Start',
|
||||
'""',
|
||||
'/High',
|
||||
'/Affinity',
|
||||
processorAffinity,
|
||||
path,
|
||||
]);
|
||||
Process.run("cmd.exe", ['/C', 'Start', '""', '/High', '/Affinity', processorAffinity, path]);
|
||||
}
|
||||
dPrint(path);
|
||||
}
|
||||
|
||||
static Future<int> getSystemMemorySizeGB() async {
|
||||
final r = await Process.run(powershellPath, [
|
||||
"(Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum /1gb"
|
||||
]);
|
||||
return int.tryParse(r.stdout.toString().trim()) ?? 0;
|
||||
try {
|
||||
final memoryGb = await win32.getSystemMemorySizeGb();
|
||||
return memoryGb.toInt();
|
||||
} catch (e) {
|
||||
dPrint("getSystemMemorySizeGB error: $e");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> getSystemCimInstance(String win32InstanceName,
|
||||
{pathName = "Name"}) async {
|
||||
final r = await Process.run(
|
||||
powershellPath, ["(Get-CimInstance $win32InstanceName).$pathName"]);
|
||||
return r.stdout.toString().trim();
|
||||
static Future<String> getSystemCimInstance(String win32InstanceName, {pathName = "Name"}) async {
|
||||
// This method is deprecated, use getSystemInfo() instead
|
||||
try {
|
||||
final sysInfo = await win32.getSystemInfo();
|
||||
if (win32InstanceName.contains("OperatingSystem")) {
|
||||
return sysInfo.osName;
|
||||
} else if (win32InstanceName.contains("Processor")) {
|
||||
return sysInfo.cpuName;
|
||||
} else if (win32InstanceName.contains("VideoController")) {
|
||||
return sysInfo.gpuInfo;
|
||||
} else if (win32InstanceName.contains("DiskDrive")) {
|
||||
return sysInfo.diskInfo;
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("getSystemCimInstance error: $e");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static Future<String> getSystemName() async {
|
||||
final r = await Process.run(
|
||||
powershellPath, ["(Get-ComputerInfo | Select-Object -expand OsName)"]);
|
||||
return r.stdout.toString().trim();
|
||||
try {
|
||||
final sysInfo = await win32.getSystemInfo();
|
||||
return sysInfo.osName;
|
||||
} catch (e) {
|
||||
dPrint("getSystemName error: $e");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> getCpuName() async {
|
||||
final r = await Process.run(
|
||||
powershellPath, ["(Get-WmiObject -Class Win32_Processor).Name"]);
|
||||
return r.stdout.toString().trim();
|
||||
try {
|
||||
final sysInfo = await win32.getSystemInfo();
|
||||
return sysInfo.cpuName;
|
||||
} catch (e) {
|
||||
dPrint("getCpuName error: $e");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> getGpuInfo() async {
|
||||
const cmd = r"""
|
||||
$adapterMemory = (Get-ItemProperty -Path "HKLM:\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0*" -Name "HardwareInformation.AdapterString", "HardwareInformation.qwMemorySize" -Exclude PSPath -ErrorAction SilentlyContinue)
|
||||
foreach ($adapter in $adapterMemory) {
|
||||
[PSCustomObject] @{
|
||||
Model=$adapter."HardwareInformation.AdapterString"
|
||||
"VRAM (GB)"=[math]::round($adapter."HardwareInformation.qwMemorySize"/1GB)
|
||||
try {
|
||||
// Try registry first for more accurate VRAM info
|
||||
final regInfo = await win32.getGpuInfoFromRegistry();
|
||||
if (regInfo.isNotEmpty) {
|
||||
return regInfo;
|
||||
}
|
||||
// Fallback to WMI
|
||||
final sysInfo = await win32.getSystemInfo();
|
||||
return sysInfo.gpuInfo;
|
||||
} catch (e) {
|
||||
dPrint("getGpuInfo error: $e");
|
||||
return "";
|
||||
}
|
||||
""";
|
||||
final r = await Process.run(powershellPath, [cmd]);
|
||||
return r.stdout.toString().trim();
|
||||
}
|
||||
|
||||
static Future<String> getDiskInfo() async {
|
||||
return (await Process.run(powershellPath,
|
||||
["Get-PhysicalDisk | format-table BusType,FriendlyName,Size"]))
|
||||
.stdout
|
||||
.toString()
|
||||
.trim();
|
||||
try {
|
||||
final sysInfo = await win32.getSystemInfo();
|
||||
return sysInfo.diskInfo;
|
||||
} catch (e) {
|
||||
dPrint("getDiskInfo error: $e");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static Future<int> getDirLen(String path, {List<String>? skipPath}) async {
|
||||
@ -225,17 +203,17 @@ foreach ($adapter in $adapterMemory) {
|
||||
}
|
||||
|
||||
static Future<int> getNumberOfLogicalProcessors() async {
|
||||
final cpuNumberResult = await Process.run(powershellPath,
|
||||
["(Get-WmiObject -Class Win32_Processor).NumberOfLogicalProcessors"]);
|
||||
if (cpuNumberResult.exitCode != 0) return 0;
|
||||
return int.tryParse(cpuNumberResult.stdout.toString().trim()) ?? 0;
|
||||
try {
|
||||
return await win32.getNumberOfLogicalProcessors();
|
||||
} catch (e) {
|
||||
dPrint("getNumberOfLogicalProcessors error: $e");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String?> getCpuAffinity() async {
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final eCoreCount = int.tryParse(
|
||||
confBox.get("gameLaunch_eCore_count", defaultValue: "0")) ??
|
||||
0;
|
||||
final eCoreCount = int.tryParse(confBox.get("gameLaunch_eCore_count", defaultValue: "0")) ?? 0;
|
||||
final cpuNumber = await getNumberOfLogicalProcessors();
|
||||
if (cpuNumber == 0 || eCoreCount == 0 || eCoreCount > cpuNumber) {
|
||||
return null;
|
||||
@ -252,21 +230,26 @@ foreach ($adapter in $adapterMemory) {
|
||||
final binaryString = sb.toString();
|
||||
int hexDigits = (binaryString.length / 4).ceil();
|
||||
dPrint("Affinity sb ==== $sb");
|
||||
return int.parse(binaryString, radix: 2)
|
||||
.toRadixString(16)
|
||||
.padLeft(hexDigits, '0')
|
||||
.toUpperCase();
|
||||
return int.parse(binaryString, radix: 2).toRadixString(16).padLeft(hexDigits, '0').toUpperCase();
|
||||
}
|
||||
|
||||
static Future openDir(path, {bool isFile = false}) async {
|
||||
static Future openDir(dynamic path, {bool isFile = false}) async {
|
||||
dPrint("SystemHelper.openDir path === $path");
|
||||
await Process.run(SystemHelper.powershellPath,
|
||||
["explorer.exe", isFile ? "/select,$path" : "\"/select,\"$path\"\""]);
|
||||
if (Platform.isWindows) {
|
||||
try {
|
||||
await win32.openDirWithExplorer(path: path.toString(), isFile: isFile);
|
||||
} catch (e) {
|
||||
dPrint("openDir error: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getHostsFilePath() {
|
||||
if (Platform.isWindows) {
|
||||
final envVars = Platform.environment;
|
||||
final systemRoot = envVars["SYSTEMROOT"];
|
||||
return "$systemRoot\\System32\\drivers\\etc\\hosts";
|
||||
}
|
||||
return "/etc/hosts";
|
||||
}
|
||||
}
|
||||
|
||||
839
lib/common/helper/yearly_report_analyzer.dart
Normal file
839
lib/common/helper/yearly_report_analyzer.dart
Normal file
@ -0,0 +1,839 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
import 'package:starcitizen_doctor/common/helper/game_log_analyzer.dart';
|
||||
|
||||
/// 年度报告数据类
|
||||
class YearlyReportData {
|
||||
// 基础统计
|
||||
final int totalLaunchCount; // 累计启动次数
|
||||
final Duration totalPlayTime; // 累计游玩时长
|
||||
final int yearlyLaunchCount; // 年度启动次数
|
||||
final Duration yearlyPlayTime; // 年度游玩时长
|
||||
final int totalCrashCount; // 总崩溃次数
|
||||
final int yearlyCrashCount; // 年度崩溃次数
|
||||
|
||||
// 时间统计
|
||||
final DateTime? yearlyFirstLaunchTime; // 年度第一次启动时间
|
||||
final DateTime? earliestPlayDate; // 年度游玩最早的一天 (05:00及以后)
|
||||
final DateTime? latestPlayDate; // 年度游玩最晚的一天 (04:00及以前)
|
||||
|
||||
// 游玩时长统计
|
||||
final Duration? longestSession; // 最长单次游玩时长
|
||||
final DateTime? longestSessionDate; // 最长游玩那一天
|
||||
final Duration? shortestSession; // 最短单次游玩时长 (超过5分钟的)
|
||||
final DateTime? shortestSessionDate; // 最短游玩那一天
|
||||
final Duration? averageSessionTime; // 平均单次游玩时长
|
||||
|
||||
// 载具统计
|
||||
final int yearlyVehicleDestructionCount; // 年度炸船次数
|
||||
final String? mostDestroyedVehicle; // 年度炸的最多的船
|
||||
final int mostDestroyedVehicleCount; // 炸的最多的船的次数
|
||||
final String? mostPilotedVehicle; // 年度最爱驾驶的载具
|
||||
final int mostPilotedVehicleCount; // 驾驶次数
|
||||
|
||||
// 账号统计
|
||||
final int accountCount; // 账号数量
|
||||
final String? mostPlayedAccount; // 游玩最多的账号
|
||||
final int mostPlayedAccountSessionCount; // 游玩最多的账号的会话次数
|
||||
|
||||
// 地点统计
|
||||
final List<MapEntry<String, int>> topLocations; // Top 地点访问统计
|
||||
|
||||
// 击杀统计 (K/D)
|
||||
final int yearlyKillCount; // 年度击杀次数
|
||||
final int yearlyDeathCount; // 年度死亡次数
|
||||
final int yearlySelfKillCount; // 年度自杀次数
|
||||
|
||||
// 月份统计
|
||||
final int? mostPlayedMonth; // 游玩最多的月份 (1-12)
|
||||
final int mostPlayedMonthCount; // 该月游玩次数
|
||||
final int? leastPlayedMonth; // 游玩最少的月份 (1-12, 不包括完全没上游戏的月份)
|
||||
final int leastPlayedMonthCount; // 该月游玩次数
|
||||
|
||||
// 连续游玩/离线统计
|
||||
final int longestPlayStreak; // 最长连续游玩天数
|
||||
final DateTime? playStreakStartDate; // 连续游玩开始日期
|
||||
final DateTime? playStreakEndDate; // 连续游玩结束日期
|
||||
final int longestOfflineStreak; // 最长连续离线天数
|
||||
final DateTime? offlineStreakStartDate; // 连续离线开始日期
|
||||
final DateTime? offlineStreakEndDate; // 连续离线结束日期
|
||||
|
||||
// 详细数据 (用于展示)
|
||||
final Map<String, int> vehiclePilotedDetails; // 驾驶载具详情
|
||||
final Map<String, int> accountSessionDetails; // 账号会话详情
|
||||
final Map<String, int> locationDetails; // 地点访问详情
|
||||
|
||||
const YearlyReportData({
|
||||
required this.totalLaunchCount,
|
||||
required this.totalPlayTime,
|
||||
required this.yearlyLaunchCount,
|
||||
required this.yearlyPlayTime,
|
||||
required this.totalCrashCount,
|
||||
required this.yearlyCrashCount,
|
||||
this.yearlyFirstLaunchTime,
|
||||
this.earliestPlayDate,
|
||||
this.latestPlayDate,
|
||||
this.longestSession,
|
||||
this.longestSessionDate,
|
||||
this.shortestSession,
|
||||
this.shortestSessionDate,
|
||||
this.averageSessionTime,
|
||||
required this.yearlyVehicleDestructionCount,
|
||||
this.mostDestroyedVehicle,
|
||||
required this.mostDestroyedVehicleCount,
|
||||
this.mostPilotedVehicle,
|
||||
required this.mostPilotedVehicleCount,
|
||||
required this.accountCount,
|
||||
this.mostPlayedAccount,
|
||||
required this.mostPlayedAccountSessionCount,
|
||||
required this.topLocations,
|
||||
required this.yearlyKillCount,
|
||||
required this.yearlyDeathCount,
|
||||
required this.yearlySelfKillCount,
|
||||
this.mostPlayedMonth,
|
||||
required this.mostPlayedMonthCount,
|
||||
this.leastPlayedMonth,
|
||||
required this.leastPlayedMonthCount,
|
||||
required this.longestPlayStreak,
|
||||
this.playStreakStartDate,
|
||||
this.playStreakEndDate,
|
||||
required this.longestOfflineStreak,
|
||||
this.offlineStreakStartDate,
|
||||
this.offlineStreakEndDate,
|
||||
required this.vehiclePilotedDetails,
|
||||
required this.accountSessionDetails,
|
||||
required this.locationDetails,
|
||||
});
|
||||
|
||||
/// 将 DateTime 转换为 UTC 毫秒时间戳
|
||||
static int? _toUtcTimestamp(DateTime? dateTime) {
|
||||
if (dateTime == null) return null;
|
||||
return dateTime.toUtc().millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
/// 转换为 JSON Map
|
||||
///
|
||||
/// 时间字段使用 UTC 毫秒时间戳 (int),配合 timezoneOffsetMinutes 可在客户端还原本地时间
|
||||
Map<String, dynamic> toJson() {
|
||||
final now = DateTime.now();
|
||||
final offset = now.timeZoneOffset;
|
||||
|
||||
return {
|
||||
// 元数据
|
||||
'generatedAtUtc': _toUtcTimestamp(now),
|
||||
'timezoneOffsetMinutes': offset.inMinutes,
|
||||
|
||||
// 基础统计
|
||||
'totalLaunchCount': totalLaunchCount,
|
||||
'totalPlayTimeMs': totalPlayTime.inMilliseconds,
|
||||
'yearlyLaunchCount': yearlyLaunchCount,
|
||||
'yearlyPlayTimeMs': yearlyPlayTime.inMilliseconds,
|
||||
'totalCrashCount': totalCrashCount,
|
||||
'yearlyCrashCount': yearlyCrashCount,
|
||||
|
||||
// 时间统计 (UTC 毫秒时间戳)
|
||||
'yearlyFirstLaunchTimeUtc': _toUtcTimestamp(yearlyFirstLaunchTime),
|
||||
'earliestPlayDateUtc': _toUtcTimestamp(earliestPlayDate),
|
||||
'latestPlayDateUtc': _toUtcTimestamp(latestPlayDate),
|
||||
|
||||
// 游玩时长统计
|
||||
'longestSessionMs': longestSession?.inMilliseconds,
|
||||
'longestSessionDateUtc': _toUtcTimestamp(longestSessionDate),
|
||||
'shortestSessionMs': shortestSession?.inMilliseconds,
|
||||
'shortestSessionDateUtc': _toUtcTimestamp(shortestSessionDate),
|
||||
'averageSessionTimeMs': averageSessionTime?.inMilliseconds,
|
||||
|
||||
// 载具统计
|
||||
'yearlyVehicleDestructionCount': yearlyVehicleDestructionCount,
|
||||
'mostDestroyedVehicle': mostDestroyedVehicle,
|
||||
'mostDestroyedVehicleCount': mostDestroyedVehicleCount,
|
||||
'mostPilotedVehicle': mostPilotedVehicle,
|
||||
'mostPilotedVehicleCount': mostPilotedVehicleCount,
|
||||
|
||||
// 账号统计
|
||||
'accountCount': accountCount,
|
||||
'mostPlayedAccount': mostPlayedAccount,
|
||||
'mostPlayedAccountSessionCount': mostPlayedAccountSessionCount,
|
||||
|
||||
// 地点统计
|
||||
'topLocations': topLocations.map((e) => {'location': e.key, 'count': e.value}).toList(),
|
||||
|
||||
// 击杀统计
|
||||
'yearlyKillCount': yearlyKillCount,
|
||||
'yearlyDeathCount': yearlyDeathCount,
|
||||
'yearlySelfKillCount': yearlySelfKillCount,
|
||||
|
||||
// 月份统计
|
||||
'mostPlayedMonth': mostPlayedMonth,
|
||||
'mostPlayedMonthCount': mostPlayedMonthCount,
|
||||
'leastPlayedMonth': leastPlayedMonth,
|
||||
'leastPlayedMonthCount': leastPlayedMonthCount,
|
||||
|
||||
// 连续游玩/离线统计
|
||||
'longestPlayStreak': longestPlayStreak,
|
||||
'playStreakStartDateUtc': _toUtcTimestamp(playStreakStartDate),
|
||||
'playStreakEndDateUtc': _toUtcTimestamp(playStreakEndDate),
|
||||
'longestOfflineStreak': longestOfflineStreak,
|
||||
'offlineStreakStartDateUtc': _toUtcTimestamp(offlineStreakStartDate),
|
||||
'offlineStreakEndDateUtc': _toUtcTimestamp(offlineStreakEndDate),
|
||||
|
||||
// 详细数据
|
||||
'vehiclePilotedDetails': vehiclePilotedDetails,
|
||||
'accountSessionDetails': accountSessionDetails,
|
||||
'locationDetails': locationDetails,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''YearlyReportData(
|
||||
totalLaunchCount: $totalLaunchCount,
|
||||
totalPlayTime: $totalPlayTime,
|
||||
yearlyLaunchCount: $yearlyLaunchCount,
|
||||
yearlyPlayTime: $yearlyPlayTime,
|
||||
totalCrashCount: $totalCrashCount,
|
||||
yearlyCrashCount: $yearlyCrashCount,
|
||||
yearlyFirstLaunchTime: $yearlyFirstLaunchTime,
|
||||
earliestPlayDate: $earliestPlayDate,
|
||||
latestPlayDate: $latestPlayDate,
|
||||
longestSession: $longestSession (on $longestSessionDate),
|
||||
shortestSession: $shortestSession (on $shortestSessionDate),
|
||||
averageSessionTime: $averageSessionTime,
|
||||
yearlyVehicleDestructionCount: $yearlyVehicleDestructionCount,
|
||||
mostDestroyedVehicle: $mostDestroyedVehicle ($mostDestroyedVehicleCount),
|
||||
mostPilotedVehicle: $mostPilotedVehicle ($mostPilotedVehicleCount),
|
||||
accountCount: $accountCount,
|
||||
mostPlayedAccount: $mostPlayedAccount ($mostPlayedAccountSessionCount),
|
||||
topLocations: ${topLocations.take(5).map((e) => '${e.key}: ${e.value}').join(', ')},
|
||||
)''';
|
||||
}
|
||||
}
|
||||
|
||||
/// 单个日志文件的统计结果 (内部使用)
|
||||
class _LogFileStats {
|
||||
DateTime? startTime;
|
||||
DateTime? endTime;
|
||||
bool hasCrash = false;
|
||||
int killCount = 0;
|
||||
int deathCount = 0;
|
||||
int selfKillCount = 0;
|
||||
Set<String> playerNames = {};
|
||||
String? currentPlayerName;
|
||||
String? firstPlayerName; // 第一个检测到的玩家名,用于去重
|
||||
|
||||
// 载具损毁: 载具型号 (去除ID后) -> 次数
|
||||
Map<String, int> vehicleDestruction = {};
|
||||
|
||||
// 驾驶载具: 载具型号 (去除ID后) -> 次数
|
||||
Map<String, int> vehiclePiloted = {};
|
||||
|
||||
// 地点访问: 地点名 -> 次数
|
||||
Map<String, int> locationVisits = {};
|
||||
|
||||
// 上次记录死亡的时间 (用于 2s 内去重)
|
||||
DateTime? _lastDeathTime;
|
||||
|
||||
// 年度内的会话记录
|
||||
List<_SessionInfo> yearlySessions = [];
|
||||
|
||||
/// 生成用于去重的唯一标识
|
||||
/// 基于启动时间和第一个玩家名生成
|
||||
String? get uniqueKey {
|
||||
if (startTime == null) return null;
|
||||
final timeKey = startTime!.toUtc().toIso8601String();
|
||||
final playerKey = firstPlayerName ?? 'unknown';
|
||||
return '$timeKey|$playerKey';
|
||||
}
|
||||
}
|
||||
|
||||
/// 单次游玩会话信息
|
||||
class _SessionInfo {
|
||||
final DateTime startTime;
|
||||
final DateTime endTime;
|
||||
|
||||
_SessionInfo({required this.startTime, required this.endTime});
|
||||
|
||||
Duration get duration => endTime.difference(startTime);
|
||||
}
|
||||
|
||||
/// 年度报告分析器
|
||||
class YearlyReportAnalyzer {
|
||||
static final _characterNamePattern = RegExp(r'name\s+([^-]+)');
|
||||
static final _vehicleDestructionPattern = RegExp(
|
||||
r"Vehicle\s+'([^']+)'.*?" // 载具型号
|
||||
r"in zone\s+'([^']+)'.*?" // Zone
|
||||
r"destroy level \d+ to (\d+).*?" // 损毁等级
|
||||
r"caused by\s+'([^']+)'", // 责任方
|
||||
);
|
||||
static final _actorDeathPattern = RegExp(
|
||||
r"Actor '([^']+)'.*?" // 受害者ID
|
||||
r"ejected from zone '([^']+)'.*?" // 原载具/区域
|
||||
r"to zone '([^']+)'", // 目标区域
|
||||
);
|
||||
|
||||
// Legacy 格式的正则表达式 (旧版日志)
|
||||
static final _legacyActorDeathPattern = RegExp(
|
||||
r"CActor::Kill: '([^']+)'.*?" // 受害者ID
|
||||
r"in zone '([^']+)'.*?" // 死亡位置区域
|
||||
r"killed by '([^']+)'.*?" // 击杀者ID
|
||||
r"with damage type '([^']+)'", // 伤害类型
|
||||
);
|
||||
static final _requestLocationInventoryPattern = RegExp(r"Player\[([^\]]+)\].*?Location\[([^\]]+)\]");
|
||||
|
||||
/// 分析单个日志文件
|
||||
static Future<_LogFileStats> _analyzeLogFile(File logFile, int targetYear) async {
|
||||
final stats = _LogFileStats();
|
||||
|
||||
try {
|
||||
if (!(await logFile.exists())) {
|
||||
return stats;
|
||||
}
|
||||
|
||||
final content = utf8.decode(await logFile.readAsBytes(), allowMalformed: true);
|
||||
final lines = content.split('\n');
|
||||
|
||||
for (final line in lines) {
|
||||
if (line.isEmpty) continue;
|
||||
|
||||
final lineTime = GameLogAnalyzer.getLogLineDateTime(line);
|
||||
|
||||
// 记录开始时间 (第一个有效时间)
|
||||
if (stats.startTime == null && lineTime != null) {
|
||||
stats.startTime = lineTime;
|
||||
}
|
||||
|
||||
// 更新结束时间 (最后一个有效时间)
|
||||
if (lineTime != null) {
|
||||
stats.endTime = lineTime;
|
||||
}
|
||||
|
||||
// 检测崩溃
|
||||
if (line.contains("Cloud Imperium Games public crash handler")) {
|
||||
stats.hasCrash = true;
|
||||
}
|
||||
|
||||
// 检测玩家登录
|
||||
if (line.contains('AccountLoginCharacterStatus_Character')) {
|
||||
final nameMatch = _characterNamePattern.firstMatch(line);
|
||||
if (nameMatch != null) {
|
||||
final playerName = nameMatch.group(1)?.trim();
|
||||
if (playerName != null &&
|
||||
playerName.isNotEmpty &&
|
||||
!playerName.contains(' ') &&
|
||||
!playerName.contains('/') &&
|
||||
!playerName.contains(r'\\') &&
|
||||
!playerName.contains('.')) {
|
||||
stats.currentPlayerName = playerName;
|
||||
// 去重添加到玩家列表 (忽略大小写)
|
||||
if (!stats.playerNames.any((n) => n.toLowerCase() == playerName.toLowerCase())) {
|
||||
stats.playerNames.add(playerName);
|
||||
}
|
||||
stats.firstPlayerName ??= playerName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 年度内的统计
|
||||
if (lineTime != null && lineTime.year == targetYear) {
|
||||
// 检测载具损毁
|
||||
final destructionMatch = _vehicleDestructionPattern.firstMatch(line);
|
||||
if (destructionMatch != null) {
|
||||
final vehicleModel = destructionMatch.group(1);
|
||||
final causedBy = destructionMatch.group(4)?.trim();
|
||||
|
||||
if (vehicleModel != null &&
|
||||
causedBy != null &&
|
||||
stats.currentPlayerName != null &&
|
||||
causedBy == stats.currentPlayerName) {
|
||||
final cleanVehicleName = GameLogAnalyzer.removeVehicleId(vehicleModel);
|
||||
stats.vehicleDestruction[cleanVehicleName] = (stats.vehicleDestruction[cleanVehicleName] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 检测驾驶载具
|
||||
final controlMatch = GameLogAnalyzer.vehicleControlPattern.firstMatch(line);
|
||||
if (controlMatch != null) {
|
||||
final vehicleName = controlMatch.group(1);
|
||||
if (vehicleName != null) {
|
||||
final cleanVehicleName = GameLogAnalyzer.removeVehicleId(vehicleName);
|
||||
// 过滤掉名为 "Default" 的载具
|
||||
if (cleanVehicleName != 'Default') {
|
||||
stats.vehiclePiloted[cleanVehicleName] = (stats.vehiclePiloted[cleanVehicleName] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检测死亡 (新版格式)
|
||||
var deathMatch = _actorDeathPattern.firstMatch(line);
|
||||
if (deathMatch != null) {
|
||||
final victimId = deathMatch.group(1)?.trim();
|
||||
if (victimId != null && stats.currentPlayerName != null && victimId == stats.currentPlayerName) {
|
||||
// 防抖去重 (2秒内不重复计数)
|
||||
if (stats._lastDeathTime == null || lineTime.difference(stats._lastDeathTime!).abs().inSeconds > 2) {
|
||||
stats.deathCount++;
|
||||
stats._lastDeathTime = lineTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检测死亡 (旧版格式 - Legacy)
|
||||
final legacyDeathMatch = _legacyActorDeathPattern.firstMatch(line);
|
||||
if (legacyDeathMatch != null) {
|
||||
final victimId = legacyDeathMatch.group(1)?.trim();
|
||||
final killerId = legacyDeathMatch.group(3)?.trim();
|
||||
|
||||
if (victimId != null && stats.currentPlayerName != null) {
|
||||
bool isRecent =
|
||||
stats._lastDeathTime != null && lineTime.difference(stats._lastDeathTime!).abs().inSeconds <= 2;
|
||||
|
||||
// 检测自杀
|
||||
// 自杀逻辑:selfKillCount 独立统计自杀次数
|
||||
// deathCount 包含所有死亡(普通死亡+自杀),因此自杀时不再从 deathCount 回退
|
||||
if (victimId == killerId) {
|
||||
if (victimId == stats.currentPlayerName) {
|
||||
if (isRecent) {
|
||||
// 如果最近已经记录过一次死亡 (通用格式记录的),说明已经在 deathCount 中计入
|
||||
// 只需额外标记为自杀
|
||||
stats.selfKillCount++;
|
||||
// 更新时间以保持锁定
|
||||
stats._lastDeathTime = lineTime;
|
||||
} else {
|
||||
// 没有被新版格式记录过,需要同时计入 deathCount 和 selfKillCount
|
||||
stats.deathCount++;
|
||||
stats.selfKillCount++;
|
||||
stats._lastDeathTime = lineTime;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 检测死亡 (被击杀)
|
||||
if (victimId == stats.currentPlayerName) {
|
||||
// 如果最近已经记录过 (可能是通用格式),则认为是同一事件,忽略
|
||||
if (!isRecent) {
|
||||
stats.deathCount++;
|
||||
stats._lastDeathTime = lineTime;
|
||||
}
|
||||
}
|
||||
// 检测击杀 (杀别人)
|
||||
if (killerId == stats.currentPlayerName) {
|
||||
stats.killCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检测地点访问 (RequestLocationInventory)
|
||||
final locationMatch = _requestLocationInventoryPattern.firstMatch(line);
|
||||
if (locationMatch != null) {
|
||||
final location = locationMatch.group(2)?.trim();
|
||||
if (location != null && location.isNotEmpty) {
|
||||
// 清理地点名称,移除数字ID后缀
|
||||
final cleanLocation = _cleanLocationName(location);
|
||||
stats.locationVisits[cleanLocation] = (stats.locationVisits[cleanLocation] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 记录会话信息
|
||||
if (stats.startTime != null && stats.endTime != null && stats.startTime!.year == targetYear) {
|
||||
stats.yearlySessions.add(_SessionInfo(startTime: stats.startTime!, endTime: stats.endTime!));
|
||||
}
|
||||
} catch (e) {
|
||||
// Error handled silently in isolate
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/// 清理地点名称,移除数字ID后缀
|
||||
static String _cleanLocationName(String location) {
|
||||
// 移除末尾的数字ID (如 "_12345678")
|
||||
final cleanPattern = RegExp(r'_\d{6,}$');
|
||||
return location.replaceAll(cleanPattern, '');
|
||||
}
|
||||
|
||||
/// 生成年度报告
|
||||
///
|
||||
/// [gameInstallPaths] 游戏安装路径列表 (完整路径,如 ["D:/Games/StarCitizen/LIVE", "D:/Games/StarCitizen/PTU"])
|
||||
/// [targetYear] 目标年份
|
||||
///
|
||||
/// 该方法在独立 Isolate 中运行,避免阻塞 UI
|
||||
static Future<YearlyReportData> generateReport(List<String> gameInstallPaths, int targetYear) async {
|
||||
// 在独立 Isolate 中运行以避免阻塞 UI
|
||||
return await Isolate.run(() async {
|
||||
return await _generateReportInIsolate(gameInstallPaths, targetYear);
|
||||
});
|
||||
}
|
||||
|
||||
/// 内部方法:在 Isolate 中执行的报告生成逻辑
|
||||
static Future<YearlyReportData> _generateReportInIsolate(List<String> gameInstallPaths, int targetYear) async {
|
||||
final List<File> allLogFiles = [];
|
||||
|
||||
// 从所有安装路径收集日志文件
|
||||
for (final installPath in gameInstallPaths) {
|
||||
try {
|
||||
final installDir = Directory(installPath);
|
||||
|
||||
// 检查安装目录是否存在
|
||||
if (!await installDir.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final gameLogFile = File('$installPath/Game.log');
|
||||
final logBackupsDir = Directory('$installPath/logbackups');
|
||||
|
||||
// 添加当前 Game.log
|
||||
try {
|
||||
if (await gameLogFile.exists()) {
|
||||
allLogFiles.add(gameLogFile);
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略单个文件检查错误
|
||||
}
|
||||
|
||||
// 添加备份日志
|
||||
try {
|
||||
if (await logBackupsDir.exists()) {
|
||||
await for (final entity in logBackupsDir.list()) {
|
||||
if (entity is File && entity.path.endsWith('.log')) {
|
||||
allLogFiles.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略备份目录读取错误
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略单个安装路径的错误,继续处理其他路径
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 并发分析所有日志文件,使用错误处理确保单个文件失败不影响其他文件
|
||||
final futures = allLogFiles.map((file) async {
|
||||
try {
|
||||
return await _analyzeLogFile(file, targetYear);
|
||||
} catch (_) {
|
||||
// 单个文件分析失败时返回空的统计数据
|
||||
return _LogFileStats();
|
||||
}
|
||||
});
|
||||
final allStatsRaw = await Future.wait(futures);
|
||||
|
||||
// 去重: 使用 uniqueKey (启动时间 + 玩家名) 来过滤重复的日志
|
||||
final seenKeys = <String>{};
|
||||
final allStats = <_LogFileStats>[];
|
||||
|
||||
for (final stats in allStatsRaw) {
|
||||
final key = stats.uniqueKey;
|
||||
if (key == null) {
|
||||
allStats.add(stats);
|
||||
} else if (!seenKeys.contains(key)) {
|
||||
seenKeys.add(key);
|
||||
allStats.add(stats);
|
||||
}
|
||||
}
|
||||
|
||||
// 合并统计数据
|
||||
int totalLaunchCount = allStats.length;
|
||||
Duration totalPlayTime = Duration.zero;
|
||||
int yearlyLaunchCount = 0;
|
||||
Duration yearlyPlayTime = Duration.zero;
|
||||
int totalCrashCount = 0;
|
||||
int yearlyCrashCount = 0;
|
||||
DateTime? yearlyFirstLaunchTime;
|
||||
DateTime? earliestPlayDate;
|
||||
DateTime? latestPlayDate;
|
||||
|
||||
// 会话时长统计
|
||||
Duration? longestSession;
|
||||
DateTime? longestSessionDate;
|
||||
Duration? shortestSession;
|
||||
DateTime? shortestSessionDate;
|
||||
List<Duration> allSessionDurations = [];
|
||||
|
||||
// K/D 统计
|
||||
int yearlyKillCount = 0;
|
||||
int yearlyDeathCount = 0;
|
||||
int yearlySelfKillCount = 0;
|
||||
|
||||
final Map<String, int> vehicleDestructionDetails = {};
|
||||
final Map<String, int> vehiclePilotedDetails = {};
|
||||
final Map<String, int> accountSessionDetails = {};
|
||||
final Map<String, int> locationDetails = {};
|
||||
|
||||
for (final stats in allStats) {
|
||||
// 累计游玩时长
|
||||
if (stats.startTime != null && stats.endTime != null) {
|
||||
totalPlayTime += stats.endTime!.difference(stats.startTime!);
|
||||
}
|
||||
|
||||
// 崩溃统计
|
||||
if (stats.hasCrash) {
|
||||
totalCrashCount++;
|
||||
if (stats.endTime != null && stats.endTime!.year == targetYear) {
|
||||
yearlyCrashCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 年度会话统计
|
||||
for (final session in stats.yearlySessions) {
|
||||
yearlyLaunchCount++;
|
||||
final sessionDuration = session.duration;
|
||||
yearlyPlayTime += sessionDuration;
|
||||
allSessionDurations.add(sessionDuration);
|
||||
|
||||
// 年度第一次启动时间
|
||||
if (yearlyFirstLaunchTime == null || session.startTime.isBefore(yearlyFirstLaunchTime)) {
|
||||
yearlyFirstLaunchTime = session.startTime;
|
||||
}
|
||||
|
||||
// 最早游玩的一天 (05:00及以后开始游戏)
|
||||
if (session.startTime.hour >= 5) {
|
||||
if (earliestPlayDate == null || _timeOfDayIsEarlier(session.startTime, earliestPlayDate)) {
|
||||
earliestPlayDate = session.startTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 最晚游玩的一天 (04:00及以前结束游戏)
|
||||
if (session.endTime.hour <= 4) {
|
||||
if (latestPlayDate == null || _timeOfDayIsLater(session.endTime, latestPlayDate)) {
|
||||
latestPlayDate = session.endTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 最长游玩时长
|
||||
if (longestSession == null || sessionDuration > longestSession) {
|
||||
longestSession = sessionDuration;
|
||||
longestSessionDate = session.startTime;
|
||||
}
|
||||
|
||||
// 最短游玩时长 (超过5分钟的)
|
||||
if (sessionDuration.inMinutes >= 5) {
|
||||
if (shortestSession == null || sessionDuration < shortestSession) {
|
||||
shortestSession = sessionDuration;
|
||||
shortestSessionDate = session.startTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 合并载具损毁详情 (过滤包含 PU 的载具)
|
||||
for (final entry in stats.vehicleDestruction.entries) {
|
||||
if (!entry.key.contains('PU_')) {
|
||||
vehicleDestructionDetails[entry.key] = (vehicleDestructionDetails[entry.key] ?? 0) + entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
// 合并驾驶载具详情
|
||||
for (final entry in stats.vehiclePiloted.entries) {
|
||||
vehiclePilotedDetails[entry.key] = (vehiclePilotedDetails[entry.key] ?? 0) + entry.value;
|
||||
}
|
||||
|
||||
// 累计 K/D
|
||||
yearlyKillCount += stats.killCount;
|
||||
yearlyDeathCount += stats.deathCount;
|
||||
yearlySelfKillCount += stats.selfKillCount;
|
||||
|
||||
// 合并账号会话详情
|
||||
for (final playerName in stats.playerNames) {
|
||||
if (playerName.length > 16) continue;
|
||||
String targetKey = playerName;
|
||||
// 查找是否存在忽略大小写的相同 key
|
||||
for (final key in accountSessionDetails.keys) {
|
||||
if (key.toLowerCase() == playerName.toLowerCase()) {
|
||||
targetKey = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
accountSessionDetails[targetKey] = (accountSessionDetails[targetKey] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// 合并地点访问详情
|
||||
for (final entry in stats.locationVisits.entries) {
|
||||
locationDetails[entry.key] = (locationDetails[entry.key] ?? 0) + entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算平均游玩时长
|
||||
Duration? averageSessionTime;
|
||||
if (allSessionDurations.isNotEmpty) {
|
||||
final totalMs = allSessionDurations.fold<int>(0, (sum, d) => sum + d.inMilliseconds);
|
||||
averageSessionTime = Duration(milliseconds: totalMs ~/ allSessionDurations.length);
|
||||
}
|
||||
|
||||
// 计算派生统计
|
||||
final yearlyVehicleDestructionCount = vehicleDestructionDetails.values.fold(0, (a, b) => a + b);
|
||||
|
||||
String? mostDestroyedVehicle;
|
||||
int mostDestroyedVehicleCount = 0;
|
||||
for (final entry in vehicleDestructionDetails.entries) {
|
||||
if (entry.value > mostDestroyedVehicleCount) {
|
||||
mostDestroyedVehicle = entry.key;
|
||||
mostDestroyedVehicleCount = entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
String? mostPilotedVehicle;
|
||||
int mostPilotedVehicleCount = 0;
|
||||
for (final entry in vehiclePilotedDetails.entries) {
|
||||
if (entry.value > mostPilotedVehicleCount) {
|
||||
mostPilotedVehicle = entry.key;
|
||||
mostPilotedVehicleCount = entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
String? mostPlayedAccount;
|
||||
int mostPlayedAccountSessionCount = 0;
|
||||
for (final entry in accountSessionDetails.entries) {
|
||||
if (entry.value > mostPlayedAccountSessionCount) {
|
||||
mostPlayedAccount = entry.key;
|
||||
mostPlayedAccountSessionCount = entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算 Top 10 地点
|
||||
final sortedLocations = locationDetails.entries.toList()..sort((a, b) => b.value.compareTo(a.value));
|
||||
final topLocations = sortedLocations.take(10).toList();
|
||||
|
||||
// 计算月份统计
|
||||
final Map<int, int> monthlyPlayCount = {};
|
||||
final Set<DateTime> playDates = {}; // 所有游玩的日期 (仅日期部分)
|
||||
|
||||
for (final stats in allStats) {
|
||||
for (final session in stats.yearlySessions) {
|
||||
final month = session.startTime.month;
|
||||
monthlyPlayCount[month] = (monthlyPlayCount[month] ?? 0) + 1;
|
||||
// 记录游玩日期 (只保留年月日)
|
||||
playDates.add(DateTime(session.startTime.year, session.startTime.month, session.startTime.day));
|
||||
}
|
||||
}
|
||||
|
||||
int? mostPlayedMonth;
|
||||
int mostPlayedMonthCount = 0;
|
||||
int? leastPlayedMonth;
|
||||
int leastPlayedMonthCount = 0;
|
||||
|
||||
if (monthlyPlayCount.isNotEmpty) {
|
||||
// 最多游玩的月份
|
||||
for (final entry in monthlyPlayCount.entries) {
|
||||
if (entry.value > mostPlayedMonthCount) {
|
||||
mostPlayedMonth = entry.key;
|
||||
mostPlayedMonthCount = entry.value;
|
||||
}
|
||||
}
|
||||
// 最少游玩的月份 (不包括完全没上游戏的月份)
|
||||
leastPlayedMonthCount = monthlyPlayCount.values.first;
|
||||
for (final entry in monthlyPlayCount.entries) {
|
||||
if (entry.value <= leastPlayedMonthCount) {
|
||||
leastPlayedMonth = entry.key;
|
||||
leastPlayedMonthCount = entry.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算连续游玩天数和连续离线天数
|
||||
int longestPlayStreak = 0;
|
||||
DateTime? playStreakStartDate;
|
||||
DateTime? playStreakEndDate;
|
||||
int longestOfflineStreak = 0;
|
||||
DateTime? offlineStreakStartDate;
|
||||
DateTime? offlineStreakEndDate;
|
||||
|
||||
if (playDates.isNotEmpty) {
|
||||
// 将日期排序
|
||||
final sortedDates = playDates.toList()..sort();
|
||||
|
||||
// 计算连续游玩天数
|
||||
int currentStreak = 1;
|
||||
DateTime streakStart = sortedDates.first;
|
||||
|
||||
for (int i = 1; i < sortedDates.length; i++) {
|
||||
final diff = sortedDates[i].difference(sortedDates[i - 1]).inDays;
|
||||
if (diff == 1) {
|
||||
currentStreak++;
|
||||
} else {
|
||||
if (currentStreak > longestPlayStreak) {
|
||||
longestPlayStreak = currentStreak;
|
||||
playStreakStartDate = streakStart;
|
||||
playStreakEndDate = sortedDates[i - 1];
|
||||
}
|
||||
currentStreak = 1;
|
||||
streakStart = sortedDates[i];
|
||||
}
|
||||
}
|
||||
// 检查最后一段连续
|
||||
if (currentStreak > longestPlayStreak) {
|
||||
longestPlayStreak = currentStreak;
|
||||
playStreakStartDate = streakStart;
|
||||
playStreakEndDate = sortedDates.last;
|
||||
}
|
||||
|
||||
// 计算连续离线天数 (在游玩日期之间的间隔)
|
||||
for (int i = 1; i < sortedDates.length; i++) {
|
||||
final gapDays = sortedDates[i].difference(sortedDates[i - 1]).inDays - 1;
|
||||
if (gapDays > longestOfflineStreak) {
|
||||
longestOfflineStreak = gapDays;
|
||||
offlineStreakStartDate = sortedDates[i - 1].add(const Duration(days: 1));
|
||||
offlineStreakEndDate = sortedDates[i].subtract(const Duration(days: 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return YearlyReportData(
|
||||
totalLaunchCount: totalLaunchCount,
|
||||
totalPlayTime: totalPlayTime,
|
||||
yearlyLaunchCount: yearlyLaunchCount,
|
||||
yearlyPlayTime: yearlyPlayTime,
|
||||
totalCrashCount: totalCrashCount,
|
||||
yearlyCrashCount: yearlyCrashCount,
|
||||
yearlyFirstLaunchTime: yearlyFirstLaunchTime,
|
||||
earliestPlayDate: earliestPlayDate,
|
||||
latestPlayDate: latestPlayDate,
|
||||
longestSession: longestSession,
|
||||
longestSessionDate: longestSessionDate,
|
||||
shortestSession: shortestSession,
|
||||
shortestSessionDate: shortestSessionDate,
|
||||
averageSessionTime: averageSessionTime,
|
||||
yearlyVehicleDestructionCount: yearlyVehicleDestructionCount,
|
||||
mostDestroyedVehicle: mostDestroyedVehicle,
|
||||
mostDestroyedVehicleCount: mostDestroyedVehicleCount,
|
||||
mostPilotedVehicle: mostPilotedVehicle,
|
||||
mostPilotedVehicleCount: mostPilotedVehicleCount,
|
||||
accountCount: accountSessionDetails.length,
|
||||
mostPlayedAccount: mostPlayedAccount,
|
||||
mostPlayedAccountSessionCount: mostPlayedAccountSessionCount,
|
||||
topLocations: topLocations,
|
||||
yearlyKillCount: yearlyKillCount,
|
||||
yearlyDeathCount: yearlyDeathCount,
|
||||
yearlySelfKillCount: yearlySelfKillCount,
|
||||
mostPlayedMonth: mostPlayedMonth,
|
||||
mostPlayedMonthCount: mostPlayedMonthCount,
|
||||
leastPlayedMonth: leastPlayedMonth,
|
||||
leastPlayedMonthCount: leastPlayedMonthCount,
|
||||
longestPlayStreak: longestPlayStreak,
|
||||
playStreakStartDate: playStreakStartDate,
|
||||
playStreakEndDate: playStreakEndDate,
|
||||
longestOfflineStreak: longestOfflineStreak,
|
||||
offlineStreakStartDate: offlineStreakStartDate,
|
||||
offlineStreakEndDate: offlineStreakEndDate,
|
||||
vehiclePilotedDetails: vehiclePilotedDetails,
|
||||
accountSessionDetails: accountSessionDetails,
|
||||
locationDetails: locationDetails,
|
||||
);
|
||||
}
|
||||
|
||||
/// 比较两个时间的 时:分 是否更早
|
||||
static bool _timeOfDayIsEarlier(DateTime a, DateTime b) {
|
||||
if (a.hour < b.hour) return true;
|
||||
if (a.hour > b.hour) return false;
|
||||
return a.minute < b.minute;
|
||||
}
|
||||
|
||||
/// 比较两个时间的 时:分 是否更晚
|
||||
static bool _timeOfDayIsLater(DateTime a, DateTime b) {
|
||||
if (a.hour > b.hour) return true;
|
||||
if (a.hour < b.hour) return false;
|
||||
return a.minute > b.minute;
|
||||
}
|
||||
}
|
||||
46
lib/common/io/doh_client.dart
Normal file
46
lib/common/io/doh_client.dart
Normal file
@ -0,0 +1,46 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/data/doh_client_response_data.dart';
|
||||
|
||||
class DohClient {
|
||||
static Future<DohClientResponseData?> resolve(
|
||||
String domain, String type) async {
|
||||
try {
|
||||
final r = await RSHttp.getText(
|
||||
"${ConstConf.dohAddress}?name=$domain&type=$type");
|
||||
final data = DohClientResponseData.fromJson(json.decode(r));
|
||||
return data;
|
||||
} catch (e) {
|
||||
dPrint("DohClient.resolve error: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<String>?> resolveIP(String domain, String type) async {
|
||||
final data = await resolve(domain, type);
|
||||
if (data == null) return [];
|
||||
return data.answer?.map((e) => _removeDataPadding(e.data)).toList();
|
||||
}
|
||||
|
||||
static Future<List<String>?> resolveTXT(String domain) async {
|
||||
final data = await resolve(domain, "TXT");
|
||||
if (data == null) return [];
|
||||
return data.answer?.map((e) => _removeDataPadding(e.data)).toList();
|
||||
}
|
||||
|
||||
static String _removeDataPadding(String? data) {
|
||||
// data demo: {"data":"\"https://git.scbox.xkeyc.cn,https://gitapi.scbox.org\""}
|
||||
if (data == null) return "";
|
||||
data = data.trim();
|
||||
if (data.startsWith("\"")) {
|
||||
data = data.substring(1);
|
||||
}
|
||||
if (data.endsWith("\"")) {
|
||||
data = data.substring(0, data.length - 1);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -1,42 +1,58 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/http_api.dart' as rust_http;
|
||||
import 'package:starcitizen_doctor/common/rust/api/http_api.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/http_package.dart';
|
||||
|
||||
class RSHttp {
|
||||
static init() async {
|
||||
static Future<void> init() async {
|
||||
await rust_http.setDefaultHeader(headers: {
|
||||
"User-Agent":
|
||||
"SCToolBox/${ConstConf.appVersion} (${ConstConf.appVersionCode})${ConstConf.isMSE ? "" : " DEV"} RSHttp"
|
||||
});
|
||||
}
|
||||
|
||||
static Future<RustHttpResponse> get(String url,
|
||||
{Map<String, String>? headers, String? withIpAddress}) async {
|
||||
static Future<RustHttpResponse> get(
|
||||
String url, {
|
||||
Map<String, String>? headers,
|
||||
String? withIpAddress,
|
||||
bool? withCustomDns,
|
||||
}) async {
|
||||
final r = await rust_http.fetch(
|
||||
method: MyMethod.gets,
|
||||
url: url,
|
||||
headers: headers,
|
||||
withIpAddress: withIpAddress);
|
||||
withIpAddress: withIpAddress,
|
||||
withCustomDns: withCustomDns,
|
||||
);
|
||||
return r;
|
||||
}
|
||||
|
||||
static Future<String> getText(String url,
|
||||
{Map<String, String>? headers, String? withIpAddress}) async {
|
||||
final r = await get(url, headers: headers, withIpAddress: withIpAddress);
|
||||
static Future<String> getText(
|
||||
String url, {
|
||||
Map<String, String>? headers,
|
||||
String? withIpAddress,
|
||||
bool? withCustomDns,
|
||||
}) async {
|
||||
final r = await get(url,
|
||||
headers: headers,
|
||||
withIpAddress: withIpAddress,
|
||||
withCustomDns: withCustomDns);
|
||||
if (r.data == null) return "";
|
||||
final str = utf8.decode(r.data!);
|
||||
return str;
|
||||
}
|
||||
|
||||
static Future<RustHttpResponse> postData(String url,
|
||||
{Map<String, String>? headers,
|
||||
static Future<RustHttpResponse> postData(
|
||||
String url, {
|
||||
Map<String, String>? headers,
|
||||
String? contentType,
|
||||
Uint8List? data,
|
||||
String? withIpAddress}) async {
|
||||
String? withIpAddress,
|
||||
bool? withCustomDns,
|
||||
}) async {
|
||||
if (contentType != null) {
|
||||
headers ??= {};
|
||||
headers["Content-Type"] = contentType;
|
||||
@ -46,17 +62,25 @@ class RSHttp {
|
||||
url: url,
|
||||
headers: headers,
|
||||
inputData: data,
|
||||
withIpAddress: withIpAddress);
|
||||
withIpAddress: withIpAddress,
|
||||
withCustomDns: withCustomDns,
|
||||
);
|
||||
return r;
|
||||
}
|
||||
|
||||
static Future<RustHttpResponse> head(String url,
|
||||
{Map<String, String>? headers, String? withIpAddress}) async {
|
||||
static Future<RustHttpResponse> head(
|
||||
String url, {
|
||||
Map<String, String>? headers,
|
||||
String? withIpAddress,
|
||||
bool? withCustomDns,
|
||||
}) async {
|
||||
final r = await rust_http.fetch(
|
||||
method: MyMethod.head,
|
||||
url: url,
|
||||
headers: headers,
|
||||
withIpAddress: withIpAddress);
|
||||
withIpAddress: withIpAddress,
|
||||
withCustomDns: withCustomDns,
|
||||
);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
68
lib/common/rust/api/applinks_api.dart
Normal file
68
lib/common/rust/api/applinks_api.dart
Normal file
@ -0,0 +1,68 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
||||
|
||||
/// Check if the URL scheme is already registered with the correct executable path
|
||||
Future<ApplinksRegistrationResult> checkApplinksRegistration({
|
||||
required String scheme,
|
||||
}) => RustLib.instance.api.crateApiApplinksApiCheckApplinksRegistration(
|
||||
scheme: scheme,
|
||||
);
|
||||
|
||||
/// Register URL scheme in Windows registry
|
||||
/// This will create or update the registry keys for the custom URL scheme
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `scheme` - The URL scheme to register (e.g., "sctoolbox")
|
||||
/// * `app_name` - Optional application display name (e.g., "SCToolBox"). If provided,
|
||||
/// the registry will show "URL:{app_name} Protocol" as the scheme description.
|
||||
Future<ApplinksRegistrationResult> registerApplinks({
|
||||
required String scheme,
|
||||
String? appName,
|
||||
}) => RustLib.instance.api.crateApiApplinksApiRegisterApplinks(
|
||||
scheme: scheme,
|
||||
appName: appName,
|
||||
);
|
||||
|
||||
/// Unregister URL scheme from Windows registry
|
||||
Future<ApplinksRegistrationResult> unregisterApplinks({
|
||||
required String scheme,
|
||||
}) =>
|
||||
RustLib.instance.api.crateApiApplinksApiUnregisterApplinks(scheme: scheme);
|
||||
|
||||
/// Applinks URL scheme registration result
|
||||
class ApplinksRegistrationResult {
|
||||
/// Whether registration was successful
|
||||
final bool success;
|
||||
|
||||
/// Detailed message about the operation
|
||||
final String message;
|
||||
|
||||
/// Whether the registry was modified (false if already configured correctly)
|
||||
final bool wasModified;
|
||||
|
||||
const ApplinksRegistrationResult({
|
||||
required this.success,
|
||||
required this.message,
|
||||
required this.wasModified,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
success.hashCode ^ message.hashCode ^ wasModified.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ApplinksRegistrationResult &&
|
||||
runtimeType == other.runtimeType &&
|
||||
success == other.success &&
|
||||
message == other.message &&
|
||||
wasModified == other.wasModified;
|
||||
}
|
||||
@ -1,14 +1,16 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.0.0-dev.33.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
Future<RsiLauncherAsarData> getRsiLauncherAsarData(
|
||||
{required String asarPath, dynamic hint}) =>
|
||||
RustLib.instance.api.getRsiLauncherAsarData(asarPath: asarPath, hint: hint);
|
||||
Future<RsiLauncherAsarData> getRsiLauncherAsarData({
|
||||
required String asarPath,
|
||||
}) => RustLib.instance.api.crateApiAsarApiGetRsiLauncherAsarData(
|
||||
asarPath: asarPath,
|
||||
);
|
||||
|
||||
class RsiLauncherAsarData {
|
||||
final String asarPath;
|
||||
@ -21,9 +23,11 @@ class RsiLauncherAsarData {
|
||||
required this.mainJsContent,
|
||||
});
|
||||
|
||||
Future<void> writeMainJs({required List<int> content, dynamic hint}) =>
|
||||
RustLib.instance.api.rsiLauncherAsarDataWriteMainJs(
|
||||
that: this, content: content, hint: hint);
|
||||
Future<void> writeMainJs({required List<int> content}) =>
|
||||
RustLib.instance.api.crateApiAsarApiRsiLauncherAsarDataWriteMainJs(
|
||||
that: this,
|
||||
content: content,
|
||||
);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
|
||||
284
lib/common/rust/api/downloader_api.dart
Normal file
284
lib/common/rust/api/downloader_api.dart
Normal file
@ -0,0 +1,284 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `get_session`, `get_task_status`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `eq`, `fmt`, `fmt`, `fmt`
|
||||
|
||||
/// Initialize the download manager session with persistence enabled
|
||||
///
|
||||
/// Parameters:
|
||||
/// - working_dir: The directory to store session data (persistence, DHT, etc.)
|
||||
/// - default_download_dir: The default directory to store downloads
|
||||
/// - upload_limit_bps: Upload speed limit in bytes per second (0 = unlimited)
|
||||
/// - download_limit_bps: Download speed limit in bytes per second (0 = unlimited)
|
||||
Future<void> downloaderInit({
|
||||
required String workingDir,
|
||||
required String defaultDownloadDir,
|
||||
int? uploadLimitBps,
|
||||
int? downloadLimitBps,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderInit(
|
||||
workingDir: workingDir,
|
||||
defaultDownloadDir: defaultDownloadDir,
|
||||
uploadLimitBps: uploadLimitBps,
|
||||
downloadLimitBps: downloadLimitBps,
|
||||
);
|
||||
|
||||
/// Check if the downloader is initialized
|
||||
bool downloaderIsInitialized() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderIsInitialized();
|
||||
|
||||
/// Check if there are pending tasks to restore from session file (without starting the downloader)
|
||||
/// This reads the session.json file directly to check if there are any torrents saved.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - working_dir: The directory where session data is stored (same as passed to downloader_init)
|
||||
///
|
||||
/// Returns: true if there are tasks to restore, false otherwise
|
||||
bool downloaderHasPendingSessionTasks({required String workingDir}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderHasPendingSessionTasks(
|
||||
workingDir: workingDir,
|
||||
);
|
||||
|
||||
/// Add a torrent from bytes (e.g., .torrent file content)
|
||||
Future<BigInt> downloaderAddTorrent({
|
||||
required List<int> torrentBytes,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderAddTorrent(
|
||||
torrentBytes: torrentBytes,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
|
||||
/// Add a torrent from a magnet link
|
||||
Future<BigInt> downloaderAddMagnet({
|
||||
required String magnetLink,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderAddMagnet(
|
||||
magnetLink: magnetLink,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
|
||||
/// Add a torrent from URL (HTTP download not supported, only torrent file URLs)
|
||||
Future<BigInt> downloaderAddUrl({
|
||||
required String url,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderAddUrl(
|
||||
url: url,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
|
||||
/// Pause a download task
|
||||
Future<void> downloaderPause({required BigInt taskId}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderPause(taskId: taskId);
|
||||
|
||||
/// Resume a download task
|
||||
Future<void> downloaderResume({required BigInt taskId}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderResume(taskId: taskId);
|
||||
|
||||
/// Remove a download task
|
||||
/// Handles both active tasks (task_id < 10000) and cached completed tasks (task_id >= 10000)
|
||||
Future<void> downloaderRemove({
|
||||
required BigInt taskId,
|
||||
required bool deleteFiles,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderRemove(
|
||||
taskId: taskId,
|
||||
deleteFiles: deleteFiles,
|
||||
);
|
||||
|
||||
/// Get information about a specific task
|
||||
Future<DownloadTaskInfo> downloaderGetTaskInfo({required BigInt taskId}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderGetTaskInfo(
|
||||
taskId: taskId,
|
||||
);
|
||||
|
||||
/// Get all tasks (includes both active and completed tasks from cache)
|
||||
Future<List<DownloadTaskInfo>> downloaderGetAllTasks() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderGetAllTasks();
|
||||
|
||||
/// Get global statistics
|
||||
Future<DownloadGlobalStat> downloaderGetGlobalStats() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderGetGlobalStats();
|
||||
|
||||
/// Check if a task with given name exists
|
||||
///
|
||||
/// Parameters:
|
||||
/// - name: Task name to search for
|
||||
/// - downloading_only: If true, only search in active/waiting tasks. If false, include completed tasks (default: true)
|
||||
Future<bool> downloaderIsNameInTask({
|
||||
required String name,
|
||||
bool? downloadingOnly,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderIsNameInTask(
|
||||
name: name,
|
||||
downloadingOnly: downloadingOnly,
|
||||
);
|
||||
|
||||
/// Pause all tasks
|
||||
Future<void> downloaderPauseAll() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderPauseAll();
|
||||
|
||||
/// Resume all tasks
|
||||
Future<void> downloaderResumeAll() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderResumeAll();
|
||||
|
||||
/// Stop the downloader session (pauses all tasks but keeps session)
|
||||
Future<void> downloaderStop() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderStop();
|
||||
|
||||
/// Shutdown the downloader session completely (allows restart with new settings)
|
||||
Future<void> downloaderShutdown() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderShutdown();
|
||||
|
||||
/// Get all completed tasks from cache (tasks removed by downloader_remove_completed_tasks)
|
||||
/// This cache is cleared when the downloader is shutdown/restarted
|
||||
List<DownloadTaskInfo> downloaderGetCompletedTasksCache() => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiDownloaderApiDownloaderGetCompletedTasksCache();
|
||||
|
||||
/// Clear the completed tasks cache manually
|
||||
void downloaderClearCompletedTasksCache() => RustLib.instance.api
|
||||
.crateApiDownloaderApiDownloaderClearCompletedTasksCache();
|
||||
|
||||
/// Update global speed limits
|
||||
/// Note: rqbit Session doesn't support runtime limit changes,
|
||||
/// this function is a placeholder that returns an error.
|
||||
/// Speed limits should be set during downloader_init.
|
||||
Future<void> downloaderUpdateSpeedLimits({
|
||||
int? uploadLimitBps,
|
||||
int? downloadLimitBps,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderUpdateSpeedLimits(
|
||||
uploadLimitBps: uploadLimitBps,
|
||||
downloadLimitBps: downloadLimitBps,
|
||||
);
|
||||
|
||||
/// Remove all completed tasks (equivalent to aria2's --seed-time=0 behavior)
|
||||
/// Removed tasks are cached in memory and can be queried via downloader_get_completed_tasks_cache
|
||||
Future<int> downloaderRemoveCompletedTasks() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderRemoveCompletedTasks();
|
||||
|
||||
/// Check if there are any active (non-completed) tasks
|
||||
Future<bool> downloaderHasActiveTasks() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderHasActiveTasks();
|
||||
|
||||
/// Global statistics
|
||||
class DownloadGlobalStat {
|
||||
final BigInt downloadSpeed;
|
||||
final BigInt uploadSpeed;
|
||||
final BigInt numActive;
|
||||
final BigInt numWaiting;
|
||||
|
||||
const DownloadGlobalStat({
|
||||
required this.downloadSpeed,
|
||||
required this.uploadSpeed,
|
||||
required this.numActive,
|
||||
required this.numWaiting,
|
||||
});
|
||||
|
||||
static Future<DownloadGlobalStat> default_() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloadGlobalStatDefault();
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
downloadSpeed.hashCode ^
|
||||
uploadSpeed.hashCode ^
|
||||
numActive.hashCode ^
|
||||
numWaiting.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DownloadGlobalStat &&
|
||||
runtimeType == other.runtimeType &&
|
||||
downloadSpeed == other.downloadSpeed &&
|
||||
uploadSpeed == other.uploadSpeed &&
|
||||
numActive == other.numActive &&
|
||||
numWaiting == other.numWaiting;
|
||||
}
|
||||
|
||||
/// Download task information
|
||||
class DownloadTaskInfo {
|
||||
final BigInt id;
|
||||
final String name;
|
||||
final DownloadTaskStatus status;
|
||||
final BigInt totalBytes;
|
||||
final BigInt downloadedBytes;
|
||||
final BigInt uploadedBytes;
|
||||
final BigInt downloadSpeed;
|
||||
final BigInt uploadSpeed;
|
||||
final double progress;
|
||||
final BigInt numPeers;
|
||||
final String outputFolder;
|
||||
|
||||
const DownloadTaskInfo({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.status,
|
||||
required this.totalBytes,
|
||||
required this.downloadedBytes,
|
||||
required this.uploadedBytes,
|
||||
required this.downloadSpeed,
|
||||
required this.uploadSpeed,
|
||||
required this.progress,
|
||||
required this.numPeers,
|
||||
required this.outputFolder,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
name.hashCode ^
|
||||
status.hashCode ^
|
||||
totalBytes.hashCode ^
|
||||
downloadedBytes.hashCode ^
|
||||
uploadedBytes.hashCode ^
|
||||
downloadSpeed.hashCode ^
|
||||
uploadSpeed.hashCode ^
|
||||
progress.hashCode ^
|
||||
numPeers.hashCode ^
|
||||
outputFolder.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DownloadTaskInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id &&
|
||||
name == other.name &&
|
||||
status == other.status &&
|
||||
totalBytes == other.totalBytes &&
|
||||
downloadedBytes == other.downloadedBytes &&
|
||||
uploadedBytes == other.uploadedBytes &&
|
||||
downloadSpeed == other.downloadSpeed &&
|
||||
uploadSpeed == other.uploadSpeed &&
|
||||
progress == other.progress &&
|
||||
numPeers == other.numPeers &&
|
||||
outputFolder == other.outputFolder;
|
||||
}
|
||||
|
||||
/// Download task status
|
||||
enum DownloadTaskStatus {
|
||||
/// Checking/verifying existing files
|
||||
checking,
|
||||
|
||||
/// Actively downloading
|
||||
live,
|
||||
|
||||
/// Paused
|
||||
paused,
|
||||
|
||||
/// Error occurred
|
||||
error,
|
||||
|
||||
/// Download completed
|
||||
finished,
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.0.0-dev.33.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
@ -7,40 +7,46 @@ import '../frb_generated.dart';
|
||||
import '../http_package.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
Future<void> setDefaultHeader(
|
||||
{required Map<String, String> headers, dynamic hint}) =>
|
||||
RustLib.instance.api.setDefaultHeader(headers: headers, hint: hint);
|
||||
// These functions are ignored because they are not marked as `pub`: `_my_method_to_hyper_method`
|
||||
|
||||
Future<RustHttpResponse> fetch(
|
||||
{required MyMethod method,
|
||||
Future<void> setDefaultHeader({required Map<String, String> headers}) =>
|
||||
RustLib.instance.api.crateApiHttpApiSetDefaultHeader(headers: headers);
|
||||
|
||||
Future<RustHttpResponse> fetch({
|
||||
required MyMethod method,
|
||||
required String url,
|
||||
Map<String, String>? headers,
|
||||
Uint8List? inputData,
|
||||
String? withIpAddress,
|
||||
dynamic hint}) =>
|
||||
RustLib.instance.api.fetch(
|
||||
bool? withCustomDns,
|
||||
}) => RustLib.instance.api.crateApiHttpApiFetch(
|
||||
method: method,
|
||||
url: url,
|
||||
headers: headers,
|
||||
inputData: inputData,
|
||||
withIpAddress: withIpAddress,
|
||||
hint: hint);
|
||||
withCustomDns: withCustomDns,
|
||||
);
|
||||
|
||||
Future<List<String>> dnsLookupTxt({required String host, dynamic hint}) =>
|
||||
RustLib.instance.api.dnsLookupTxt(host: host, hint: hint);
|
||||
Future<List<String>> dnsLookupTxt({required String host}) =>
|
||||
RustLib.instance.api.crateApiHttpApiDnsLookupTxt(host: host);
|
||||
|
||||
Future<List<String>> dnsLookupIps({required String host, dynamic hint}) =>
|
||||
RustLib.instance.api.dnsLookupIps(host: host, hint: hint);
|
||||
Future<List<String>> dnsLookupIps({required String host}) =>
|
||||
RustLib.instance.api.crateApiHttpApiDnsLookupIps(host: host);
|
||||
|
||||
enum MyMethod {
|
||||
options,
|
||||
gets,
|
||||
post,
|
||||
put,
|
||||
delete,
|
||||
head,
|
||||
trace,
|
||||
connect,
|
||||
patch,
|
||||
;
|
||||
}
|
||||
/// Get the fastest URL from a list of URLs by testing them concurrently.
|
||||
/// Returns the first URL that responds successfully, canceling other requests.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `urls` - List of base URLs to test
|
||||
/// * `path_suffix` - Optional path suffix to append to each URL (e.g., "/api/version")
|
||||
/// If None, tests the base URL directly
|
||||
Future<String?> getFasterUrl({
|
||||
required List<String> urls,
|
||||
String? pathSuffix,
|
||||
}) => RustLib.instance.api.crateApiHttpApiGetFasterUrl(
|
||||
urls: urls,
|
||||
pathSuffix: pathSuffix,
|
||||
);
|
||||
|
||||
enum MyMethod { options, gets, post, put, delete, head, trace, connect, patch }
|
||||
|
||||
75
lib/common/rust/api/ort_api.dart
Normal file
75
lib/common/rust/api/ort_api.dart
Normal file
@ -0,0 +1,75 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
/// 加载 ONNX 翻译模型
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_path` - 模型文件夹路径
|
||||
/// * `model_key` - 模型缓存键(用于标识模型,如 "zh-en")
|
||||
/// * `quantization_suffix` - 量化后缀(如 "_q4", "_q8",空字符串表示使用默认模型)
|
||||
/// * `use_xnnpack` - 是否使用 XNNPACK 加速
|
||||
///
|
||||
Future<void> loadTranslationModel({
|
||||
required String modelPath,
|
||||
required String modelKey,
|
||||
required String quantizationSuffix,
|
||||
required bool useXnnpack,
|
||||
}) => RustLib.instance.api.crateApiOrtApiLoadTranslationModel(
|
||||
modelPath: modelPath,
|
||||
modelKey: modelKey,
|
||||
quantizationSuffix: quantizationSuffix,
|
||||
useXnnpack: useXnnpack,
|
||||
);
|
||||
|
||||
/// 翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
/// * `text` - 要翻译的文本
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<String>` - 翻译后的文本
|
||||
Future<String> translateText({
|
||||
required String modelKey,
|
||||
required String text,
|
||||
}) => RustLib.instance.api.crateApiOrtApiTranslateText(
|
||||
modelKey: modelKey,
|
||||
text: text,
|
||||
);
|
||||
|
||||
/// 批量翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
/// * `texts` - 要翻译的文本列表
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<Vec<String>>` - 翻译后的文本列表
|
||||
Future<List<String>> translateTextBatch({
|
||||
required String modelKey,
|
||||
required List<String> texts,
|
||||
}) => RustLib.instance.api.crateApiOrtApiTranslateTextBatch(
|
||||
modelKey: modelKey,
|
||||
texts: texts,
|
||||
);
|
||||
|
||||
/// 卸载模型
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
///
|
||||
Future<void> unloadTranslationModel({required String modelKey}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiOrtApiUnloadTranslationModel(modelKey: modelKey);
|
||||
|
||||
/// 清空所有已加载的模型
|
||||
///
|
||||
/// # Returns
|
||||
Future<void> clearAllModels() =>
|
||||
RustLib.instance.api.crateApiOrtApiClearAllModels();
|
||||
@ -1,26 +1,27 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.0.0-dev.33.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// The type `RsProcess` is not used by any `pub` functions, thus it is ignored.
|
||||
// These functions are ignored because they are not marked as `pub`: `_process_output`
|
||||
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `RsProcess`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`
|
||||
|
||||
Stream<RsProcessStreamData> start(
|
||||
{required String executable,
|
||||
Stream<RsProcessStreamData> start({
|
||||
required String executable,
|
||||
required List<String> arguments,
|
||||
required String workingDirectory,
|
||||
dynamic hint}) =>
|
||||
RustLib.instance.api.start(
|
||||
}) => RustLib.instance.api.crateApiRsProcessStart(
|
||||
executable: executable,
|
||||
arguments: arguments,
|
||||
workingDirectory: workingDirectory,
|
||||
hint: hint);
|
||||
);
|
||||
|
||||
Future<void> write({required int rsPid, required String data, dynamic hint}) =>
|
||||
RustLib.instance.api.write(rsPid: rsPid, data: data, hint: hint);
|
||||
Future<void> write({required int rsPid, required String data}) =>
|
||||
RustLib.instance.api.crateApiRsProcessWrite(rsPid: rsPid, data: data);
|
||||
|
||||
class RsProcessStreamData {
|
||||
final RsProcessStreamDataType dataType;
|
||||
@ -46,9 +47,4 @@ class RsProcessStreamData {
|
||||
rsPid == other.rsPid;
|
||||
}
|
||||
|
||||
enum RsProcessStreamDataType {
|
||||
output,
|
||||
error,
|
||||
exit,
|
||||
;
|
||||
}
|
||||
enum RsProcessStreamDataType { output, error, exit }
|
||||
|
||||
120
lib/common/rust/api/unp4k_api.dart
Normal file
120
lib/common/rust/api/unp4k_api.dart
Normal file
@ -0,0 +1,120 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||
part 'unp4k_api.freezed.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `dos_datetime_to_millis`, `ensure_files_loaded`, `p4k_get_entry`
|
||||
|
||||
/// 打开 P4K 文件(仅打开,不读取文件列表)
|
||||
Future<void> p4KOpen({required String p4KPath}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KOpen(p4KPath: p4KPath);
|
||||
|
||||
/// 获取文件数量(会触发文件列表加载)
|
||||
Future<BigInt> p4KGetFileCount() =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KGetFileCount();
|
||||
|
||||
/// 获取所有文件列表
|
||||
Future<List<P4kFileItem>> p4KGetAllFiles() =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KGetAllFiles();
|
||||
|
||||
/// 提取文件到内存
|
||||
Future<Uint8List> p4KExtractToMemory({required String filePath}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KExtractToMemory(filePath: filePath);
|
||||
|
||||
/// 提取文件到磁盘
|
||||
Future<void> p4KExtractToDisk({
|
||||
required String filePath,
|
||||
required String outputPath,
|
||||
}) => RustLib.instance.api.crateApiUnp4KApiP4KExtractToDisk(
|
||||
filePath: filePath,
|
||||
outputPath: outputPath,
|
||||
);
|
||||
|
||||
/// 关闭 P4K 读取器
|
||||
Future<void> p4KClose() => RustLib.instance.api.crateApiUnp4KApiP4KClose();
|
||||
|
||||
/// 检查数据是否为 DataForge/DCB 格式
|
||||
Future<bool> dcbIsDataforge({required List<int> data}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbIsDataforge(data: data);
|
||||
|
||||
/// 从内存数据打开 DCB 文件
|
||||
Future<void> dcbOpen({required List<int> data}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbOpen(data: data);
|
||||
|
||||
/// 获取 DCB 记录数量
|
||||
Future<BigInt> dcbGetRecordCount() =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbGetRecordCount();
|
||||
|
||||
/// 获取所有 DCB 记录路径列表
|
||||
Future<List<DcbRecordItem>> dcbGetRecordList() =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbGetRecordList();
|
||||
|
||||
/// 根据路径获取单条记录的 XML
|
||||
Future<String> dcbRecordToXml({required String path}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbRecordToXml(path: path);
|
||||
|
||||
/// 根据索引获取单条记录的 XML
|
||||
Future<String> dcbRecordToXmlByIndex({required BigInt index}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbRecordToXmlByIndex(index: index);
|
||||
|
||||
/// 全文搜索 DCB 记录
|
||||
Future<List<DcbSearchResult>> dcbSearchAll({required String query}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiDcbSearchAll(query: query);
|
||||
|
||||
/// 导出 DCB 到磁盘
|
||||
/// merge: true = 合并为单个 XML,false = 分离为多个 XML 文件
|
||||
Future<void> dcbExportToDisk({
|
||||
required String outputPath,
|
||||
required String dcbPath,
|
||||
required bool merge,
|
||||
}) => RustLib.instance.api.crateApiUnp4KApiDcbExportToDisk(
|
||||
outputPath: outputPath,
|
||||
dcbPath: dcbPath,
|
||||
merge: merge,
|
||||
);
|
||||
|
||||
/// 关闭 DCB 读取器
|
||||
Future<void> dcbClose() => RustLib.instance.api.crateApiUnp4KApiDcbClose();
|
||||
|
||||
/// DCB 记录项信息
|
||||
@freezed
|
||||
sealed class DcbRecordItem with _$DcbRecordItem {
|
||||
const factory DcbRecordItem({required String path, required BigInt index}) =
|
||||
_DcbRecordItem;
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class DcbSearchMatch with _$DcbSearchMatch {
|
||||
const factory DcbSearchMatch({
|
||||
required BigInt lineNumber,
|
||||
required String lineContent,
|
||||
}) = _DcbSearchMatch;
|
||||
}
|
||||
|
||||
/// 全文搜索 DCB 记录
|
||||
/// 返回匹配的记录路径和预览摘要
|
||||
@freezed
|
||||
sealed class DcbSearchResult with _$DcbSearchResult {
|
||||
const factory DcbSearchResult({
|
||||
required String path,
|
||||
required BigInt index,
|
||||
required List<DcbSearchMatch> matches,
|
||||
}) = _DcbSearchResult;
|
||||
}
|
||||
|
||||
/// P4K 文件项信息
|
||||
@freezed
|
||||
sealed class P4kFileItem with _$P4kFileItem {
|
||||
const factory P4kFileItem({
|
||||
required String name,
|
||||
required bool isDirectory,
|
||||
required BigInt size,
|
||||
required BigInt compressedSize,
|
||||
required PlatformInt64 dateModified,
|
||||
}) = _P4kFileItem;
|
||||
}
|
||||
1048
lib/common/rust/api/unp4k_api.freezed.dart
Normal file
1048
lib/common/rust/api/unp4k_api.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
141
lib/common/rust/api/webview_api.dart
Normal file
141
lib/common/rust/api/webview_api.dart
Normal file
@ -0,0 +1,141 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||
part 'webview_api.freezed.dart';
|
||||
|
||||
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `WebViewCommand`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`
|
||||
|
||||
/// Create a new WebView window and return its ID
|
||||
String webviewCreate({required WebViewConfiguration config}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewCreate(config: config);
|
||||
|
||||
/// Navigate to a URL
|
||||
void webviewNavigate({required String id, required String url}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewNavigate(id: id, url: url);
|
||||
|
||||
/// Go back in history
|
||||
void webviewGoBack({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewGoBack(id: id);
|
||||
|
||||
/// Go forward in history
|
||||
void webviewGoForward({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewGoForward(id: id);
|
||||
|
||||
/// Reload the current page
|
||||
void webviewReload({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewReload(id: id);
|
||||
|
||||
/// Stop loading
|
||||
void webviewStop({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewStop(id: id);
|
||||
|
||||
/// Execute JavaScript in the WebView
|
||||
void webviewExecuteScript({required String id, required String script}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewExecuteScript(
|
||||
id: id,
|
||||
script: script,
|
||||
);
|
||||
|
||||
/// Set window visibility
|
||||
void webviewSetVisibility({required String id, required bool visible}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewSetVisibility(
|
||||
id: id,
|
||||
visible: visible,
|
||||
);
|
||||
|
||||
/// Close the WebView window
|
||||
void webviewClose({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewClose(id: id);
|
||||
|
||||
/// Set window size
|
||||
void webviewSetWindowSize({
|
||||
required String id,
|
||||
required int width,
|
||||
required int height,
|
||||
}) => RustLib.instance.api.crateApiWebviewApiWebviewSetWindowSize(
|
||||
id: id,
|
||||
width: width,
|
||||
height: height,
|
||||
);
|
||||
|
||||
/// Set window position
|
||||
void webviewSetWindowPosition({
|
||||
required String id,
|
||||
required int x,
|
||||
required int y,
|
||||
}) => RustLib.instance.api.crateApiWebviewApiWebviewSetWindowPosition(
|
||||
id: id,
|
||||
x: x,
|
||||
y: y,
|
||||
);
|
||||
|
||||
/// Get the current navigation state
|
||||
WebViewNavigationState webviewGetState({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewGetState(id: id);
|
||||
|
||||
/// Check if the WebView is closed
|
||||
bool webviewIsClosed({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewIsClosed(id: id);
|
||||
|
||||
/// Poll for events from the WebView (non-blocking)
|
||||
List<WebViewEvent> webviewPollEvents({required String id}) =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewPollEvents(id: id);
|
||||
|
||||
/// Get a list of all active WebView IDs
|
||||
List<String> webviewListAll() =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebviewListAll();
|
||||
|
||||
/// WebView window configuration
|
||||
@freezed
|
||||
sealed class WebViewConfiguration with _$WebViewConfiguration {
|
||||
const WebViewConfiguration._();
|
||||
const factory WebViewConfiguration({
|
||||
required String title,
|
||||
required int width,
|
||||
required int height,
|
||||
String? userDataFolder,
|
||||
required bool enableDevtools,
|
||||
required bool transparent,
|
||||
String? userAgent,
|
||||
}) = _WebViewConfiguration;
|
||||
static Future<WebViewConfiguration> default_() =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebViewConfigurationDefault();
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class WebViewEvent with _$WebViewEvent {
|
||||
const WebViewEvent._();
|
||||
|
||||
const factory WebViewEvent.navigationStarted({required String url}) =
|
||||
WebViewEvent_NavigationStarted;
|
||||
const factory WebViewEvent.navigationCompleted({required String url}) =
|
||||
WebViewEvent_NavigationCompleted;
|
||||
const factory WebViewEvent.titleChanged({required String title}) =
|
||||
WebViewEvent_TitleChanged;
|
||||
const factory WebViewEvent.webMessage({required String message}) =
|
||||
WebViewEvent_WebMessage;
|
||||
const factory WebViewEvent.windowClosed() = WebViewEvent_WindowClosed;
|
||||
const factory WebViewEvent.error({required String message}) =
|
||||
WebViewEvent_Error;
|
||||
}
|
||||
|
||||
/// Navigation state of the WebView
|
||||
@freezed
|
||||
sealed class WebViewNavigationState with _$WebViewNavigationState {
|
||||
const WebViewNavigationState._();
|
||||
const factory WebViewNavigationState({
|
||||
required String url,
|
||||
required String title,
|
||||
required bool canGoBack,
|
||||
required bool canGoForward,
|
||||
required bool isLoading,
|
||||
}) = _WebViewNavigationState;
|
||||
static Future<WebViewNavigationState> default_() =>
|
||||
RustLib.instance.api.crateApiWebviewApiWebViewNavigationStateDefault();
|
||||
}
|
||||
1092
lib/common/rust/api/webview_api.freezed.dart
Normal file
1092
lib/common/rust/api/webview_api.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,24 +1,168 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.0.0-dev.33.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
Future<void> sendNotify(
|
||||
{String? summary,
|
||||
// These functions are ignored because they are not marked as `pub`: `get_process_path`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `fmt`, `fmt`
|
||||
|
||||
Future<void> sendNotify({
|
||||
String? summary,
|
||||
String? body,
|
||||
String? appName,
|
||||
String? appId,
|
||||
dynamic hint}) =>
|
||||
RustLib.instance.api.sendNotify(
|
||||
}) => RustLib.instance.api.crateApiWin32ApiSendNotify(
|
||||
summary: summary,
|
||||
body: body,
|
||||
appName: appName,
|
||||
appId: appId,
|
||||
hint: hint);
|
||||
);
|
||||
|
||||
Future<bool> setForegroundWindow({required String windowName, dynamic hint}) =>
|
||||
RustLib.instance.api
|
||||
.setForegroundWindow(windowName: windowName, hint: hint);
|
||||
/// Get system memory size in GB
|
||||
Future<BigInt> getSystemMemorySizeGb() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetSystemMemorySizeGb();
|
||||
|
||||
/// Get number of logical processors
|
||||
Future<int> getNumberOfLogicalProcessors() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetNumberOfLogicalProcessors();
|
||||
|
||||
/// Get all system information at once
|
||||
Future<SystemInfo> getSystemInfo() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetSystemInfo();
|
||||
|
||||
/// Get GPU info from registry (more accurate VRAM)
|
||||
Future<String> getGpuInfoFromRegistry() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetGpuInfoFromRegistry();
|
||||
|
||||
/// Resolve shortcut (.lnk) file to get target path
|
||||
Future<String> resolveShortcut({required String lnkPath}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiResolveShortcut(lnkPath: lnkPath);
|
||||
|
||||
/// Open file explorer and select file/folder
|
||||
Future<void> openDirWithExplorer({
|
||||
required String path,
|
||||
required bool isFile,
|
||||
}) => RustLib.instance.api.crateApiWin32ApiOpenDirWithExplorer(
|
||||
path: path,
|
||||
isFile: isFile,
|
||||
);
|
||||
|
||||
Future<bool> setForegroundWindow({required String windowName}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiSetForegroundWindow(windowName: windowName);
|
||||
|
||||
Future<int> getProcessPidByName({required String processName}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiGetProcessPidByName(processName: processName);
|
||||
|
||||
Future<List<ProcessInfo>> getProcessListByName({required String processName}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetProcessListByName(
|
||||
processName: processName,
|
||||
);
|
||||
|
||||
/// Kill processes by name
|
||||
Future<int> killProcessByName({required String processName}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiKillProcessByName(processName: processName);
|
||||
|
||||
/// Get disk physical sector size for performance
|
||||
Future<int> getDiskPhysicalSectorSize({required String driveLetter}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiGetDiskPhysicalSectorSize(driveLetter: driveLetter);
|
||||
|
||||
/// Create a desktop shortcut
|
||||
Future<void> createDesktopShortcut({
|
||||
required String targetPath,
|
||||
required String shortcutName,
|
||||
}) => RustLib.instance.api.crateApiWin32ApiCreateDesktopShortcut(
|
||||
targetPath: targetPath,
|
||||
shortcutName: shortcutName,
|
||||
);
|
||||
|
||||
/// Run a program with admin privileges (UAC)
|
||||
Future<void> runAsAdmin({required String program, required String args}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiRunAsAdmin(
|
||||
program: program,
|
||||
args: args,
|
||||
);
|
||||
|
||||
/// Start a program (without waiting)
|
||||
Future<void> startProcess({
|
||||
required String program,
|
||||
required List<String> args,
|
||||
}) => RustLib.instance.api.crateApiWin32ApiStartProcess(
|
||||
program: program,
|
||||
args: args,
|
||||
);
|
||||
|
||||
/// Check if NVME patch is applied
|
||||
Future<bool> checkNvmePatchStatus() =>
|
||||
RustLib.instance.api.crateApiWin32ApiCheckNvmePatchStatus();
|
||||
|
||||
/// Add NVME patch to registry
|
||||
Future<void> addNvmePatch() =>
|
||||
RustLib.instance.api.crateApiWin32ApiAddNvmePatch();
|
||||
|
||||
/// Remove NVME patch from registry
|
||||
Future<void> removeNvmePatch() =>
|
||||
RustLib.instance.api.crateApiWin32ApiRemoveNvmePatch();
|
||||
|
||||
class ProcessInfo {
|
||||
final int pid;
|
||||
final String name;
|
||||
final String path;
|
||||
|
||||
const ProcessInfo({
|
||||
required this.pid,
|
||||
required this.name,
|
||||
required this.path,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode => pid.hashCode ^ name.hashCode ^ path.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ProcessInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
pid == other.pid &&
|
||||
name == other.name &&
|
||||
path == other.path;
|
||||
}
|
||||
|
||||
/// System information struct
|
||||
class SystemInfo {
|
||||
final String osName;
|
||||
final String cpuName;
|
||||
final String gpuInfo;
|
||||
final String diskInfo;
|
||||
|
||||
const SystemInfo({
|
||||
required this.osName,
|
||||
required this.cpuName,
|
||||
required this.gpuInfo,
|
||||
required this.diskInfo,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
osName.hashCode ^ cpuName.hashCode ^ gpuInfo.hashCode ^ diskInfo.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is SystemInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
osName == other.osName &&
|
||||
cpuName == other.cpuName &&
|
||||
gpuInfo == other.gpuInfo &&
|
||||
diskInfo == other.diskInfo;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,18 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.0.0-dev.33.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import 'frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
enum MyHttpVersion {
|
||||
http09,
|
||||
http10,
|
||||
http11,
|
||||
http2,
|
||||
http3,
|
||||
httpUnknown,
|
||||
;
|
||||
}
|
||||
enum MyHttpVersion { http09, http10, http11, http2, http3, httpUnknown }
|
||||
|
||||
class RustHttpResponse {
|
||||
final int statusCode;
|
||||
final Map<String, String> headers;
|
||||
final String url;
|
||||
final int? contentLength;
|
||||
final BigInt? contentLength;
|
||||
final MyHttpVersion version;
|
||||
final String remoteAddr;
|
||||
final Uint8List? data;
|
||||
|
||||
340
lib/common/rust/rust_webview_controller.dart
Normal file
340
lib/common/rust/rust_webview_controller.dart
Normal file
@ -0,0 +1,340 @@
|
||||
// Rust WebView 管理器
|
||||
// 使用 wry + tao 实现的 WebView 窗口管理
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/webview_api.dart'
|
||||
as rust_webview;
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
typedef OnWebMessageCallback = void Function(String message);
|
||||
typedef OnNavigationCallback = void Function(String url);
|
||||
typedef OnNavigationCompletedCallback = void Function(String url);
|
||||
typedef OnWindowClosedCallback = void Function();
|
||||
|
||||
/// Rust WebView 控制器
|
||||
/// 封装 Rust wry + tao WebView 的业务逻辑
|
||||
class RustWebViewController {
|
||||
final String id;
|
||||
final List<OnWebMessageCallback> _messageCallbacks = [];
|
||||
final List<OnNavigationCallback> _navigationCallbacks = [];
|
||||
final List<OnNavigationCompletedCallback> _navigationCompletedCallbacks = [];
|
||||
final List<OnWindowClosedCallback> _closeCallbacks = [];
|
||||
|
||||
Timer? _pollTimer;
|
||||
bool _isDisposed = false;
|
||||
|
||||
/// 本地化脚本(从 assets 加载)
|
||||
String _localizationScript = "";
|
||||
|
||||
/// 当前 URL
|
||||
String _currentUrl = "";
|
||||
String get currentUrl => _currentUrl;
|
||||
|
||||
RustWebViewController._(this.id);
|
||||
|
||||
/// 创建新的 WebView 窗口
|
||||
static Future<RustWebViewController> create({
|
||||
String title = "WebView",
|
||||
int width = 1280,
|
||||
int height = 720,
|
||||
String? userDataFolder,
|
||||
bool enableDevtools = false,
|
||||
bool transparent = false,
|
||||
String? userAgent,
|
||||
}) async {
|
||||
try {
|
||||
final config = rust_webview.WebViewConfiguration(
|
||||
title: title,
|
||||
width: width,
|
||||
height: height,
|
||||
userDataFolder: userDataFolder,
|
||||
enableDevtools: enableDevtools,
|
||||
transparent: transparent,
|
||||
userAgent: userAgent,
|
||||
);
|
||||
|
||||
final id = rust_webview.webviewCreate(config: config);
|
||||
final controller = RustWebViewController._(id);
|
||||
|
||||
// 加载脚本资源
|
||||
await controller._loadScripts();
|
||||
|
||||
// 启动事件轮询
|
||||
controller._startEventPolling();
|
||||
|
||||
return controller;
|
||||
} catch (e) {
|
||||
throw Exception("Failed to create WebView: $e");
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载本地化和拦截器脚本
|
||||
Future<void> _loadScripts() async {
|
||||
try {
|
||||
_localizationScript = await rootBundle.loadString('assets/web_script.js');
|
||||
} catch (e) {
|
||||
dPrint("Failed to load scripts: $e");
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动事件轮询
|
||||
void _startEventPolling() {
|
||||
_pollTimer = Timer.periodic(const Duration(milliseconds: 50), (_) {
|
||||
if (_isDisposed) return;
|
||||
_pollEvents();
|
||||
});
|
||||
}
|
||||
|
||||
/// 轮询事件
|
||||
void _pollEvents() {
|
||||
try {
|
||||
final events = rust_webview.webviewPollEvents(id: id);
|
||||
for (final event in events) {
|
||||
_handleEvent(event);
|
||||
}
|
||||
} catch (e) {
|
||||
// WebView 可能已关闭
|
||||
if (!_isDisposed) {
|
||||
dPrint("Error polling events: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理事件
|
||||
void _handleEvent(rust_webview.WebViewEvent event) {
|
||||
switch (event) {
|
||||
case rust_webview.WebViewEvent_NavigationStarted(:final url):
|
||||
dPrint("Navigation started: $url");
|
||||
_currentUrl = url;
|
||||
// 导航开始时通知
|
||||
for (final callback in _navigationCallbacks) {
|
||||
callback(url);
|
||||
}
|
||||
break;
|
||||
|
||||
case rust_webview.WebViewEvent_NavigationCompleted(:final url):
|
||||
dPrint("Navigation completed: $url");
|
||||
_currentUrl = url;
|
||||
// 导航完成回调(用于注入脚本)
|
||||
for (final callback in _navigationCompletedCallbacks) {
|
||||
callback(url);
|
||||
}
|
||||
for (final callback in _navigationCallbacks) {
|
||||
callback(url);
|
||||
}
|
||||
break;
|
||||
|
||||
case rust_webview.WebViewEvent_TitleChanged(:final title):
|
||||
dPrint("Title changed: $title");
|
||||
break;
|
||||
|
||||
case rust_webview.WebViewEvent_WebMessage(:final message):
|
||||
_handleWebMessage(message);
|
||||
break;
|
||||
|
||||
case rust_webview.WebViewEvent_WindowClosed():
|
||||
dPrint("Window closed");
|
||||
for (final callback in _closeCallbacks) {
|
||||
callback();
|
||||
}
|
||||
dispose();
|
||||
break;
|
||||
|
||||
case rust_webview.WebViewEvent_Error(:final message):
|
||||
dPrint("WebView error: $message");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理来自 WebView 的消息
|
||||
void _handleWebMessage(String message) {
|
||||
dPrint("Web message: $message");
|
||||
try {
|
||||
final data = json.decode(message);
|
||||
final action = data["action"];
|
||||
|
||||
switch (action) {
|
||||
case "navigation_state":
|
||||
// 从 JS 获取导航状态更新
|
||||
final url = data["url"] ?? "";
|
||||
final isLoading = data["isLoading"] ?? false;
|
||||
_currentUrl = url;
|
||||
if (!isLoading) {
|
||||
for (final callback in _navigationCallbacks) {
|
||||
callback(url);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "close_window":
|
||||
// 处理来自导航栏的关闭请求
|
||||
close();
|
||||
break;
|
||||
default:
|
||||
// 转发其他消息给回调
|
||||
for (final callback in _messageCallbacks) {
|
||||
callback(message);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 非 JSON 消息,直接转发
|
||||
for (final callback in _messageCallbacks) {
|
||||
callback(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 导航到 URL
|
||||
void navigate(String url) {
|
||||
if (_isDisposed) return;
|
||||
_currentUrl = url;
|
||||
rust_webview.webviewNavigate(id: id, url: url);
|
||||
}
|
||||
|
||||
/// 后退
|
||||
void goBack() {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewGoBack(id: id);
|
||||
}
|
||||
|
||||
/// 前进
|
||||
void goForward() {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewGoForward(id: id);
|
||||
}
|
||||
|
||||
/// 刷新
|
||||
void reload() {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewReload(id: id);
|
||||
}
|
||||
|
||||
/// 停止加载
|
||||
void stop() {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewStop(id: id);
|
||||
}
|
||||
|
||||
/// 执行 JavaScript
|
||||
void executeScript(String script) {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewExecuteScript(id: id, script: script);
|
||||
}
|
||||
|
||||
/// 设置窗口可见性
|
||||
void setVisible(bool visible) {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewSetVisibility(id: id, visible: visible);
|
||||
}
|
||||
|
||||
/// 关闭窗口
|
||||
void close() {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewClose(id: id);
|
||||
dispose();
|
||||
}
|
||||
|
||||
/// 设置窗口大小
|
||||
void setWindowSize(int width, int height) {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewSetWindowSize(id: id, width: width, height: height);
|
||||
}
|
||||
|
||||
/// 设置窗口位置
|
||||
void setWindowPosition(int x, int y) {
|
||||
if (_isDisposed) return;
|
||||
rust_webview.webviewSetWindowPosition(id: id, x: x, y: y);
|
||||
}
|
||||
|
||||
/// 获取当前导航状态
|
||||
rust_webview.WebViewNavigationState getState() {
|
||||
return rust_webview.webviewGetState(id: id);
|
||||
}
|
||||
|
||||
/// 检查窗口是否已关闭
|
||||
bool get isClosed {
|
||||
if (_isDisposed) return true;
|
||||
return rust_webview.webviewIsClosed(id: id);
|
||||
}
|
||||
|
||||
/// 添加消息回调
|
||||
void addOnWebMessageCallback(OnWebMessageCallback callback) {
|
||||
_messageCallbacks.add(callback);
|
||||
}
|
||||
|
||||
/// 移除消息回调
|
||||
void removeOnWebMessageCallback(OnWebMessageCallback callback) {
|
||||
_messageCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
/// 添加导航回调
|
||||
void addOnNavigationCallback(OnNavigationCallback callback) {
|
||||
_navigationCallbacks.add(callback);
|
||||
}
|
||||
|
||||
/// 移除导航回调
|
||||
void removeOnNavigationCallback(OnNavigationCallback callback) {
|
||||
_navigationCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
/// 添加导航完成回调(用于在页面加载完成后注入脚本)
|
||||
void addOnNavigationCompletedCallback(
|
||||
OnNavigationCompletedCallback callback,
|
||||
) {
|
||||
_navigationCompletedCallbacks.add(callback);
|
||||
}
|
||||
|
||||
/// 移除导航完成回调
|
||||
void removeOnNavigationCompletedCallback(
|
||||
OnNavigationCompletedCallback callback,
|
||||
) {
|
||||
_navigationCompletedCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
/// 添加关闭回调
|
||||
void addOnCloseCallback(OnWindowClosedCallback callback) {
|
||||
_closeCallbacks.add(callback);
|
||||
}
|
||||
|
||||
/// 移除关闭回调
|
||||
void removeOnCloseCallback(OnWindowClosedCallback callback) {
|
||||
_closeCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
/// 注入本地化脚本
|
||||
void injectLocalizationScript() {
|
||||
if (_localizationScript.isNotEmpty) {
|
||||
executeScript(_localizationScript);
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化网页本地化
|
||||
void initWebLocalization() {
|
||||
executeScript("InitWebLocalization()");
|
||||
}
|
||||
|
||||
/// 更新翻译词典
|
||||
void updateReplaceWords(List<Map<String, String>> words, bool enableCapture) {
|
||||
final jsonWords = json.encode(words);
|
||||
executeScript(
|
||||
"WebLocalizationUpdateReplaceWords($jsonWords, $enableCapture)",
|
||||
);
|
||||
}
|
||||
|
||||
/// 执行 RSI 登录脚本
|
||||
void executeRsiLogin(String channel) {
|
||||
executeScript('getRSILauncherToken("$channel");');
|
||||
}
|
||||
|
||||
/// 释放资源
|
||||
void dispose() {
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
_pollTimer?.cancel();
|
||||
_messageCallbacks.clear();
|
||||
_navigationCallbacks.clear();
|
||||
_navigationCompletedCallbacks.clear();
|
||||
_closeCallbacks.clear();
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
@ -7,68 +8,79 @@ import 'dart:ui' as ui;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
|
||||
Future showToast(BuildContext context, String msg,
|
||||
{BoxConstraints? constraints, String? title}) async {
|
||||
return showBaseDialog(context,
|
||||
/// String extension for cross-platform path compatibility
|
||||
extension PathStringExtension on String {
|
||||
/// Converts path separators to the current platform's format.
|
||||
/// On Windows: / -> \
|
||||
/// On Linux/macOS: \ -> /
|
||||
String get platformPath {
|
||||
if (Platform.isWindows) {
|
||||
return replaceAll('/', '\\');
|
||||
}
|
||||
return replaceAll('\\', '/');
|
||||
}
|
||||
}
|
||||
|
||||
Future showToast(BuildContext context, String msg, {BoxConstraints? constraints, String? title}) async {
|
||||
return showBaseDialog(
|
||||
context,
|
||||
title: title ?? S.current.app_common_tip,
|
||||
content: Text(msg),
|
||||
actions: [
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
|
||||
padding: const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
|
||||
child: Text(S.current.app_common_tip_i_know),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
constraints: constraints);
|
||||
constraints: constraints,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> showConfirmDialogs(
|
||||
BuildContext context, String title, Widget content,
|
||||
{String confirm = "",
|
||||
BuildContext context,
|
||||
String title,
|
||||
Widget content, {
|
||||
String confirm = "",
|
||||
String cancel = "",
|
||||
BoxConstraints? constraints}) async {
|
||||
BoxConstraints? constraints,
|
||||
}) async {
|
||||
if (confirm.isEmpty) confirm = S.current.app_common_tip_confirm;
|
||||
if (cancel.isEmpty) cancel = S.current.app_common_tip_cancel;
|
||||
|
||||
final r = await showBaseDialog(context,
|
||||
final r = await showBaseDialog(
|
||||
context,
|
||||
title: title,
|
||||
content: content,
|
||||
actions: [
|
||||
if (confirm.isNotEmpty)
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
|
||||
child: Text(confirm),
|
||||
),
|
||||
child: Padding(padding: const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8), child: Text(confirm)),
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
),
|
||||
if (cancel.isNotEmpty)
|
||||
Button(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
|
||||
child: Text(cancel),
|
||||
),
|
||||
child: Padding(padding: const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8), child: Text(cancel)),
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
),
|
||||
],
|
||||
constraints: constraints);
|
||||
constraints: constraints,
|
||||
);
|
||||
return r == true;
|
||||
}
|
||||
|
||||
Future<String?> showInputDialogs(BuildContext context,
|
||||
{required String title,
|
||||
Future<String?> showInputDialogs(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String content,
|
||||
BoxConstraints? constraints,
|
||||
String? initialValue,
|
||||
List<TextInputFormatter>? inputFormatters}) async {
|
||||
List<TextInputFormatter>? inputFormatters,
|
||||
}) async {
|
||||
String? userInput;
|
||||
constraints ??=
|
||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .38);
|
||||
constraints ??= BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .38);
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
title,
|
||||
@ -76,11 +88,7 @@ Future<String?> showInputDialogs(BuildContext context,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (content.isNotEmpty)
|
||||
Text(
|
||||
content,
|
||||
style: TextStyle(color: Colors.white.withOpacity(.6)),
|
||||
),
|
||||
if (content.isNotEmpty) Text(content, style: TextStyle(color: Colors.white.withValues(alpha: .6))),
|
||||
const SizedBox(height: 8),
|
||||
TextFormBox(
|
||||
initialValue: initialValue,
|
||||
@ -91,26 +99,25 @@ Future<String?> showInputDialogs(BuildContext context,
|
||||
),
|
||||
],
|
||||
),
|
||||
constraints: constraints);
|
||||
constraints: constraints,
|
||||
);
|
||||
if (ok == true) return userInput;
|
||||
return null;
|
||||
}
|
||||
|
||||
Future showBaseDialog(BuildContext context,
|
||||
{required String title,
|
||||
Future showBaseDialog(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required Widget content,
|
||||
List<Widget>? actions,
|
||||
BoxConstraints? constraints}) async {
|
||||
BoxConstraints? constraints,
|
||||
}) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (context) => ContentDialog(
|
||||
title: Text(title),
|
||||
content: content,
|
||||
constraints: constraints ??
|
||||
const BoxConstraints(
|
||||
maxWidth: 512,
|
||||
maxHeight: 756.0,
|
||||
),
|
||||
constraints: constraints ?? const BoxConstraints(maxWidth: 512, maxHeight: 756.0),
|
||||
actions: actions,
|
||||
),
|
||||
);
|
||||
@ -120,10 +127,8 @@ bool stringIsNotEmpty(String? s) {
|
||||
return s != null && (s.isNotEmpty);
|
||||
}
|
||||
|
||||
Future<Uint8List?> widgetToPngImage(GlobalKey repaintBoundaryKey,
|
||||
{double pixelRatio = 3.0}) async {
|
||||
RenderRepaintBoundary? boundary = repaintBoundaryKey.currentContext
|
||||
?.findRenderObject() as RenderRepaintBoundary?;
|
||||
Future<Uint8List?> widgetToPngImage(GlobalKey repaintBoundaryKey, {double pixelRatio = 3.0}) async {
|
||||
RenderRepaintBoundary? boundary = repaintBoundaryKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
|
||||
if (boundary == null) return null;
|
||||
|
||||
ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
|
||||
@ -133,11 +138,25 @@ Future<Uint8List?> widgetToPngImage(GlobalKey repaintBoundaryKey,
|
||||
return pngBytes;
|
||||
}
|
||||
|
||||
double roundDoubleTo(double value, double precision) =>
|
||||
(value * precision).round() / precision;
|
||||
double roundDoubleTo(double value, double precision) => (value * precision).round() / precision;
|
||||
|
||||
int getMinNumber(List<int> list) {
|
||||
if (list.isEmpty) return 0;
|
||||
list.sort((a, b) => a.compareTo(b));
|
||||
return list.first;
|
||||
}
|
||||
|
||||
String colorToHexCode(Color color, {ignoreTransparency = false}) {
|
||||
final colorValue = color.toARGB32();
|
||||
final colorAlpha = ((0xff000000 & colorValue) >> 24);
|
||||
final r = ((0x00ff0000 & colorValue) >> 16).toRadixString(16).padLeft(2, '0');
|
||||
final g = ((0x0000ff00 & colorValue) >> 8).toRadixString(16).padLeft(2, '0');
|
||||
final b = ((0x000000ff & colorValue) >> 0).toRadixString(16).padLeft(2, '0');
|
||||
final a = colorAlpha.toRadixString(16).padLeft(2, '0');
|
||||
|
||||
if (ignoreTransparency || colorAlpha == 255) {
|
||||
return '#$r$g$b';
|
||||
} else {
|
||||
return '#$a$r$g$b';
|
||||
}
|
||||
}
|
||||
|
||||
101
lib/common/utils/file_cache_utils.dart
Normal file
101
lib/common/utils/file_cache_utils.dart
Normal file
@ -0,0 +1,101 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
|
||||
class FileCacheUtils {
|
||||
// 存储正在进行的下载任务
|
||||
static final Map<String, Future<File>> _downloadingTasks = {};
|
||||
|
||||
// 缓存目录
|
||||
static Directory? _cacheDir;
|
||||
|
||||
/// 获取缓存目录
|
||||
static Future<Directory> _getCacheDirectory() async {
|
||||
if (_cacheDir != null) return _cacheDir!;
|
||||
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
_cacheDir = Directory(path.join(tempDir.path, 'ScToolbox_File_Cache'));
|
||||
|
||||
if (!await _cacheDir!.exists()) {
|
||||
await _cacheDir!.create(recursive: true);
|
||||
}
|
||||
|
||||
return _cacheDir!;
|
||||
}
|
||||
|
||||
/// 从URL获取文件,如果已经在下载中,则共享同一个下载任务
|
||||
static Future<File> getFile(String url) async {
|
||||
// 如果已经在下载中,直接返回正在进行的下载任务
|
||||
if (_downloadingTasks.containsKey(url)) {
|
||||
return _downloadingTasks[url]!;
|
||||
}
|
||||
|
||||
final fileTask = _downloadFile(url);
|
||||
_downloadingTasks[url] = fileTask;
|
||||
|
||||
try {
|
||||
final file = await fileTask;
|
||||
return file;
|
||||
} finally {
|
||||
// 无论成功失败,下载完成后从任务列表中移除
|
||||
_downloadingTasks.remove(url);
|
||||
}
|
||||
}
|
||||
|
||||
/// 实际进行下载的方法
|
||||
static Future<File> _downloadFile(String url) async {
|
||||
// 生成文件名 (使用URL的MD5哈希作为文件名)
|
||||
final filename = md5.convert(utf8.encode(url)).toString();
|
||||
final cacheDir = await _getCacheDirectory();
|
||||
final file = File(path.join(cacheDir.path, filename));
|
||||
|
||||
// 检查文件是否已经存在
|
||||
if (await file.exists()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
final response = await RSHttp.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
await file.writeAsBytes(response.data ?? []);
|
||||
return file;
|
||||
} else {
|
||||
throw Exception('Failed to download file: ${response.statusCode}');
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除特定URL的缓存
|
||||
static Future<bool> clearCache(String url) async {
|
||||
try {
|
||||
final filename = md5.convert(utf8.encode(url)).toString();
|
||||
final cacheDir = await _getCacheDirectory();
|
||||
final file = File(path.join(cacheDir.path, filename));
|
||||
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除所有缓存
|
||||
static Future<void> clearAllCache() async {
|
||||
try {
|
||||
final cacheDir = await _getCacheDirectory();
|
||||
if (await cacheDir.exists()) {
|
||||
await cacheDir.delete(recursive: true);
|
||||
await cacheDir.create();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('clear All Cache Error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ export 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
var _logLock = Lock();
|
||||
File? _logFile;
|
||||
|
||||
void dPrint(src) async {
|
||||
void dPrint(dynamic src) async {
|
||||
if (kDebugMode) {
|
||||
print(src);
|
||||
return;
|
||||
@ -23,10 +23,10 @@ void dPrint(src) async {
|
||||
Future<void> initDPrintFile(String applicationSupportDir) async {
|
||||
final now = DateTime.now();
|
||||
final logFile =
|
||||
File("$applicationSupportDir\\logs\\${now.millisecondsSinceEpoch}.log");
|
||||
File("$applicationSupportDir/logs/${now.millisecondsSinceEpoch}.log");
|
||||
await logFile.create(recursive: true);
|
||||
_logFile = logFile;
|
||||
final logsDir = Directory("$applicationSupportDir\\logs");
|
||||
final logsDir = Directory("$applicationSupportDir/logs");
|
||||
await for (final files in logsDir.list()) {
|
||||
if (files is File) {
|
||||
final stat = await files.stat();
|
||||
|
||||
239
lib/common/utils/multi_window_manager.dart
Normal file
239
lib/common/utils/multi_window_manager.dart
Normal file
@ -0,0 +1,239 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hexcolor/hexcolor.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/log_analyze_ui/log_analyze_ui.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'base_utils.dart';
|
||||
|
||||
part 'multi_window_manager.freezed.dart';
|
||||
|
||||
part 'multi_window_manager.g.dart';
|
||||
|
||||
/// Window type definitions for multi-window support
|
||||
class WindowTypes {
|
||||
/// Main application window
|
||||
static const String main = 'main';
|
||||
|
||||
/// Log analyzer window
|
||||
static const String logAnalyze = 'log_analyze';
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class MultiWindowAppState with _$MultiWindowAppState {
|
||||
const factory MultiWindowAppState({
|
||||
required String backgroundColor,
|
||||
required String menuColor,
|
||||
required String micaColor,
|
||||
required List<String> gameInstallPaths,
|
||||
String? languageCode,
|
||||
String? countryCode,
|
||||
@Default(10) windowsVersion,
|
||||
}) = _MultiWindowAppState;
|
||||
|
||||
factory MultiWindowAppState.fromJson(Map<String, dynamic> json) => _$MultiWindowAppStateFromJson(json);
|
||||
}
|
||||
|
||||
class MultiWindowManager {
|
||||
/// Parse window type from arguments string
|
||||
static String parseWindowType(String arguments) {
|
||||
if (arguments.isEmpty) {
|
||||
return WindowTypes.main;
|
||||
}
|
||||
try {
|
||||
final Map<String, dynamic> argument = jsonDecode(arguments);
|
||||
return argument['window_type'] ?? WindowTypes.main;
|
||||
} catch (e) {
|
||||
return WindowTypes.main;
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch a sub-window with specified type and title
|
||||
static Future<void> launchSubWindow(String type, String title, AppGlobalState appGlobalState) async {
|
||||
final gameInstallPaths = await SCLoggerHelper.getGameInstallPath(
|
||||
await SCLoggerHelper.getLauncherLogList() ?? [],
|
||||
checkExists: true,
|
||||
withVersion: AppConf.gameChannels,
|
||||
);
|
||||
|
||||
final controller = await WindowController.create(
|
||||
WindowConfiguration(
|
||||
hiddenAtLaunch: true,
|
||||
arguments: jsonEncode({
|
||||
'window_type': type,
|
||||
'app_state': _appStateToWindowState(appGlobalState, gameInstallPaths: gameInstallPaths).toJson(),
|
||||
}),
|
||||
),
|
||||
);
|
||||
await Future.delayed(Duration(milliseconds: 500)).then((_) async {
|
||||
await controller.setFrame(const Rect.fromLTWH(0, 0, 720, 800));
|
||||
await controller.setTitle(title);
|
||||
await controller.center();
|
||||
await controller.show();
|
||||
});
|
||||
}
|
||||
|
||||
static MultiWindowAppState _appStateToWindowState(AppGlobalState appGlobalState, {List<String>? gameInstallPaths}) {
|
||||
return MultiWindowAppState(
|
||||
backgroundColor: colorToHexCode(appGlobalState.themeConf.backgroundColor),
|
||||
menuColor: colorToHexCode(appGlobalState.themeConf.menuColor),
|
||||
micaColor: colorToHexCode(appGlobalState.themeConf.micaColor),
|
||||
languageCode: appGlobalState.appLocale?.languageCode,
|
||||
countryCode: appGlobalState.appLocale?.countryCode,
|
||||
gameInstallPaths: gameInstallPaths ?? [],
|
||||
windowsVersion: appGlobalState.windowsVersion,
|
||||
);
|
||||
}
|
||||
|
||||
/// Run sub-window app with parsed arguments
|
||||
static Future<void> runSubWindowApp(String arguments, String windowType) async {
|
||||
final Map<String, dynamic> argument = arguments.isEmpty ? const {} : jsonDecode(arguments);
|
||||
final windowAppState = MultiWindowAppState.fromJson(argument['app_state'] ?? {});
|
||||
Widget? windowWidget;
|
||||
|
||||
switch (windowType) {
|
||||
case WindowTypes.logAnalyze:
|
||||
windowWidget = ToolsLogAnalyzeDialogUI(appState: windowAppState);
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unknown window type: $windowType');
|
||||
}
|
||||
|
||||
await Window.initialize();
|
||||
|
||||
if (Platform.isWindows && windowAppState.windowsVersion >= 10) {
|
||||
await Window.setEffect(effect: WindowEffect.acrylic);
|
||||
}
|
||||
|
||||
final backgroundColor = HexColor(windowAppState.backgroundColor).withValues(alpha: .1);
|
||||
|
||||
return runApp(
|
||||
ProviderScope(
|
||||
child: FluentApp(
|
||||
title: "StarCitizenToolBox",
|
||||
restorationScopeId: "StarCitizenToolBox",
|
||||
themeMode: ThemeMode.dark,
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
FluentLocalizations.delegate,
|
||||
S.delegate,
|
||||
],
|
||||
supportedLocales: S.delegate.supportedLocales,
|
||||
home: windowWidget,
|
||||
theme: FluentThemeData(
|
||||
brightness: Brightness.dark,
|
||||
fontFamily: "SourceHanSansCN-Regular",
|
||||
navigationPaneTheme: NavigationPaneThemeData(backgroundColor: backgroundColor),
|
||||
menuColor: HexColor(windowAppState.menuColor),
|
||||
micaBackgroundColor: HexColor(windowAppState.micaColor),
|
||||
scaffoldBackgroundColor: backgroundColor,
|
||||
buttonTheme: ButtonThemeData(
|
||||
defaultButtonStyle: ButtonStyle(
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
side: BorderSide(color: Colors.white.withValues(alpha: .01)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
locale: windowAppState.languageCode != null
|
||||
? Locale(windowAppState.languageCode!, windowAppState.countryCode)
|
||||
: null,
|
||||
debugShowCheckedModeBanner: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension methods for WindowController to add custom functionality
|
||||
extension WindowControllerExtension on WindowController {
|
||||
/// Initialize custom window method handlers
|
||||
Future<void> doCustomInitialize() async {
|
||||
windowManager.ensureInitialized();
|
||||
return await setWindowMethodHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'window_center':
|
||||
return await windowManager.center();
|
||||
case 'window_close':
|
||||
return await windowManager.close();
|
||||
case 'window_show':
|
||||
return await windowManager.show();
|
||||
case 'window_hide':
|
||||
return await windowManager.hide();
|
||||
case 'window_focus':
|
||||
return await windowManager.focus();
|
||||
case 'window_set_frame':
|
||||
final args = call.arguments as Map;
|
||||
return await windowManager.setBounds(
|
||||
Rect.fromLTWH(
|
||||
args['left'] as double,
|
||||
args['top'] as double,
|
||||
args['width'] as double,
|
||||
args['height'] as double,
|
||||
),
|
||||
);
|
||||
case 'window_set_title':
|
||||
return await windowManager.setTitle(call.arguments as String);
|
||||
default:
|
||||
throw MissingPluginException('Not implemented: ${call.method}');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Center the window
|
||||
Future<void> center() {
|
||||
return invokeMethod('window_center');
|
||||
}
|
||||
|
||||
/// Close the window
|
||||
void close() async {
|
||||
await invokeMethod('window_close');
|
||||
}
|
||||
|
||||
/// Show the window
|
||||
Future<void> show() {
|
||||
return invokeMethod('window_show');
|
||||
}
|
||||
|
||||
/// Hide the window
|
||||
Future<void> hide() {
|
||||
return invokeMethod('window_hide');
|
||||
}
|
||||
|
||||
/// Focus the window
|
||||
Future<void> focus() {
|
||||
return invokeMethod('window_focus');
|
||||
}
|
||||
|
||||
/// Set window frame (position and size)
|
||||
Future<void> setFrame(Rect frame) {
|
||||
return invokeMethod('window_set_frame', {
|
||||
'left': frame.left,
|
||||
'top': frame.top,
|
||||
'width': frame.width,
|
||||
'height': frame.height,
|
||||
});
|
||||
}
|
||||
|
||||
/// Set window title
|
||||
Future<void> setTitle(String title) {
|
||||
return invokeMethod('window_set_title', title);
|
||||
}
|
||||
}
|
||||
301
lib/common/utils/multi_window_manager.freezed.dart
Normal file
301
lib/common/utils/multi_window_manager.freezed.dart
Normal file
@ -0,0 +1,301 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'multi_window_manager.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$MultiWindowAppState {
|
||||
|
||||
String get backgroundColor; String get menuColor; String get micaColor; List<String> get gameInstallPaths; String? get languageCode; String? get countryCode; dynamic get windowsVersion;
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$MultiWindowAppStateCopyWith<MultiWindowAppState> get copyWith => _$MultiWindowAppStateCopyWithImpl<MultiWindowAppState>(this as MultiWindowAppState, _$identity);
|
||||
|
||||
/// Serializes this MultiWindowAppState to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other.gameInstallPaths, gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(gameInstallPaths),languageCode,countryCode,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $MultiWindowAppStateCopyWith<$Res> {
|
||||
factory $MultiWindowAppStateCopyWith(MultiWindowAppState value, $Res Function(MultiWindowAppState) _then) = _$MultiWindowAppStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$MultiWindowAppStateCopyWithImpl<$Res>
|
||||
implements $MultiWindowAppStateCopyWith<$Res> {
|
||||
_$MultiWindowAppStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final MultiWindowAppState _self;
|
||||
final $Res Function(MultiWindowAppState) _then;
|
||||
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,gameInstallPaths: null == gameInstallPaths ? _self.gameInstallPaths : gameInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,languageCode: freezed == languageCode ? _self.languageCode : languageCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [MultiWindowAppState].
|
||||
extension MultiWindowAppStatePatterns on MultiWindowAppState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MultiWindowAppState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MultiWindowAppState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MultiWindowAppState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState() when $default != null:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState():
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState() when $default != null:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _MultiWindowAppState implements MultiWindowAppState {
|
||||
const _MultiWindowAppState({required this.backgroundColor, required this.menuColor, required this.micaColor, required final List<String> gameInstallPaths, this.languageCode, this.countryCode, this.windowsVersion = 10}): _gameInstallPaths = gameInstallPaths;
|
||||
factory _MultiWindowAppState.fromJson(Map<String, dynamic> json) => _$MultiWindowAppStateFromJson(json);
|
||||
|
||||
@override final String backgroundColor;
|
||||
@override final String menuColor;
|
||||
@override final String micaColor;
|
||||
final List<String> _gameInstallPaths;
|
||||
@override List<String> get gameInstallPaths {
|
||||
if (_gameInstallPaths is EqualUnmodifiableListView) return _gameInstallPaths;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_gameInstallPaths);
|
||||
}
|
||||
|
||||
@override final String? languageCode;
|
||||
@override final String? countryCode;
|
||||
@override@JsonKey() final dynamic windowsVersion;
|
||||
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$MultiWindowAppStateCopyWith<_MultiWindowAppState> get copyWith => __$MultiWindowAppStateCopyWithImpl<_MultiWindowAppState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$MultiWindowAppStateToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other._gameInstallPaths, _gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(_gameInstallPaths),languageCode,countryCode,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$MultiWindowAppStateCopyWith<$Res> implements $MultiWindowAppStateCopyWith<$Res> {
|
||||
factory _$MultiWindowAppStateCopyWith(_MultiWindowAppState value, $Res Function(_MultiWindowAppState) _then) = __$MultiWindowAppStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$MultiWindowAppStateCopyWithImpl<$Res>
|
||||
implements _$MultiWindowAppStateCopyWith<$Res> {
|
||||
__$MultiWindowAppStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _MultiWindowAppState _self;
|
||||
final $Res Function(_MultiWindowAppState) _then;
|
||||
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_MultiWindowAppState(
|
||||
backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,gameInstallPaths: null == gameInstallPaths ? _self._gameInstallPaths : gameInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,languageCode: freezed == languageCode ? _self.languageCode : languageCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
32
lib/common/utils/multi_window_manager.g.dart
Normal file
32
lib/common/utils/multi_window_manager.g.dart
Normal file
@ -0,0 +1,32 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'multi_window_manager.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_MultiWindowAppState _$MultiWindowAppStateFromJson(Map<String, dynamic> json) =>
|
||||
_MultiWindowAppState(
|
||||
backgroundColor: json['backgroundColor'] as String,
|
||||
menuColor: json['menuColor'] as String,
|
||||
micaColor: json['micaColor'] as String,
|
||||
gameInstallPaths: (json['gameInstallPaths'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
languageCode: json['languageCode'] as String?,
|
||||
countryCode: json['countryCode'] as String?,
|
||||
windowsVersion: json['windowsVersion'] ?? 10,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MultiWindowAppStateToJson(
|
||||
_MultiWindowAppState instance,
|
||||
) => <String, dynamic>{
|
||||
'backgroundColor': instance.backgroundColor,
|
||||
'menuColor': instance.menuColor,
|
||||
'micaColor': instance.micaColor,
|
||||
'gameInstallPaths': instance.gameInstallPaths,
|
||||
'languageCode': instance.languageCode,
|
||||
'countryCode': instance.countryCode,
|
||||
'windowsVersion': instance.windowsVersion,
|
||||
};
|
||||
@ -1,10 +1,11 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
// ignore_for_file: invalid_use_of_protected_member
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
|
||||
extension ProviderExtension on AutoDisposeNotifier {
|
||||
AppGlobalModel get appGlobalModel =>
|
||||
ref.read(appGlobalModelProvider.notifier);
|
||||
extension ProviderExtension<T> on $Notifier<T> {
|
||||
AppGlobalModel get appGlobalModel => ref.read(appGlobalModelProvider.notifier);
|
||||
|
||||
AppGlobalState get appGlobalState => ref.read(appGlobalModelProvider);
|
||||
|
||||
|
||||
121
lib/common/utils/url_scheme_handler.dart
Normal file
121
lib/common/utils/url_scheme_handler.dart
Normal file
@ -0,0 +1,121 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/ui/auth/auth_page.dart';
|
||||
|
||||
/// URL Scheme handler for deep linking
|
||||
/// Handles: sctoolbox://auth?callbackUrl=https://example.com
|
||||
class UrlSchemeHandler {
|
||||
static final UrlSchemeHandler _instance = UrlSchemeHandler._internal();
|
||||
factory UrlSchemeHandler() => _instance;
|
||||
UrlSchemeHandler._internal();
|
||||
|
||||
final _appLinks = AppLinks();
|
||||
StreamSubscription<Uri>? _linkSubscription;
|
||||
BuildContext? _context;
|
||||
|
||||
// Debouncing variables
|
||||
String? _lastHandledUri;
|
||||
DateTime? _lastHandledTime;
|
||||
static const _debounceDuration = Duration(seconds: 2);
|
||||
|
||||
/// Initialize URL scheme handler
|
||||
Future<void> initialize(BuildContext context) async {
|
||||
_context = context;
|
||||
|
||||
// Handle initial link when app is launched via URL scheme
|
||||
try {
|
||||
final initialUri = await _appLinks.getInitialLink();
|
||||
if (initialUri != null) {
|
||||
dPrint('Initial URI: $initialUri');
|
||||
_handleUri(initialUri);
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint('Failed to get initial URI: $e');
|
||||
}
|
||||
|
||||
// Handle links while app is running
|
||||
_linkSubscription = _appLinks.uriLinkStream.listen(
|
||||
(uri) {
|
||||
dPrint('Received URI: $uri');
|
||||
_handleUri(uri);
|
||||
},
|
||||
onError: (err) {
|
||||
dPrint('URI link stream error: $err');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Handle incoming URI with debouncing
|
||||
void _handleUri(Uri uri) {
|
||||
final uriString = uri.toString();
|
||||
final now = DateTime.now();
|
||||
|
||||
// Check if this is a duplicate URI within debounce duration
|
||||
if (_lastHandledUri == uriString && _lastHandledTime != null) {
|
||||
final timeSinceLastHandle = now.difference(_lastHandledTime!);
|
||||
if (timeSinceLastHandle < _debounceDuration) {
|
||||
dPrint('Debounced duplicate URI: $uriString (${timeSinceLastHandle.inMilliseconds}ms since last)');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update last handled URI and time
|
||||
_lastHandledUri = uriString;
|
||||
_lastHandledTime = now;
|
||||
|
||||
dPrint('Handling URI: $uri');
|
||||
|
||||
// Check if it's an auth request
|
||||
// Check if it's an auth request
|
||||
// Expected format: sctoolbox://auth?callbackUrl=https://example.com&state=...&nonce=...
|
||||
// Note: old format with domain in path (sctoolbox://auth/domain?...) is also supported but domain is ignored
|
||||
if (uri.scheme == 'sctoolbox' && uri.host == 'auth') {
|
||||
final callbackUrl = uri.queryParameters['callbackUrl'];
|
||||
final state = uri.queryParameters['state'];
|
||||
final nonce = uri.queryParameters['nonce'];
|
||||
|
||||
if (callbackUrl == null || callbackUrl.isEmpty) {
|
||||
dPrint('Invalid auth URI: missing callbackUrl parameter');
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == null || state.isEmpty) {
|
||||
dPrint('Invalid auth URI: missing state parameter');
|
||||
return;
|
||||
}
|
||||
|
||||
dPrint('Auth request - callbackUrl: $callbackUrl, state: $state');
|
||||
_showAuthDialog(callbackUrl, state, nonce);
|
||||
}
|
||||
}
|
||||
|
||||
/// Show auth dialog
|
||||
void _showAuthDialog(String callbackUrl, String state, String? nonce) {
|
||||
if (_context == null || !_context!.mounted) {
|
||||
dPrint('Cannot show auth dialog: context not available');
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: _context!,
|
||||
builder: (context) => AuthPage(callbackUrl: callbackUrl, stateParameter: state, nonce: nonce),
|
||||
);
|
||||
}
|
||||
|
||||
/// Dispose the handler
|
||||
void dispose() {
|
||||
_linkSubscription?.cancel();
|
||||
_linkSubscription = null;
|
||||
_context = null;
|
||||
_lastHandledUri = null;
|
||||
_lastHandledTime = null;
|
||||
}
|
||||
|
||||
/// Update context (useful when switching screens)
|
||||
void updateContext(BuildContext context) {
|
||||
_context = context;
|
||||
}
|
||||
}
|
||||
@ -1,28 +1,33 @@
|
||||
/// name : "Data.p4k"
|
||||
/// update_at : "2024-02-24 18:00"
|
||||
/// url : "https://p4k.42kit.com/3.22.1-LIVE.9072370/Data.p4k.torrent"
|
||||
/// update_at : "2024-09-03 15:00"
|
||||
/// url : "https://p4k.42kit.com/3.24.1-LIVE.9234446/Data.p4k.torrent"
|
||||
/// info : "3.24.1-LIVE.9234446"
|
||||
|
||||
class AppTorrentData {
|
||||
AppTorrentData({
|
||||
this.name,
|
||||
this.updateAt,
|
||||
this.url,});
|
||||
this.url,
|
||||
this.info,
|
||||
});
|
||||
|
||||
AppTorrentData.fromJson(dynamic json) {
|
||||
name = json['name'];
|
||||
updateAt = json['update_at'];
|
||||
url = json['url'];
|
||||
info = json['info'];
|
||||
}
|
||||
String? name;
|
||||
String? updateAt;
|
||||
String? url;
|
||||
String? info;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['name'] = name;
|
||||
map['update_at'] = updateAt;
|
||||
map['url'] = url;
|
||||
map['info'] = info;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,46 +2,25 @@
|
||||
/// size : 524288
|
||||
/// compressedSize : 169812
|
||||
/// isDirectory : false
|
||||
/// isFile : true
|
||||
/// isEncrypted : false
|
||||
/// isUnicodeText : false
|
||||
/// dateTime : "2019-12-16T15:11:18"
|
||||
/// version : 45
|
||||
|
||||
class AppUnp4kP4kItemData {
|
||||
AppUnp4kP4kItemData({
|
||||
this.name,
|
||||
this.size,
|
||||
this.compressedSize,
|
||||
this.isDirectory,
|
||||
this.isFile,
|
||||
this.isEncrypted,
|
||||
this.isUnicodeText,
|
||||
this.dateTime,
|
||||
this.version,
|
||||
});
|
||||
AppUnp4kP4kItemData({this.name, this.size, this.compressedSize, this.isDirectory, this.dateModified});
|
||||
|
||||
AppUnp4kP4kItemData.fromJson(dynamic json) {
|
||||
name = json['name'];
|
||||
size = json['size'];
|
||||
compressedSize = json['compressedSize'];
|
||||
isDirectory = json['isDirectory'];
|
||||
isFile = json['isFile'];
|
||||
isEncrypted = json['isEncrypted'];
|
||||
isUnicodeText = json['isUnicodeText'];
|
||||
dateTime = json['dateTime'];
|
||||
version = json['version'];
|
||||
dateModified = json['dateModified'];
|
||||
}
|
||||
|
||||
String? name;
|
||||
num? size;
|
||||
num? compressedSize;
|
||||
bool? isDirectory;
|
||||
bool? isFile;
|
||||
bool? isEncrypted;
|
||||
bool? isUnicodeText;
|
||||
String? dateTime;
|
||||
num? version;
|
||||
|
||||
/// 文件修改时间(毫秒时间戳)
|
||||
int? dateModified;
|
||||
List<AppUnp4kP4kItemData> children = [];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -50,11 +29,7 @@ class AppUnp4kP4kItemData {
|
||||
map['size'] = size;
|
||||
map['compressedSize'] = compressedSize;
|
||||
map['isDirectory'] = isDirectory;
|
||||
map['isFile'] = isFile;
|
||||
map['isEncrypted'] = isEncrypted;
|
||||
map['isUnicodeText'] = isUnicodeText;
|
||||
map['dateTime'] = dateTime;
|
||||
map['version'] = version;
|
||||
map['dateModified'] = dateModified;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@ class AppVersionData {
|
||||
this.mSEMinVersionCode,
|
||||
this.p4kDownloadUrl,
|
||||
this.activityColors,
|
||||
this.nav42KitUrl,
|
||||
this.gameChannels,
|
||||
this.webMirrors,
|
||||
});
|
||||
|
||||
AppVersionData.fromJson(dynamic json) {
|
||||
@ -27,10 +30,10 @@ class AppVersionData {
|
||||
mSELastVersionCode = json['MSE_lastVersionCode'];
|
||||
mSEMinVersionCode = json['MSE_minVersionCode'];
|
||||
p4kDownloadUrl = json['p4kDownloadUrl'];
|
||||
activityColors = json['activityColors'] != null
|
||||
? ActivityColors.fromJson(json['activityColors'])
|
||||
: null;
|
||||
activityColors = json['activityColors'] != null ? ActivityColors.fromJson(json['activityColors']) : null;
|
||||
gameChannels = List.from(json["game_channels"]).cast<String>();
|
||||
webMirrors = json["web_mirrors"];
|
||||
nav42KitUrl = json['nav_42kit_url'];
|
||||
}
|
||||
|
||||
String? lastVersion;
|
||||
@ -41,7 +44,9 @@ class AppVersionData {
|
||||
num? mSEMinVersionCode;
|
||||
String? p4kDownloadUrl;
|
||||
ActivityColors? activityColors;
|
||||
List<String>? gameChannels;
|
||||
Map? webMirrors;
|
||||
String? nav42KitUrl;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
@ -56,6 +61,8 @@ class AppVersionData {
|
||||
map['activityColors'] = activityColors?.toJson();
|
||||
}
|
||||
map["web_mirrors"] = webMirrors;
|
||||
map["game_channels"] = gameChannels;
|
||||
map["nav_42kit_url"] = nav42KitUrl;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
54
lib/data/citizen_news_data.dart
Normal file
54
lib/data/citizen_news_data.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'citizen_news_data.freezed.dart';
|
||||
|
||||
part 'citizen_news_data.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class CitizenNewsData with _$CitizenNewsData {
|
||||
const factory CitizenNewsData({
|
||||
@Default(<CitizenNewsVideosItemData>[]) @JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos,
|
||||
@Default(<CitizenNewsArticlesItemData>[]) @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles,
|
||||
}) = _CitizenNewsData;
|
||||
|
||||
const CitizenNewsData._();
|
||||
|
||||
factory CitizenNewsData.fromJson(Map<String, Object?> json) => _$CitizenNewsDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class CitizenNewsVideosItemData with _$CitizenNewsVideosItemData {
|
||||
const factory CitizenNewsVideosItemData({
|
||||
@Default('') @JsonKey(name: 'title') String title,
|
||||
@Default('') @JsonKey(name: 'author') String author,
|
||||
@Default('') @JsonKey(name: 'description') String description,
|
||||
@Default('') @JsonKey(name: 'link') String link,
|
||||
@Default('') @JsonKey(name: 'pubDate') String pubDate,
|
||||
@Default('') @JsonKey(name: 'postId') String postId,
|
||||
@Default(<String>[]) @JsonKey(name: 'detailedDescription') List<String> detailedDescription,
|
||||
@Default('') @JsonKey(name: 'tag') String tag,
|
||||
}) = _CitizenNewsVideosItemData;
|
||||
|
||||
const CitizenNewsVideosItemData._();
|
||||
|
||||
factory CitizenNewsVideosItemData.fromJson(Map<String, Object?> json) => _$CitizenNewsVideosItemDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class CitizenNewsArticlesItemData with _$CitizenNewsArticlesItemData {
|
||||
const factory CitizenNewsArticlesItemData({
|
||||
@Default('') @JsonKey(name: 'title') String title,
|
||||
@Default('') @JsonKey(name: 'author') String author,
|
||||
@Default('') @JsonKey(name: 'description') String description,
|
||||
@Default('') @JsonKey(name: 'link') String link,
|
||||
@Default('') @JsonKey(name: 'pubDate') String pubDate,
|
||||
@Default('') @JsonKey(name: 'postId') String postId,
|
||||
@Default(<String>[]) @JsonKey(name: 'detailedDescription') List<String> detailedDescription,
|
||||
@Default('') @JsonKey(name: 'tag') String tag,
|
||||
}) = _CitizenNewsArticlesItemData;
|
||||
|
||||
const CitizenNewsArticlesItemData._();
|
||||
|
||||
factory CitizenNewsArticlesItemData.fromJson(Map<String, Object?> json) =>
|
||||
_$CitizenNewsArticlesItemDataFromJson(json);
|
||||
}
|
||||
854
lib/data/citizen_news_data.freezed.dart
Normal file
854
lib/data/citizen_news_data.freezed.dart
Normal file
@ -0,0 +1,854 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'citizen_news_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CitizenNewsData {
|
||||
|
||||
@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> get videos;@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> get articles;
|
||||
/// Create a copy of CitizenNewsData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CitizenNewsDataCopyWith<CitizenNewsData> get copyWith => _$CitizenNewsDataCopyWithImpl<CitizenNewsData>(this as CitizenNewsData, _$identity);
|
||||
|
||||
/// Serializes this CitizenNewsData to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CitizenNewsData&&const DeepCollectionEquality().equals(other.videos, videos)&&const DeepCollectionEquality().equals(other.articles, articles));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(videos),const DeepCollectionEquality().hash(articles));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CitizenNewsData(videos: $videos, articles: $articles)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CitizenNewsDataCopyWith<$Res> {
|
||||
factory $CitizenNewsDataCopyWith(CitizenNewsData value, $Res Function(CitizenNewsData) _then) = _$CitizenNewsDataCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos,@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CitizenNewsDataCopyWithImpl<$Res>
|
||||
implements $CitizenNewsDataCopyWith<$Res> {
|
||||
_$CitizenNewsDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CitizenNewsData _self;
|
||||
final $Res Function(CitizenNewsData) _then;
|
||||
|
||||
/// Create a copy of CitizenNewsData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? videos = null,Object? articles = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
videos: null == videos ? _self.videos : videos // ignore: cast_nullable_to_non_nullable
|
||||
as List<CitizenNewsVideosItemData>,articles: null == articles ? _self.articles : articles // ignore: cast_nullable_to_non_nullable
|
||||
as List<CitizenNewsArticlesItemData>,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [CitizenNewsData].
|
||||
extension CitizenNewsDataPatterns on CitizenNewsData {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CitizenNewsData value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CitizenNewsData value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsData():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CitizenNewsData value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos, @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsData() when $default != null:
|
||||
return $default(_that.videos,_that.articles);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos, @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsData():
|
||||
return $default(_that.videos,_that.articles);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos, @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsData() when $default != null:
|
||||
return $default(_that.videos,_that.articles);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _CitizenNewsData extends CitizenNewsData {
|
||||
const _CitizenNewsData({@JsonKey(name: 'videos') final List<CitizenNewsVideosItemData> videos = const <CitizenNewsVideosItemData>[], @JsonKey(name: 'articles') final List<CitizenNewsArticlesItemData> articles = const <CitizenNewsArticlesItemData>[]}): _videos = videos,_articles = articles,super._();
|
||||
factory _CitizenNewsData.fromJson(Map<String, dynamic> json) => _$CitizenNewsDataFromJson(json);
|
||||
|
||||
final List<CitizenNewsVideosItemData> _videos;
|
||||
@override@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> get videos {
|
||||
if (_videos is EqualUnmodifiableListView) return _videos;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_videos);
|
||||
}
|
||||
|
||||
final List<CitizenNewsArticlesItemData> _articles;
|
||||
@override@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> get articles {
|
||||
if (_articles is EqualUnmodifiableListView) return _articles;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_articles);
|
||||
}
|
||||
|
||||
|
||||
/// Create a copy of CitizenNewsData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CitizenNewsDataCopyWith<_CitizenNewsData> get copyWith => __$CitizenNewsDataCopyWithImpl<_CitizenNewsData>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$CitizenNewsDataToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CitizenNewsData&&const DeepCollectionEquality().equals(other._videos, _videos)&&const DeepCollectionEquality().equals(other._articles, _articles));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_videos),const DeepCollectionEquality().hash(_articles));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CitizenNewsData(videos: $videos, articles: $articles)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CitizenNewsDataCopyWith<$Res> implements $CitizenNewsDataCopyWith<$Res> {
|
||||
factory _$CitizenNewsDataCopyWith(_CitizenNewsData value, $Res Function(_CitizenNewsData) _then) = __$CitizenNewsDataCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos,@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CitizenNewsDataCopyWithImpl<$Res>
|
||||
implements _$CitizenNewsDataCopyWith<$Res> {
|
||||
__$CitizenNewsDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CitizenNewsData _self;
|
||||
final $Res Function(_CitizenNewsData) _then;
|
||||
|
||||
/// Create a copy of CitizenNewsData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? videos = null,Object? articles = null,}) {
|
||||
return _then(_CitizenNewsData(
|
||||
videos: null == videos ? _self._videos : videos // ignore: cast_nullable_to_non_nullable
|
||||
as List<CitizenNewsVideosItemData>,articles: null == articles ? _self._articles : articles // ignore: cast_nullable_to_non_nullable
|
||||
as List<CitizenNewsArticlesItemData>,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CitizenNewsVideosItemData {
|
||||
|
||||
@JsonKey(name: 'title') String get title;@JsonKey(name: 'author') String get author;@JsonKey(name: 'description') String get description;@JsonKey(name: 'link') String get link;@JsonKey(name: 'pubDate') String get pubDate;@JsonKey(name: 'postId') String get postId;@JsonKey(name: 'detailedDescription') List<String> get detailedDescription;@JsonKey(name: 'tag') String get tag;
|
||||
/// Create a copy of CitizenNewsVideosItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CitizenNewsVideosItemDataCopyWith<CitizenNewsVideosItemData> get copyWith => _$CitizenNewsVideosItemDataCopyWithImpl<CitizenNewsVideosItemData>(this as CitizenNewsVideosItemData, _$identity);
|
||||
|
||||
/// Serializes this CitizenNewsVideosItemData to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CitizenNewsVideosItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other.detailedDescription, detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(detailedDescription),tag);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CitizenNewsVideosItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CitizenNewsVideosItemDataCopyWith<$Res> {
|
||||
factory $CitizenNewsVideosItemDataCopyWith(CitizenNewsVideosItemData value, $Res Function(CitizenNewsVideosItemData) _then) = _$CitizenNewsVideosItemDataCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CitizenNewsVideosItemDataCopyWithImpl<$Res>
|
||||
implements $CitizenNewsVideosItemDataCopyWith<$Res> {
|
||||
_$CitizenNewsVideosItemDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CitizenNewsVideosItemData _self;
|
||||
final $Res Function(CitizenNewsVideosItemData) _then;
|
||||
|
||||
/// Create a copy of CitizenNewsVideosItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
|
||||
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
|
||||
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
|
||||
as String,detailedDescription: null == detailedDescription ? _self.detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [CitizenNewsVideosItemData].
|
||||
extension CitizenNewsVideosItemDataPatterns on CitizenNewsVideosItemData {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CitizenNewsVideosItemData value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsVideosItemData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CitizenNewsVideosItemData value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsVideosItemData():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CitizenNewsVideosItemData value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsVideosItemData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsVideosItemData() when $default != null:
|
||||
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsVideosItemData():
|
||||
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsVideosItemData() when $default != null:
|
||||
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _CitizenNewsVideosItemData extends CitizenNewsVideosItemData {
|
||||
const _CitizenNewsVideosItemData({@JsonKey(name: 'title') this.title = '', @JsonKey(name: 'author') this.author = '', @JsonKey(name: 'description') this.description = '', @JsonKey(name: 'link') this.link = '', @JsonKey(name: 'pubDate') this.pubDate = '', @JsonKey(name: 'postId') this.postId = '', @JsonKey(name: 'detailedDescription') final List<String> detailedDescription = const <String>[], @JsonKey(name: 'tag') this.tag = ''}): _detailedDescription = detailedDescription,super._();
|
||||
factory _CitizenNewsVideosItemData.fromJson(Map<String, dynamic> json) => _$CitizenNewsVideosItemDataFromJson(json);
|
||||
|
||||
@override@JsonKey(name: 'title') final String title;
|
||||
@override@JsonKey(name: 'author') final String author;
|
||||
@override@JsonKey(name: 'description') final String description;
|
||||
@override@JsonKey(name: 'link') final String link;
|
||||
@override@JsonKey(name: 'pubDate') final String pubDate;
|
||||
@override@JsonKey(name: 'postId') final String postId;
|
||||
final List<String> _detailedDescription;
|
||||
@override@JsonKey(name: 'detailedDescription') List<String> get detailedDescription {
|
||||
if (_detailedDescription is EqualUnmodifiableListView) return _detailedDescription;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_detailedDescription);
|
||||
}
|
||||
|
||||
@override@JsonKey(name: 'tag') final String tag;
|
||||
|
||||
/// Create a copy of CitizenNewsVideosItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CitizenNewsVideosItemDataCopyWith<_CitizenNewsVideosItemData> get copyWith => __$CitizenNewsVideosItemDataCopyWithImpl<_CitizenNewsVideosItemData>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$CitizenNewsVideosItemDataToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CitizenNewsVideosItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other._detailedDescription, _detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(_detailedDescription),tag);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CitizenNewsVideosItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CitizenNewsVideosItemDataCopyWith<$Res> implements $CitizenNewsVideosItemDataCopyWith<$Res> {
|
||||
factory _$CitizenNewsVideosItemDataCopyWith(_CitizenNewsVideosItemData value, $Res Function(_CitizenNewsVideosItemData) _then) = __$CitizenNewsVideosItemDataCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CitizenNewsVideosItemDataCopyWithImpl<$Res>
|
||||
implements _$CitizenNewsVideosItemDataCopyWith<$Res> {
|
||||
__$CitizenNewsVideosItemDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CitizenNewsVideosItemData _self;
|
||||
final $Res Function(_CitizenNewsVideosItemData) _then;
|
||||
|
||||
/// Create a copy of CitizenNewsVideosItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
|
||||
return _then(_CitizenNewsVideosItemData(
|
||||
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
|
||||
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
|
||||
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
|
||||
as String,detailedDescription: null == detailedDescription ? _self._detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CitizenNewsArticlesItemData {
|
||||
|
||||
@JsonKey(name: 'title') String get title;@JsonKey(name: 'author') String get author;@JsonKey(name: 'description') String get description;@JsonKey(name: 'link') String get link;@JsonKey(name: 'pubDate') String get pubDate;@JsonKey(name: 'postId') String get postId;@JsonKey(name: 'detailedDescription') List<String> get detailedDescription;@JsonKey(name: 'tag') String get tag;
|
||||
/// Create a copy of CitizenNewsArticlesItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CitizenNewsArticlesItemDataCopyWith<CitizenNewsArticlesItemData> get copyWith => _$CitizenNewsArticlesItemDataCopyWithImpl<CitizenNewsArticlesItemData>(this as CitizenNewsArticlesItemData, _$identity);
|
||||
|
||||
/// Serializes this CitizenNewsArticlesItemData to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CitizenNewsArticlesItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other.detailedDescription, detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(detailedDescription),tag);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CitizenNewsArticlesItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CitizenNewsArticlesItemDataCopyWith<$Res> {
|
||||
factory $CitizenNewsArticlesItemDataCopyWith(CitizenNewsArticlesItemData value, $Res Function(CitizenNewsArticlesItemData) _then) = _$CitizenNewsArticlesItemDataCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CitizenNewsArticlesItemDataCopyWithImpl<$Res>
|
||||
implements $CitizenNewsArticlesItemDataCopyWith<$Res> {
|
||||
_$CitizenNewsArticlesItemDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CitizenNewsArticlesItemData _self;
|
||||
final $Res Function(CitizenNewsArticlesItemData) _then;
|
||||
|
||||
/// Create a copy of CitizenNewsArticlesItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
|
||||
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
|
||||
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
|
||||
as String,detailedDescription: null == detailedDescription ? _self.detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [CitizenNewsArticlesItemData].
|
||||
extension CitizenNewsArticlesItemDataPatterns on CitizenNewsArticlesItemData {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CitizenNewsArticlesItemData value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsArticlesItemData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CitizenNewsArticlesItemData value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsArticlesItemData():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CitizenNewsArticlesItemData value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsArticlesItemData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsArticlesItemData() when $default != null:
|
||||
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsArticlesItemData():
|
||||
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CitizenNewsArticlesItemData() when $default != null:
|
||||
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _CitizenNewsArticlesItemData extends CitizenNewsArticlesItemData {
|
||||
const _CitizenNewsArticlesItemData({@JsonKey(name: 'title') this.title = '', @JsonKey(name: 'author') this.author = '', @JsonKey(name: 'description') this.description = '', @JsonKey(name: 'link') this.link = '', @JsonKey(name: 'pubDate') this.pubDate = '', @JsonKey(name: 'postId') this.postId = '', @JsonKey(name: 'detailedDescription') final List<String> detailedDescription = const <String>[], @JsonKey(name: 'tag') this.tag = ''}): _detailedDescription = detailedDescription,super._();
|
||||
factory _CitizenNewsArticlesItemData.fromJson(Map<String, dynamic> json) => _$CitizenNewsArticlesItemDataFromJson(json);
|
||||
|
||||
@override@JsonKey(name: 'title') final String title;
|
||||
@override@JsonKey(name: 'author') final String author;
|
||||
@override@JsonKey(name: 'description') final String description;
|
||||
@override@JsonKey(name: 'link') final String link;
|
||||
@override@JsonKey(name: 'pubDate') final String pubDate;
|
||||
@override@JsonKey(name: 'postId') final String postId;
|
||||
final List<String> _detailedDescription;
|
||||
@override@JsonKey(name: 'detailedDescription') List<String> get detailedDescription {
|
||||
if (_detailedDescription is EqualUnmodifiableListView) return _detailedDescription;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_detailedDescription);
|
||||
}
|
||||
|
||||
@override@JsonKey(name: 'tag') final String tag;
|
||||
|
||||
/// Create a copy of CitizenNewsArticlesItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CitizenNewsArticlesItemDataCopyWith<_CitizenNewsArticlesItemData> get copyWith => __$CitizenNewsArticlesItemDataCopyWithImpl<_CitizenNewsArticlesItemData>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$CitizenNewsArticlesItemDataToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CitizenNewsArticlesItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other._detailedDescription, _detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(_detailedDescription),tag);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CitizenNewsArticlesItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CitizenNewsArticlesItemDataCopyWith<$Res> implements $CitizenNewsArticlesItemDataCopyWith<$Res> {
|
||||
factory _$CitizenNewsArticlesItemDataCopyWith(_CitizenNewsArticlesItemData value, $Res Function(_CitizenNewsArticlesItemData) _then) = __$CitizenNewsArticlesItemDataCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CitizenNewsArticlesItemDataCopyWithImpl<$Res>
|
||||
implements _$CitizenNewsArticlesItemDataCopyWith<$Res> {
|
||||
__$CitizenNewsArticlesItemDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CitizenNewsArticlesItemData _self;
|
||||
final $Res Function(_CitizenNewsArticlesItemData) _then;
|
||||
|
||||
/// Create a copy of CitizenNewsArticlesItemData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
|
||||
return _then(_CitizenNewsArticlesItemData(
|
||||
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
|
||||
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
|
||||
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
|
||||
as String,detailedDescription: null == detailedDescription ? _self._detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
91
lib/data/citizen_news_data.g.dart
Normal file
91
lib/data/citizen_news_data.g.dart
Normal file
@ -0,0 +1,91 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'citizen_news_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_CitizenNewsData _$CitizenNewsDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _CitizenNewsData(
|
||||
videos:
|
||||
(json['videos'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) =>
|
||||
CitizenNewsVideosItemData.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList() ??
|
||||
const <CitizenNewsVideosItemData>[],
|
||||
articles:
|
||||
(json['articles'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) =>
|
||||
CitizenNewsArticlesItemData.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList() ??
|
||||
const <CitizenNewsArticlesItemData>[],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CitizenNewsDataToJson(_CitizenNewsData instance) =>
|
||||
<String, dynamic>{'videos': instance.videos, 'articles': instance.articles};
|
||||
|
||||
_CitizenNewsVideosItemData _$CitizenNewsVideosItemDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _CitizenNewsVideosItemData(
|
||||
title: json['title'] as String? ?? '',
|
||||
author: json['author'] as String? ?? '',
|
||||
description: json['description'] as String? ?? '',
|
||||
link: json['link'] as String? ?? '',
|
||||
pubDate: json['pubDate'] as String? ?? '',
|
||||
postId: json['postId'] as String? ?? '',
|
||||
detailedDescription:
|
||||
(json['detailedDescription'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const <String>[],
|
||||
tag: json['tag'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CitizenNewsVideosItemDataToJson(
|
||||
_CitizenNewsVideosItemData instance,
|
||||
) => <String, dynamic>{
|
||||
'title': instance.title,
|
||||
'author': instance.author,
|
||||
'description': instance.description,
|
||||
'link': instance.link,
|
||||
'pubDate': instance.pubDate,
|
||||
'postId': instance.postId,
|
||||
'detailedDescription': instance.detailedDescription,
|
||||
'tag': instance.tag,
|
||||
};
|
||||
|
||||
_CitizenNewsArticlesItemData _$CitizenNewsArticlesItemDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _CitizenNewsArticlesItemData(
|
||||
title: json['title'] as String? ?? '',
|
||||
author: json['author'] as String? ?? '',
|
||||
description: json['description'] as String? ?? '',
|
||||
link: json['link'] as String? ?? '',
|
||||
pubDate: json['pubDate'] as String? ?? '',
|
||||
postId: json['postId'] as String? ?? '',
|
||||
detailedDescription:
|
||||
(json['detailedDescription'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const <String>[],
|
||||
tag: json['tag'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CitizenNewsArticlesItemDataToJson(
|
||||
_CitizenNewsArticlesItemData instance,
|
||||
) => <String, dynamic>{
|
||||
'title': instance.title,
|
||||
'author': instance.author,
|
||||
'description': instance.description,
|
||||
'link': instance.link,
|
||||
'pubDate': instance.pubDate,
|
||||
'postId': instance.postId,
|
||||
'detailedDescription': instance.detailedDescription,
|
||||
'tag': instance.tag,
|
||||
};
|
||||
24
lib/data/dcb_data.dart
Normal file
24
lib/data/dcb_data.dart
Normal file
@ -0,0 +1,24 @@
|
||||
/// DCB 记录项数据
|
||||
class DcbRecordData {
|
||||
final String path;
|
||||
final int index;
|
||||
|
||||
const DcbRecordData({required this.path, required this.index});
|
||||
}
|
||||
|
||||
/// DCB 搜索匹配数据
|
||||
class DcbSearchMatchData {
|
||||
final int lineNumber;
|
||||
final String lineContent;
|
||||
|
||||
const DcbSearchMatchData({required this.lineNumber, required this.lineContent});
|
||||
}
|
||||
|
||||
/// DCB 搜索结果数据
|
||||
class DcbSearchResultData {
|
||||
final String path;
|
||||
final int index;
|
||||
final List<DcbSearchMatchData> matches;
|
||||
|
||||
const DcbSearchResultData({required this.path, required this.index, required this.matches});
|
||||
}
|
||||
108
lib/data/doh_client_response_data.dart
Normal file
108
lib/data/doh_client_response_data.dart
Normal file
@ -0,0 +1,108 @@
|
||||
class DohClientResponseData {
|
||||
DohClientResponseData({
|
||||
this.status,
|
||||
this.tc,
|
||||
this.rd,
|
||||
this.ra,
|
||||
this.ad,
|
||||
this.cd,
|
||||
this.question,
|
||||
this.answer,
|
||||
});
|
||||
|
||||
DohClientResponseData.fromJson(dynamic json) {
|
||||
status = json['Status'];
|
||||
tc = json['TC'];
|
||||
rd = json['RD'];
|
||||
ra = json['RA'];
|
||||
ad = json['AD'];
|
||||
cd = json['CD'];
|
||||
question = json['Question'] != null
|
||||
? DohClientResponseQuestionData.fromJson(json['Question'])
|
||||
: null;
|
||||
if (json['Answer'] != null) {
|
||||
answer = [];
|
||||
json['Answer'].forEach((v) {
|
||||
answer?.add(DohClientResponseAnswerData.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
num? status;
|
||||
bool? tc;
|
||||
bool? rd;
|
||||
bool? ra;
|
||||
bool? ad;
|
||||
bool? cd;
|
||||
DohClientResponseQuestionData? question;
|
||||
List<DohClientResponseAnswerData>? answer;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['Status'] = status;
|
||||
map['TC'] = tc;
|
||||
map['RD'] = rd;
|
||||
map['RA'] = ra;
|
||||
map['AD'] = ad;
|
||||
map['CD'] = cd;
|
||||
if (question != null) {
|
||||
map['Question'] = question?.toJson();
|
||||
}
|
||||
if (answer != null) {
|
||||
map['Answer'] = answer?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class DohClientResponseAnswerData {
|
||||
DohClientResponseAnswerData({
|
||||
this.name,
|
||||
this.ttl,
|
||||
this.type,
|
||||
this.data,
|
||||
});
|
||||
|
||||
DohClientResponseAnswerData.fromJson(dynamic json) {
|
||||
name = json['name'];
|
||||
ttl = json['TTL'];
|
||||
type = json['type'];
|
||||
data = json['data'];
|
||||
}
|
||||
|
||||
String? name;
|
||||
num? ttl;
|
||||
num? type;
|
||||
String? data;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['name'] = name;
|
||||
map['TTL'] = ttl;
|
||||
map['type'] = type;
|
||||
map['data'] = data;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class DohClientResponseQuestionData {
|
||||
DohClientResponseQuestionData({
|
||||
this.name,
|
||||
this.type,
|
||||
});
|
||||
|
||||
DohClientResponseQuestionData.fromJson(dynamic json) {
|
||||
name = json['name'];
|
||||
type = json['type'];
|
||||
}
|
||||
|
||||
String? name;
|
||||
num? type;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['name'] = name;
|
||||
map['type'] = type;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ class GamePerformanceData {
|
||||
this.min,
|
||||
this.value,
|
||||
this.group,
|
||||
this.defaultValue,
|
||||
});
|
||||
|
||||
GamePerformanceData.fromJson(dynamic json) {
|
||||
@ -28,6 +29,8 @@ class GamePerformanceData {
|
||||
min = json['min'];
|
||||
value = json['value'];
|
||||
group = json['group'];
|
||||
// Store the initial value as default value
|
||||
defaultValue = json['value'];
|
||||
}
|
||||
String? key;
|
||||
String? name;
|
||||
@ -37,6 +40,7 @@ class GamePerformanceData {
|
||||
num? min;
|
||||
num? value;
|
||||
String? group;
|
||||
num? defaultValue;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
|
||||
52
lib/data/input_method_api_data.dart
Normal file
52
lib/data/input_method_api_data.dart
Normal file
@ -0,0 +1,52 @@
|
||||
class InputMethodApiData {
|
||||
InputMethodApiData({
|
||||
this.enable,
|
||||
this.languages,
|
||||
});
|
||||
|
||||
InputMethodApiData.fromJson(dynamic json) {
|
||||
enable = json['enable'];
|
||||
if (json['languages'] != null) {
|
||||
languages = <String, InputMethodApiLanguageData>{};
|
||||
json['languages'].forEach((String key, dynamic v) {
|
||||
languages![key] = InputMethodApiLanguageData.fromJson(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool? enable;
|
||||
Map<String, InputMethodApiLanguageData>? languages;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['enable'] = enable;
|
||||
if (languages != null) {
|
||||
map['languages'] = languages!.map<String, dynamic>((key, value) {
|
||||
return MapEntry(key, value.toJson());
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class InputMethodApiLanguageData {
|
||||
InputMethodApiLanguageData({
|
||||
this.file,
|
||||
this.version,
|
||||
});
|
||||
|
||||
InputMethodApiLanguageData.fromJson(dynamic json) {
|
||||
file = json['file'];
|
||||
version = json['version'];
|
||||
}
|
||||
|
||||
String? file;
|
||||
String? version;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['file'] = file;
|
||||
map['version'] = version;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
246
lib/data/nav_api_data.dart
Normal file
246
lib/data/nav_api_data.dart
Normal file
@ -0,0 +1,246 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'nav_api_data.freezed.dart';
|
||||
|
||||
part 'nav_api_data.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemData with _$NavApiDocsItemData {
|
||||
const factory NavApiDocsItemData({
|
||||
@Default('') @JsonKey(name: 'id') String id,
|
||||
@Default('') @JsonKey(name: 'name') String name,
|
||||
@Default('') @JsonKey(name: 'slug') String slug,
|
||||
@Default('') @JsonKey(name: 'abstract') String abstract_,
|
||||
@Default('') @JsonKey(name: 'description') String description,
|
||||
@Default(NavApiDocsItemImageData())
|
||||
@JsonKey(name: 'image')
|
||||
NavApiDocsItemImageData image,
|
||||
@Default('') @JsonKey(name: 'link') String link,
|
||||
@Default(false) @JsonKey(name: 'is_sponsored') bool isSponsored,
|
||||
@Default(<NavApiDocsItemTagsItemData>[])
|
||||
@JsonKey(name: 'tags')
|
||||
List<NavApiDocsItemTagsItemData> tags,
|
||||
@Default('') @JsonKey(name: 'updatedAt') String updatedAt,
|
||||
@Default('') @JsonKey(name: 'createdAt') String createdAt,
|
||||
}) = _NavApiDocsItemData;
|
||||
|
||||
const NavApiDocsItemData._();
|
||||
|
||||
factory NavApiDocsItemData.fromJson(Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageData with _$NavApiDocsItemImageData {
|
||||
const factory NavApiDocsItemImageData({
|
||||
@Default('') @JsonKey(name: 'id') String id,
|
||||
@Default(NavApiDocsItemImageCreatedByData())
|
||||
@JsonKey(name: 'createdBy')
|
||||
NavApiDocsItemImageCreatedByData createdBy,
|
||||
@Default('') @JsonKey(name: 'title') String title,
|
||||
@Default(false) @JsonKey(name: 'original') bool original,
|
||||
@Default('') @JsonKey(name: 'credit') String credit,
|
||||
@Default('') @JsonKey(name: 'source') String source,
|
||||
@Default('') @JsonKey(name: 'license') String license,
|
||||
@JsonKey(name: 'caption') dynamic caption,
|
||||
@Default('') @JsonKey(name: 'updatedAt') String updatedAt,
|
||||
@Default('') @JsonKey(name: 'createdAt') String createdAt,
|
||||
@Default('') @JsonKey(name: 'url') String url,
|
||||
@Default('') @JsonKey(name: 'filename') String filename,
|
||||
@Default('') @JsonKey(name: 'mimeType') String mimeType,
|
||||
@Default(0) @JsonKey(name: 'filesize') int filesize,
|
||||
@Default(0) @JsonKey(name: 'width') int width,
|
||||
@Default(0) @JsonKey(name: 'height') int height,
|
||||
@Default(NavApiDocsItemImageSizesData())
|
||||
@JsonKey(name: 'sizes')
|
||||
NavApiDocsItemImageSizesData sizes,
|
||||
}) = _NavApiDocsItemImageData;
|
||||
|
||||
const NavApiDocsItemImageData._();
|
||||
|
||||
factory NavApiDocsItemImageData.fromJson(Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageCreatedByData with _$NavApiDocsItemImageCreatedByData {
|
||||
const factory NavApiDocsItemImageCreatedByData({
|
||||
@Default('') @JsonKey(name: 'id') String id,
|
||||
@Default('') @JsonKey(name: 'sub') String sub,
|
||||
@Default('') @JsonKey(name: 'external_provider') String externalProvider,
|
||||
@Default('') @JsonKey(name: 'username') String username,
|
||||
@Default('') @JsonKey(name: 'name') String name,
|
||||
@Default(<String>[]) @JsonKey(name: 'roles') List<String> roles,
|
||||
@Default('') @JsonKey(name: 'avatar_url') String avatarUrl,
|
||||
@Default('') @JsonKey(name: 'updatedAt') String updatedAt,
|
||||
@Default('') @JsonKey(name: 'createdAt') String createdAt,
|
||||
@Default('') @JsonKey(name: 'email') String email,
|
||||
@Default(0) @JsonKey(name: 'loginAttempts') int loginAttempts,
|
||||
@Default('') @JsonKey(name: 'avatar') String avatar,
|
||||
}) = _NavApiDocsItemImageCreatedByData;
|
||||
|
||||
const NavApiDocsItemImageCreatedByData._();
|
||||
|
||||
factory NavApiDocsItemImageCreatedByData.fromJson(
|
||||
Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageCreatedByDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageSizesThumbnailData
|
||||
with _$NavApiDocsItemImageSizesThumbnailData {
|
||||
const factory NavApiDocsItemImageSizesThumbnailData({
|
||||
@Default('') @JsonKey(name: 'url') String url,
|
||||
@Default(0) @JsonKey(name: 'width') int width,
|
||||
@Default(0) @JsonKey(name: 'height') int height,
|
||||
@Default('') @JsonKey(name: 'mimeType') String mimeType,
|
||||
@Default(0) @JsonKey(name: 'filesize') int filesize,
|
||||
@Default('') @JsonKey(name: 'filename') String filename,
|
||||
}) = _NavApiDocsItemImageSizesThumbnailData;
|
||||
|
||||
const NavApiDocsItemImageSizesThumbnailData._();
|
||||
|
||||
factory NavApiDocsItemImageSizesThumbnailData.fromJson(
|
||||
Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageSizesThumbnailDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageSizesData with _$NavApiDocsItemImageSizesData {
|
||||
const factory NavApiDocsItemImageSizesData({
|
||||
@Default(NavApiDocsItemImageSizesThumbnailData())
|
||||
@JsonKey(name: 'thumbnail')
|
||||
NavApiDocsItemImageSizesThumbnailData thumbnail,
|
||||
@Default(NavApiDocsItemImageSizesPreloadData())
|
||||
@JsonKey(name: 'preload')
|
||||
NavApiDocsItemImageSizesPreloadData preload,
|
||||
@Default(NavApiDocsItemImageSizesCardData())
|
||||
@JsonKey(name: 'card')
|
||||
NavApiDocsItemImageSizesCardData card,
|
||||
@Default(NavApiDocsItemImageSizesTabletData())
|
||||
@JsonKey(name: 'tablet')
|
||||
NavApiDocsItemImageSizesTabletData tablet,
|
||||
@Default(NavApiDocsItemImageSizesAvatarData())
|
||||
@JsonKey(name: 'avatar')
|
||||
NavApiDocsItemImageSizesAvatarData avatar,
|
||||
}) = _NavApiDocsItemImageSizesData;
|
||||
|
||||
const NavApiDocsItemImageSizesData._();
|
||||
|
||||
factory NavApiDocsItemImageSizesData.fromJson(Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageSizesDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageSizesPreloadData
|
||||
with _$NavApiDocsItemImageSizesPreloadData {
|
||||
const factory NavApiDocsItemImageSizesPreloadData({
|
||||
@JsonKey(name: 'url') dynamic url,
|
||||
@JsonKey(name: 'width') dynamic width,
|
||||
@JsonKey(name: 'height') dynamic height,
|
||||
@JsonKey(name: 'mimeType') dynamic mimeType,
|
||||
@JsonKey(name: 'filesize') dynamic filesize,
|
||||
@JsonKey(name: 'filename') dynamic filename,
|
||||
}) = _NavApiDocsItemImageSizesPreloadData;
|
||||
|
||||
const NavApiDocsItemImageSizesPreloadData._();
|
||||
|
||||
factory NavApiDocsItemImageSizesPreloadData.fromJson(
|
||||
Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageSizesPreloadDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageSizesCardData with _$NavApiDocsItemImageSizesCardData {
|
||||
const factory NavApiDocsItemImageSizesCardData({
|
||||
@Default('') @JsonKey(name: 'url') String url,
|
||||
@Default(0) @JsonKey(name: 'width') int width,
|
||||
@Default(0) @JsonKey(name: 'height') int height,
|
||||
@Default('') @JsonKey(name: 'mimeType') String mimeType,
|
||||
@Default(0) @JsonKey(name: 'filesize') int filesize,
|
||||
@Default('') @JsonKey(name: 'filename') String filename,
|
||||
}) = _NavApiDocsItemImageSizesCardData;
|
||||
|
||||
const NavApiDocsItemImageSizesCardData._();
|
||||
|
||||
factory NavApiDocsItemImageSizesCardData.fromJson(
|
||||
Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageSizesCardDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageSizesTabletData
|
||||
with _$NavApiDocsItemImageSizesTabletData {
|
||||
const factory NavApiDocsItemImageSizesTabletData({
|
||||
@Default('') @JsonKey(name: 'url') String url,
|
||||
@Default(0) @JsonKey(name: 'width') int width,
|
||||
@Default(0) @JsonKey(name: 'height') int height,
|
||||
@Default('') @JsonKey(name: 'mimeType') String mimeType,
|
||||
@Default(0) @JsonKey(name: 'filesize') int filesize,
|
||||
@Default('') @JsonKey(name: 'filename') String filename,
|
||||
}) = _NavApiDocsItemImageSizesTabletData;
|
||||
|
||||
const NavApiDocsItemImageSizesTabletData._();
|
||||
|
||||
factory NavApiDocsItemImageSizesTabletData.fromJson(
|
||||
Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageSizesTabletDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemImageSizesAvatarData
|
||||
with _$NavApiDocsItemImageSizesAvatarData {
|
||||
const factory NavApiDocsItemImageSizesAvatarData({
|
||||
@Default('') @JsonKey(name: 'url') String url,
|
||||
@Default(0) @JsonKey(name: 'width') int width,
|
||||
@Default(0) @JsonKey(name: 'height') int height,
|
||||
@Default('') @JsonKey(name: 'mimeType') String mimeType,
|
||||
@Default(0) @JsonKey(name: 'filesize') int filesize,
|
||||
@Default('') @JsonKey(name: 'filename') String filename,
|
||||
}) = _NavApiDocsItemImageSizesAvatarData;
|
||||
|
||||
const NavApiDocsItemImageSizesAvatarData._();
|
||||
|
||||
factory NavApiDocsItemImageSizesAvatarData.fromJson(
|
||||
Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemImageSizesAvatarDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiDocsItemTagsItemData with _$NavApiDocsItemTagsItemData {
|
||||
const factory NavApiDocsItemTagsItemData({
|
||||
@Default('') @JsonKey(name: 'id') String id,
|
||||
@Default('') @JsonKey(name: 'name') String name,
|
||||
@Default('') @JsonKey(name: 'slug') String slug,
|
||||
@Default('') @JsonKey(name: 'updatedAt') String updatedAt,
|
||||
@Default('') @JsonKey(name: 'createdAt') String createdAt,
|
||||
}) = _NavApiDocsItemTagsItemData;
|
||||
|
||||
const NavApiDocsItemTagsItemData._();
|
||||
|
||||
factory NavApiDocsItemTagsItemData.fromJson(Map<String, Object?> json) =>
|
||||
_$NavApiDocsItemTagsItemDataFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class NavApiData with _$NavApiData {
|
||||
const factory NavApiData({
|
||||
@Default(<NavApiDocsItemData>[])
|
||||
@JsonKey(name: 'docs')
|
||||
List<NavApiDocsItemData> docs,
|
||||
@Default(false) @JsonKey(name: 'hasNextPage') bool hasNextPage,
|
||||
@Default(false) @JsonKey(name: 'hasPrevPage') bool hasPrevPage,
|
||||
@Default(0) @JsonKey(name: 'limit') int limit,
|
||||
@JsonKey(name: 'nextPage') dynamic nextPage,
|
||||
@Default(0) @JsonKey(name: 'page') int page,
|
||||
@Default(0) @JsonKey(name: 'pagingCounter') int pagingCounter,
|
||||
@JsonKey(name: 'prevPage') dynamic prevPage,
|
||||
@Default(0) @JsonKey(name: 'totalDocs') int totalDocs,
|
||||
@Default(0) @JsonKey(name: 'totalPages') int totalPages,
|
||||
}) = _NavApiData;
|
||||
|
||||
const NavApiData._();
|
||||
|
||||
factory NavApiData.fromJson(Map<String, Object?> json) =>
|
||||
_$NavApiDataFromJson(json);
|
||||
}
|
||||
3306
lib/data/nav_api_data.freezed.dart
Normal file
3306
lib/data/nav_api_data.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
336
lib/data/nav_api_data.g.dart
Normal file
336
lib/data/nav_api_data.g.dart
Normal file
@ -0,0 +1,336 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'nav_api_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_NavApiDocsItemData _$NavApiDocsItemDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _NavApiDocsItemData(
|
||||
id: json['id'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
slug: json['slug'] as String? ?? '',
|
||||
abstract_: json['abstract'] as String? ?? '',
|
||||
description: json['description'] as String? ?? '',
|
||||
image: json['image'] == null
|
||||
? const NavApiDocsItemImageData()
|
||||
: NavApiDocsItemImageData.fromJson(json['image'] as Map<String, dynamic>),
|
||||
link: json['link'] as String? ?? '',
|
||||
isSponsored: json['is_sponsored'] as bool? ?? false,
|
||||
tags:
|
||||
(json['tags'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) =>
|
||||
NavApiDocsItemTagsItemData.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList() ??
|
||||
const <NavApiDocsItemTagsItemData>[],
|
||||
updatedAt: json['updatedAt'] as String? ?? '',
|
||||
createdAt: json['createdAt'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemDataToJson(_NavApiDocsItemData instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'slug': instance.slug,
|
||||
'abstract': instance.abstract_,
|
||||
'description': instance.description,
|
||||
'image': instance.image,
|
||||
'link': instance.link,
|
||||
'is_sponsored': instance.isSponsored,
|
||||
'tags': instance.tags,
|
||||
'updatedAt': instance.updatedAt,
|
||||
'createdAt': instance.createdAt,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageData _$NavApiDocsItemImageDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _NavApiDocsItemImageData(
|
||||
id: json['id'] as String? ?? '',
|
||||
createdBy: json['createdBy'] == null
|
||||
? const NavApiDocsItemImageCreatedByData()
|
||||
: NavApiDocsItemImageCreatedByData.fromJson(
|
||||
json['createdBy'] as Map<String, dynamic>,
|
||||
),
|
||||
title: json['title'] as String? ?? '',
|
||||
original: json['original'] as bool? ?? false,
|
||||
credit: json['credit'] as String? ?? '',
|
||||
source: json['source'] as String? ?? '',
|
||||
license: json['license'] as String? ?? '',
|
||||
caption: json['caption'],
|
||||
updatedAt: json['updatedAt'] as String? ?? '',
|
||||
createdAt: json['createdAt'] as String? ?? '',
|
||||
url: json['url'] as String? ?? '',
|
||||
filename: json['filename'] as String? ?? '',
|
||||
mimeType: json['mimeType'] as String? ?? '',
|
||||
filesize: (json['filesize'] as num?)?.toInt() ?? 0,
|
||||
width: (json['width'] as num?)?.toInt() ?? 0,
|
||||
height: (json['height'] as num?)?.toInt() ?? 0,
|
||||
sizes: json['sizes'] == null
|
||||
? const NavApiDocsItemImageSizesData()
|
||||
: NavApiDocsItemImageSizesData.fromJson(
|
||||
json['sizes'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageDataToJson(
|
||||
_NavApiDocsItemImageData instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'createdBy': instance.createdBy,
|
||||
'title': instance.title,
|
||||
'original': instance.original,
|
||||
'credit': instance.credit,
|
||||
'source': instance.source,
|
||||
'license': instance.license,
|
||||
'caption': instance.caption,
|
||||
'updatedAt': instance.updatedAt,
|
||||
'createdAt': instance.createdAt,
|
||||
'url': instance.url,
|
||||
'filename': instance.filename,
|
||||
'mimeType': instance.mimeType,
|
||||
'filesize': instance.filesize,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'sizes': instance.sizes,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageCreatedByData _$NavApiDocsItemImageCreatedByDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _NavApiDocsItemImageCreatedByData(
|
||||
id: json['id'] as String? ?? '',
|
||||
sub: json['sub'] as String? ?? '',
|
||||
externalProvider: json['external_provider'] as String? ?? '',
|
||||
username: json['username'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
roles:
|
||||
(json['roles'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const <String>[],
|
||||
avatarUrl: json['avatar_url'] as String? ?? '',
|
||||
updatedAt: json['updatedAt'] as String? ?? '',
|
||||
createdAt: json['createdAt'] as String? ?? '',
|
||||
email: json['email'] as String? ?? '',
|
||||
loginAttempts: (json['loginAttempts'] as num?)?.toInt() ?? 0,
|
||||
avatar: json['avatar'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageCreatedByDataToJson(
|
||||
_NavApiDocsItemImageCreatedByData instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'sub': instance.sub,
|
||||
'external_provider': instance.externalProvider,
|
||||
'username': instance.username,
|
||||
'name': instance.name,
|
||||
'roles': instance.roles,
|
||||
'avatar_url': instance.avatarUrl,
|
||||
'updatedAt': instance.updatedAt,
|
||||
'createdAt': instance.createdAt,
|
||||
'email': instance.email,
|
||||
'loginAttempts': instance.loginAttempts,
|
||||
'avatar': instance.avatar,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageSizesThumbnailData
|
||||
_$NavApiDocsItemImageSizesThumbnailDataFromJson(Map<String, dynamic> json) =>
|
||||
_NavApiDocsItemImageSizesThumbnailData(
|
||||
url: json['url'] as String? ?? '',
|
||||
width: (json['width'] as num?)?.toInt() ?? 0,
|
||||
height: (json['height'] as num?)?.toInt() ?? 0,
|
||||
mimeType: json['mimeType'] as String? ?? '',
|
||||
filesize: (json['filesize'] as num?)?.toInt() ?? 0,
|
||||
filename: json['filename'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageSizesThumbnailDataToJson(
|
||||
_NavApiDocsItemImageSizesThumbnailData instance,
|
||||
) => <String, dynamic>{
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'mimeType': instance.mimeType,
|
||||
'filesize': instance.filesize,
|
||||
'filename': instance.filename,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageSizesData _$NavApiDocsItemImageSizesDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _NavApiDocsItemImageSizesData(
|
||||
thumbnail: json['thumbnail'] == null
|
||||
? const NavApiDocsItemImageSizesThumbnailData()
|
||||
: NavApiDocsItemImageSizesThumbnailData.fromJson(
|
||||
json['thumbnail'] as Map<String, dynamic>,
|
||||
),
|
||||
preload: json['preload'] == null
|
||||
? const NavApiDocsItemImageSizesPreloadData()
|
||||
: NavApiDocsItemImageSizesPreloadData.fromJson(
|
||||
json['preload'] as Map<String, dynamic>,
|
||||
),
|
||||
card: json['card'] == null
|
||||
? const NavApiDocsItemImageSizesCardData()
|
||||
: NavApiDocsItemImageSizesCardData.fromJson(
|
||||
json['card'] as Map<String, dynamic>,
|
||||
),
|
||||
tablet: json['tablet'] == null
|
||||
? const NavApiDocsItemImageSizesTabletData()
|
||||
: NavApiDocsItemImageSizesTabletData.fromJson(
|
||||
json['tablet'] as Map<String, dynamic>,
|
||||
),
|
||||
avatar: json['avatar'] == null
|
||||
? const NavApiDocsItemImageSizesAvatarData()
|
||||
: NavApiDocsItemImageSizesAvatarData.fromJson(
|
||||
json['avatar'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageSizesDataToJson(
|
||||
_NavApiDocsItemImageSizesData instance,
|
||||
) => <String, dynamic>{
|
||||
'thumbnail': instance.thumbnail,
|
||||
'preload': instance.preload,
|
||||
'card': instance.card,
|
||||
'tablet': instance.tablet,
|
||||
'avatar': instance.avatar,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageSizesPreloadData
|
||||
_$NavApiDocsItemImageSizesPreloadDataFromJson(Map<String, dynamic> json) =>
|
||||
_NavApiDocsItemImageSizesPreloadData(
|
||||
url: json['url'],
|
||||
width: json['width'],
|
||||
height: json['height'],
|
||||
mimeType: json['mimeType'],
|
||||
filesize: json['filesize'],
|
||||
filename: json['filename'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageSizesPreloadDataToJson(
|
||||
_NavApiDocsItemImageSizesPreloadData instance,
|
||||
) => <String, dynamic>{
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'mimeType': instance.mimeType,
|
||||
'filesize': instance.filesize,
|
||||
'filename': instance.filename,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageSizesCardData _$NavApiDocsItemImageSizesCardDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _NavApiDocsItemImageSizesCardData(
|
||||
url: json['url'] as String? ?? '',
|
||||
width: (json['width'] as num?)?.toInt() ?? 0,
|
||||
height: (json['height'] as num?)?.toInt() ?? 0,
|
||||
mimeType: json['mimeType'] as String? ?? '',
|
||||
filesize: (json['filesize'] as num?)?.toInt() ?? 0,
|
||||
filename: json['filename'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageSizesCardDataToJson(
|
||||
_NavApiDocsItemImageSizesCardData instance,
|
||||
) => <String, dynamic>{
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'mimeType': instance.mimeType,
|
||||
'filesize': instance.filesize,
|
||||
'filename': instance.filename,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageSizesTabletData
|
||||
_$NavApiDocsItemImageSizesTabletDataFromJson(Map<String, dynamic> json) =>
|
||||
_NavApiDocsItemImageSizesTabletData(
|
||||
url: json['url'] as String? ?? '',
|
||||
width: (json['width'] as num?)?.toInt() ?? 0,
|
||||
height: (json['height'] as num?)?.toInt() ?? 0,
|
||||
mimeType: json['mimeType'] as String? ?? '',
|
||||
filesize: (json['filesize'] as num?)?.toInt() ?? 0,
|
||||
filename: json['filename'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageSizesTabletDataToJson(
|
||||
_NavApiDocsItemImageSizesTabletData instance,
|
||||
) => <String, dynamic>{
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'mimeType': instance.mimeType,
|
||||
'filesize': instance.filesize,
|
||||
'filename': instance.filename,
|
||||
};
|
||||
|
||||
_NavApiDocsItemImageSizesAvatarData
|
||||
_$NavApiDocsItemImageSizesAvatarDataFromJson(Map<String, dynamic> json) =>
|
||||
_NavApiDocsItemImageSizesAvatarData(
|
||||
url: json['url'] as String? ?? '',
|
||||
width: (json['width'] as num?)?.toInt() ?? 0,
|
||||
height: (json['height'] as num?)?.toInt() ?? 0,
|
||||
mimeType: json['mimeType'] as String? ?? '',
|
||||
filesize: (json['filesize'] as num?)?.toInt() ?? 0,
|
||||
filename: json['filename'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemImageSizesAvatarDataToJson(
|
||||
_NavApiDocsItemImageSizesAvatarData instance,
|
||||
) => <String, dynamic>{
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'mimeType': instance.mimeType,
|
||||
'filesize': instance.filesize,
|
||||
'filename': instance.filename,
|
||||
};
|
||||
|
||||
_NavApiDocsItemTagsItemData _$NavApiDocsItemTagsItemDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _NavApiDocsItemTagsItemData(
|
||||
id: json['id'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
slug: json['slug'] as String? ?? '',
|
||||
updatedAt: json['updatedAt'] as String? ?? '',
|
||||
createdAt: json['createdAt'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDocsItemTagsItemDataToJson(
|
||||
_NavApiDocsItemTagsItemData instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'slug': instance.slug,
|
||||
'updatedAt': instance.updatedAt,
|
||||
'createdAt': instance.createdAt,
|
||||
};
|
||||
|
||||
_NavApiData _$NavApiDataFromJson(Map<String, dynamic> json) => _NavApiData(
|
||||
docs:
|
||||
(json['docs'] as List<dynamic>?)
|
||||
?.map((e) => NavApiDocsItemData.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const <NavApiDocsItemData>[],
|
||||
hasNextPage: json['hasNextPage'] as bool? ?? false,
|
||||
hasPrevPage: json['hasPrevPage'] as bool? ?? false,
|
||||
limit: (json['limit'] as num?)?.toInt() ?? 0,
|
||||
nextPage: json['nextPage'],
|
||||
page: (json['page'] as num?)?.toInt() ?? 0,
|
||||
pagingCounter: (json['pagingCounter'] as num?)?.toInt() ?? 0,
|
||||
prevPage: json['prevPage'],
|
||||
totalDocs: (json['totalDocs'] as num?)?.toInt() ?? 0,
|
||||
totalPages: (json['totalPages'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NavApiDataToJson(_NavApiData instance) =>
|
||||
<String, dynamic>{
|
||||
'docs': instance.docs,
|
||||
'hasNextPage': instance.hasNextPage,
|
||||
'hasPrevPage': instance.hasPrevPage,
|
||||
'limit': instance.limit,
|
||||
'nextPage': instance.nextPage,
|
||||
'page': instance.page,
|
||||
'pagingCounter': instance.pagingCounter,
|
||||
'prevPage': instance.prevPage,
|
||||
'totalDocs': instance.totalDocs,
|
||||
'totalPages': instance.totalPages,
|
||||
};
|
||||
@ -18,6 +18,7 @@ import 'package:intl/src/intl_helpers.dart';
|
||||
|
||||
import 'messages_en.dart' as messages_en;
|
||||
import 'messages_ja.dart' as messages_ja;
|
||||
import 'messages_ru.dart' as messages_ru;
|
||||
import 'messages_zh_CN.dart' as messages_zh_cn;
|
||||
import 'messages_zh_TW.dart' as messages_zh_tw;
|
||||
|
||||
@ -25,6 +26,7 @@ typedef Future<dynamic> LibraryLoader();
|
||||
Map<String, LibraryLoader> _deferredLibraries = {
|
||||
'en': () => new SynchronousFuture(null),
|
||||
'ja': () => new SynchronousFuture(null),
|
||||
'ru': () => new SynchronousFuture(null),
|
||||
'zh_CN': () => new SynchronousFuture(null),
|
||||
'zh_TW': () => new SynchronousFuture(null),
|
||||
};
|
||||
@ -35,6 +37,8 @@ MessageLookupByLibrary? _findExact(String localeName) {
|
||||
return messages_en.messages;
|
||||
case 'ja':
|
||||
return messages_ja.messages;
|
||||
case 'ru':
|
||||
return messages_ru.messages;
|
||||
case 'zh_CN':
|
||||
return messages_zh_cn.messages;
|
||||
case 'zh_TW':
|
||||
@ -47,8 +51,10 @@ MessageLookupByLibrary? _findExact(String localeName) {
|
||||
/// User programs should call this before using [localeName] for messages.
|
||||
Future<bool> initializeMessages(String localeName) {
|
||||
var availableLocale = Intl.verifiedLocale(
|
||||
localeName, (locale) => _deferredLibraries[locale] != null,
|
||||
onFailure: (_) => null);
|
||||
localeName,
|
||||
(locale) => _deferredLibraries[locale] != null,
|
||||
onFailure: (_) => null,
|
||||
);
|
||||
if (availableLocale == null) {
|
||||
return new SynchronousFuture(false);
|
||||
}
|
||||
@ -68,8 +74,11 @@ bool _messagesExistFor(String locale) {
|
||||
}
|
||||
|
||||
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
|
||||
var actualLocale =
|
||||
Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
|
||||
var actualLocale = Intl.verifiedLocale(
|
||||
locale,
|
||||
_messagesExistFor,
|
||||
onFailure: (_) => null,
|
||||
);
|
||||
if (actualLocale == null) return null;
|
||||
return _findExact(actualLocale);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2542
lib/generated/intl/messages_ru.dart
Normal file
2542
lib/generated/intl/messages_ru.dart
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,4 +4,8 @@ class NoL10n {
|
||||
static const String langEn = 'English';
|
||||
static const String langJa = '日本語';
|
||||
static const String langFR = 'Français';
|
||||
static const String langRU = 'Русский';
|
||||
static const String langCodeZhCn = 'zh_CN';
|
||||
|
||||
static const String aniCatTitle = '【寰宇周刊】';
|
||||
}
|
||||
|
||||
1418
lib/generated/proto/auth/auth.pb.dart
Normal file
1418
lib/generated/proto/auth/auth.pb.dart
Normal file
File diff suppressed because it is too large
Load Diff
11
lib/generated/proto/auth/auth.pbenum.dart
Normal file
11
lib/generated/proto/auth/auth.pbenum.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/auth/auth.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
341
lib/generated/proto/auth/auth.pbgrpc.dart
Normal file
341
lib/generated/proto/auth/auth.pbgrpc.dart
Normal file
@ -0,0 +1,341 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/auth/auth.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'auth.pb.dart' as $0;
|
||||
|
||||
export 'auth.pb.dart';
|
||||
|
||||
/// 认证服务
|
||||
@$pb.GrpcServiceName('auth.AuthService')
|
||||
class AuthServiceClient extends $grpc.Client {
|
||||
/// The hostname for this service.
|
||||
static const $core.String defaultHost = '';
|
||||
|
||||
/// OAuth scopes needed for the client.
|
||||
static const $core.List<$core.String> oauthScopes = [
|
||||
'',
|
||||
];
|
||||
|
||||
AuthServiceClient(super.channel, {super.options, super.interceptors});
|
||||
|
||||
/// 获取服务状态(匿名接口)
|
||||
$grpc.ResponseFuture<$0.StatusResponse> status(
|
||||
$0.StatusRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$status, request, options: options);
|
||||
}
|
||||
|
||||
/// 获取当前账号状态(需认证)
|
||||
$grpc.ResponseFuture<$0.LoginResponse> login(
|
||||
$0.LoginRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$login, request, options: options);
|
||||
}
|
||||
|
||||
/// 请求注册(匿名接口)
|
||||
$grpc.ResponseFuture<$0.PreRegisterResponse> preRegister(
|
||||
$0.PreRegisterRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$preRegister, request, options: options);
|
||||
}
|
||||
|
||||
/// 注册账号(匿名接口)
|
||||
$grpc.ResponseFuture<$0.RegisterResponse> register(
|
||||
$0.RegisterRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$register, request, options: options);
|
||||
}
|
||||
|
||||
/// 注销账号(需认证)
|
||||
$grpc.ResponseFuture<$0.UnregisterResponse> unregister(
|
||||
$0.UnregisterRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$unregister, request, options: options);
|
||||
}
|
||||
|
||||
/// 验证 JWT token
|
||||
$grpc.ResponseFuture<$0.ValidateTokenResponse> validateToken(
|
||||
$0.ValidateTokenRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$validateToken, request, options: options);
|
||||
}
|
||||
|
||||
/// 获取公钥信息
|
||||
$grpc.ResponseFuture<$0.GetPublicKeyResponse> getPublicKey(
|
||||
$0.GetPublicKeyRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$getPublicKey, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.GetJWTDomainListResponse> getJWTDomainList(
|
||||
$0.GetJWTDomainListRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$getJWTDomainList, request, options: options);
|
||||
}
|
||||
|
||||
/// 刷新用户资料(需要认证)
|
||||
$grpc.ResponseFuture<$0.RefreshUserProfileResponse> refreshUserProfile(
|
||||
$0.RefreshUserProfileRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$refreshUserProfile, request, options: options);
|
||||
}
|
||||
|
||||
/// 生成 OIDC 授权码(供客户端 App 使用)
|
||||
$grpc.ResponseFuture<$0.GenerateOIDCAuthCodeResponse> generateOIDCAuthCode(
|
||||
$0.GenerateOIDCAuthCodeRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$generateOIDCAuthCode, request, options: options);
|
||||
}
|
||||
|
||||
// method descriptors
|
||||
|
||||
static final _$status =
|
||||
$grpc.ClientMethod<$0.StatusRequest, $0.StatusResponse>(
|
||||
'/auth.AuthService/Status',
|
||||
($0.StatusRequest value) => value.writeToBuffer(),
|
||||
$0.StatusResponse.fromBuffer);
|
||||
static final _$login = $grpc.ClientMethod<$0.LoginRequest, $0.LoginResponse>(
|
||||
'/auth.AuthService/Login',
|
||||
($0.LoginRequest value) => value.writeToBuffer(),
|
||||
$0.LoginResponse.fromBuffer);
|
||||
static final _$preRegister =
|
||||
$grpc.ClientMethod<$0.PreRegisterRequest, $0.PreRegisterResponse>(
|
||||
'/auth.AuthService/PreRegister',
|
||||
($0.PreRegisterRequest value) => value.writeToBuffer(),
|
||||
$0.PreRegisterResponse.fromBuffer);
|
||||
static final _$register =
|
||||
$grpc.ClientMethod<$0.RegisterRequest, $0.RegisterResponse>(
|
||||
'/auth.AuthService/Register',
|
||||
($0.RegisterRequest value) => value.writeToBuffer(),
|
||||
$0.RegisterResponse.fromBuffer);
|
||||
static final _$unregister =
|
||||
$grpc.ClientMethod<$0.UnregisterRequest, $0.UnregisterResponse>(
|
||||
'/auth.AuthService/Unregister',
|
||||
($0.UnregisterRequest value) => value.writeToBuffer(),
|
||||
$0.UnregisterResponse.fromBuffer);
|
||||
static final _$validateToken =
|
||||
$grpc.ClientMethod<$0.ValidateTokenRequest, $0.ValidateTokenResponse>(
|
||||
'/auth.AuthService/ValidateToken',
|
||||
($0.ValidateTokenRequest value) => value.writeToBuffer(),
|
||||
$0.ValidateTokenResponse.fromBuffer);
|
||||
static final _$getPublicKey =
|
||||
$grpc.ClientMethod<$0.GetPublicKeyRequest, $0.GetPublicKeyResponse>(
|
||||
'/auth.AuthService/GetPublicKey',
|
||||
($0.GetPublicKeyRequest value) => value.writeToBuffer(),
|
||||
$0.GetPublicKeyResponse.fromBuffer);
|
||||
static final _$getJWTDomainList = $grpc.ClientMethod<
|
||||
$0.GetJWTDomainListRequest, $0.GetJWTDomainListResponse>(
|
||||
'/auth.AuthService/GetJWTDomainList',
|
||||
($0.GetJWTDomainListRequest value) => value.writeToBuffer(),
|
||||
$0.GetJWTDomainListResponse.fromBuffer);
|
||||
static final _$refreshUserProfile = $grpc.ClientMethod<
|
||||
$0.RefreshUserProfileRequest, $0.RefreshUserProfileResponse>(
|
||||
'/auth.AuthService/RefreshUserProfile',
|
||||
($0.RefreshUserProfileRequest value) => value.writeToBuffer(),
|
||||
$0.RefreshUserProfileResponse.fromBuffer);
|
||||
static final _$generateOIDCAuthCode = $grpc.ClientMethod<
|
||||
$0.GenerateOIDCAuthCodeRequest, $0.GenerateOIDCAuthCodeResponse>(
|
||||
'/auth.AuthService/GenerateOIDCAuthCode',
|
||||
($0.GenerateOIDCAuthCodeRequest value) => value.writeToBuffer(),
|
||||
$0.GenerateOIDCAuthCodeResponse.fromBuffer);
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('auth.AuthService')
|
||||
abstract class AuthServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'auth.AuthService';
|
||||
|
||||
AuthServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.StatusRequest, $0.StatusResponse>(
|
||||
'Status',
|
||||
status_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.StatusRequest.fromBuffer(value),
|
||||
($0.StatusResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.LoginRequest, $0.LoginResponse>(
|
||||
'Login',
|
||||
login_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.LoginRequest.fromBuffer(value),
|
||||
($0.LoginResponse value) => value.writeToBuffer()));
|
||||
$addMethod(
|
||||
$grpc.ServiceMethod<$0.PreRegisterRequest, $0.PreRegisterResponse>(
|
||||
'PreRegister',
|
||||
preRegister_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.PreRegisterRequest.fromBuffer(value),
|
||||
($0.PreRegisterResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.RegisterRequest, $0.RegisterResponse>(
|
||||
'Register',
|
||||
register_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.RegisterRequest.fromBuffer(value),
|
||||
($0.RegisterResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.UnregisterRequest, $0.UnregisterResponse>(
|
||||
'Unregister',
|
||||
unregister_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.UnregisterRequest.fromBuffer(value),
|
||||
($0.UnregisterResponse value) => value.writeToBuffer()));
|
||||
$addMethod(
|
||||
$grpc.ServiceMethod<$0.ValidateTokenRequest, $0.ValidateTokenResponse>(
|
||||
'ValidateToken',
|
||||
validateToken_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.ValidateTokenRequest.fromBuffer(value),
|
||||
($0.ValidateTokenResponse value) => value.writeToBuffer()));
|
||||
$addMethod(
|
||||
$grpc.ServiceMethod<$0.GetPublicKeyRequest, $0.GetPublicKeyResponse>(
|
||||
'GetPublicKey',
|
||||
getPublicKey_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.GetPublicKeyRequest.fromBuffer(value),
|
||||
($0.GetPublicKeyResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.GetJWTDomainListRequest,
|
||||
$0.GetJWTDomainListResponse>(
|
||||
'GetJWTDomainList',
|
||||
getJWTDomainList_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.GetJWTDomainListRequest.fromBuffer(value),
|
||||
($0.GetJWTDomainListResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.RefreshUserProfileRequest,
|
||||
$0.RefreshUserProfileResponse>(
|
||||
'RefreshUserProfile',
|
||||
refreshUserProfile_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.RefreshUserProfileRequest.fromBuffer(value),
|
||||
($0.RefreshUserProfileResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.GenerateOIDCAuthCodeRequest,
|
||||
$0.GenerateOIDCAuthCodeResponse>(
|
||||
'GenerateOIDCAuthCode',
|
||||
generateOIDCAuthCode_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.GenerateOIDCAuthCodeRequest.fromBuffer(value),
|
||||
($0.GenerateOIDCAuthCodeResponse value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.StatusResponse> status_Pre(
|
||||
$grpc.ServiceCall $call, $async.Future<$0.StatusRequest> $request) async {
|
||||
return status($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.StatusResponse> status(
|
||||
$grpc.ServiceCall call, $0.StatusRequest request);
|
||||
|
||||
$async.Future<$0.LoginResponse> login_Pre(
|
||||
$grpc.ServiceCall $call, $async.Future<$0.LoginRequest> $request) async {
|
||||
return login($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.LoginResponse> login(
|
||||
$grpc.ServiceCall call, $0.LoginRequest request);
|
||||
|
||||
$async.Future<$0.PreRegisterResponse> preRegister_Pre($grpc.ServiceCall $call,
|
||||
$async.Future<$0.PreRegisterRequest> $request) async {
|
||||
return preRegister($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.PreRegisterResponse> preRegister(
|
||||
$grpc.ServiceCall call, $0.PreRegisterRequest request);
|
||||
|
||||
$async.Future<$0.RegisterResponse> register_Pre($grpc.ServiceCall $call,
|
||||
$async.Future<$0.RegisterRequest> $request) async {
|
||||
return register($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.RegisterResponse> register(
|
||||
$grpc.ServiceCall call, $0.RegisterRequest request);
|
||||
|
||||
$async.Future<$0.UnregisterResponse> unregister_Pre($grpc.ServiceCall $call,
|
||||
$async.Future<$0.UnregisterRequest> $request) async {
|
||||
return unregister($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.UnregisterResponse> unregister(
|
||||
$grpc.ServiceCall call, $0.UnregisterRequest request);
|
||||
|
||||
$async.Future<$0.ValidateTokenResponse> validateToken_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.ValidateTokenRequest> $request) async {
|
||||
return validateToken($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.ValidateTokenResponse> validateToken(
|
||||
$grpc.ServiceCall call, $0.ValidateTokenRequest request);
|
||||
|
||||
$async.Future<$0.GetPublicKeyResponse> getPublicKey_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.GetPublicKeyRequest> $request) async {
|
||||
return getPublicKey($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GetPublicKeyResponse> getPublicKey(
|
||||
$grpc.ServiceCall call, $0.GetPublicKeyRequest request);
|
||||
|
||||
$async.Future<$0.GetJWTDomainListResponse> getJWTDomainList_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.GetJWTDomainListRequest> $request) async {
|
||||
return getJWTDomainList($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GetJWTDomainListResponse> getJWTDomainList(
|
||||
$grpc.ServiceCall call, $0.GetJWTDomainListRequest request);
|
||||
|
||||
$async.Future<$0.RefreshUserProfileResponse> refreshUserProfile_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.RefreshUserProfileRequest> $request) async {
|
||||
return refreshUserProfile($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.RefreshUserProfileResponse> refreshUserProfile(
|
||||
$grpc.ServiceCall call, $0.RefreshUserProfileRequest request);
|
||||
|
||||
$async.Future<$0.GenerateOIDCAuthCodeResponse> generateOIDCAuthCode_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.GenerateOIDCAuthCodeRequest> $request) async {
|
||||
return generateOIDCAuthCode($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GenerateOIDCAuthCodeResponse> generateOIDCAuthCode(
|
||||
$grpc.ServiceCall call, $0.GenerateOIDCAuthCodeRequest request);
|
||||
}
|
||||
351
lib/generated/proto/auth/auth.pbjson.dart
Normal file
351
lib/generated/proto/auth/auth.pbjson.dart
Normal file
@ -0,0 +1,351 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/auth/auth.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:convert' as $convert;
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
|
||||
@$core.Deprecated('Use generateOIDCAuthCodeRequestDescriptor instead')
|
||||
const GenerateOIDCAuthCodeRequest$json = {
|
||||
'1': 'GenerateOIDCAuthCodeRequest',
|
||||
'2': [
|
||||
{'1': 'nonce', '3': 1, '4': 1, '5': 9, '10': 'nonce'},
|
||||
{'1': 'redirect_uri', '3': 2, '4': 1, '5': 9, '10': 'redirectUri'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GenerateOIDCAuthCodeRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List generateOIDCAuthCodeRequestDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChtHZW5lcmF0ZU9JRENBdXRoQ29kZVJlcXVlc3QSFAoFbm9uY2UYASABKAlSBW5vbmNlEiEKDH'
|
||||
'JlZGlyZWN0X3VyaRgCIAEoCVILcmVkaXJlY3RVcmk=');
|
||||
|
||||
@$core.Deprecated('Use generateOIDCAuthCodeResponseDescriptor instead')
|
||||
const GenerateOIDCAuthCodeResponse$json = {
|
||||
'1': 'GenerateOIDCAuthCodeResponse',
|
||||
'2': [
|
||||
{'1': 'code', '3': 1, '4': 1, '5': 9, '10': 'code'},
|
||||
{'1': 'expires_at', '3': 2, '4': 1, '5': 3, '10': 'expiresAt'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GenerateOIDCAuthCodeResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List generateOIDCAuthCodeResponseDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChxHZW5lcmF0ZU9JRENBdXRoQ29kZVJlc3BvbnNlEhIKBGNvZGUYASABKAlSBGNvZGUSHQoKZX'
|
||||
'hwaXJlc19hdBgCIAEoA1IJZXhwaXJlc0F0');
|
||||
|
||||
@$core.Deprecated('Use refreshUserProfileRequestDescriptor instead')
|
||||
const RefreshUserProfileRequest$json = {
|
||||
'1': 'RefreshUserProfileRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `RefreshUserProfileRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List refreshUserProfileRequestDescriptor =
|
||||
$convert.base64Decode('ChlSZWZyZXNoVXNlclByb2ZpbGVSZXF1ZXN0');
|
||||
|
||||
@$core.Deprecated('Use refreshUserProfileResponseDescriptor instead')
|
||||
const RefreshUserProfileResponse$json = {
|
||||
'1': 'RefreshUserProfileResponse',
|
||||
'2': [
|
||||
{'1': 'success', '3': 1, '4': 1, '5': 8, '10': 'success'},
|
||||
{
|
||||
'1': 'user_info',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.auth.GameUserInfo',
|
||||
'10': 'userInfo'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `RefreshUserProfileResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List refreshUserProfileResponseDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChpSZWZyZXNoVXNlclByb2ZpbGVSZXNwb25zZRIYCgdzdWNjZXNzGAEgASgIUgdzdWNjZXNzEi'
|
||||
'8KCXVzZXJfaW5mbxgCIAEoCzISLmF1dGguR2FtZVVzZXJJbmZvUgh1c2VySW5mbw==');
|
||||
|
||||
@$core.Deprecated('Use statusRequestDescriptor instead')
|
||||
const StatusRequest$json = {
|
||||
'1': 'StatusRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `StatusRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List statusRequestDescriptor =
|
||||
$convert.base64Decode('Cg1TdGF0dXNSZXF1ZXN0');
|
||||
|
||||
@$core.Deprecated('Use statusResponseDescriptor instead')
|
||||
const StatusResponse$json = {
|
||||
'1': 'StatusResponse',
|
||||
'2': [
|
||||
{'1': 'online', '3': 1, '4': 1, '5': 8, '10': 'online'},
|
||||
{'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'},
|
||||
{'1': 'server_time', '3': 3, '4': 1, '5': 3, '10': 'serverTime'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `StatusResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List statusResponseDescriptor = $convert.base64Decode(
|
||||
'Cg5TdGF0dXNSZXNwb25zZRIWCgZvbmxpbmUYASABKAhSBm9ubGluZRIYCgdtZXNzYWdlGAIgAS'
|
||||
'gJUgdtZXNzYWdlEh8KC3NlcnZlcl90aW1lGAMgASgDUgpzZXJ2ZXJUaW1l');
|
||||
|
||||
@$core.Deprecated('Use loginRequestDescriptor instead')
|
||||
const LoginRequest$json = {
|
||||
'1': 'LoginRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `LoginRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List loginRequestDescriptor =
|
||||
$convert.base64Decode('CgxMb2dpblJlcXVlc3Q=');
|
||||
|
||||
@$core.Deprecated('Use gameUserInfoDescriptor instead')
|
||||
const GameUserInfo$json = {
|
||||
'1': 'GameUserInfo',
|
||||
'2': [
|
||||
{'1': 'game_user_id', '3': 1, '4': 1, '5': 9, '10': 'gameUserId'},
|
||||
{'1': 'handle_name', '3': 2, '4': 1, '5': 9, '10': 'handleName'},
|
||||
{'1': 'avatar_url', '3': 3, '4': 1, '5': 9, '10': 'avatarUrl'},
|
||||
{'1': 'citizen_record', '3': 4, '4': 1, '5': 9, '10': 'citizenRecord'},
|
||||
{'1': 'enlisted_date', '3': 5, '4': 1, '5': 3, '10': 'enlistedDate'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GameUserInfo`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List gameUserInfoDescriptor = $convert.base64Decode(
|
||||
'CgxHYW1lVXNlckluZm8SIAoMZ2FtZV91c2VyX2lkGAEgASgJUgpnYW1lVXNlcklkEh8KC2hhbm'
|
||||
'RsZV9uYW1lGAIgASgJUgpoYW5kbGVOYW1lEh0KCmF2YXRhcl91cmwYAyABKAlSCWF2YXRhclVy'
|
||||
'bBIlCg5jaXRpemVuX3JlY29yZBgEIAEoCVINY2l0aXplblJlY29yZBIjCg1lbmxpc3RlZF9kYX'
|
||||
'RlGAUgASgDUgxlbmxpc3RlZERhdGU=');
|
||||
|
||||
@$core.Deprecated('Use loginResponseDescriptor instead')
|
||||
const LoginResponse$json = {
|
||||
'1': 'LoginResponse',
|
||||
'2': [
|
||||
{'1': 'uuid', '3': 1, '4': 1, '5': 9, '10': 'uuid'},
|
||||
{
|
||||
'1': 'user_info',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.auth.GameUserInfo',
|
||||
'10': 'userInfo'
|
||||
},
|
||||
{'1': 'last_login_time', '3': 3, '4': 1, '5': 3, '10': 'lastLoginTime'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `LoginResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List loginResponseDescriptor = $convert.base64Decode(
|
||||
'Cg1Mb2dpblJlc3BvbnNlEhIKBHV1aWQYASABKAlSBHV1aWQSLwoJdXNlcl9pbmZvGAIgASgLMh'
|
||||
'IuYXV0aC5HYW1lVXNlckluZm9SCHVzZXJJbmZvEiYKD2xhc3RfbG9naW5fdGltZRgDIAEoA1IN'
|
||||
'bGFzdExvZ2luVGltZQ==');
|
||||
|
||||
@$core.Deprecated('Use preRegisterRequestDescriptor instead')
|
||||
const PreRegisterRequest$json = {
|
||||
'1': 'PreRegisterRequest',
|
||||
'2': [
|
||||
{'1': 'uuid', '3': 1, '4': 1, '5': 9, '10': 'uuid'},
|
||||
{'1': 'game_user_id', '3': 2, '4': 1, '5': 9, '10': 'gameUserId'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `PreRegisterRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List preRegisterRequestDescriptor = $convert.base64Decode(
|
||||
'ChJQcmVSZWdpc3RlclJlcXVlc3QSEgoEdXVpZBgBIAEoCVIEdXVpZBIgCgxnYW1lX3VzZXJfaW'
|
||||
'QYAiABKAlSCmdhbWVVc2VySWQ=');
|
||||
|
||||
@$core.Deprecated('Use preRegisterResponseDescriptor instead')
|
||||
const PreRegisterResponse$json = {
|
||||
'1': 'PreRegisterResponse',
|
||||
'2': [
|
||||
{
|
||||
'1': 'verification_code',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 9,
|
||||
'10': 'verificationCode'
|
||||
},
|
||||
{'1': 'expire_time', '3': 2, '4': 1, '5': 3, '10': 'expireTime'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `PreRegisterResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List preRegisterResponseDescriptor = $convert.base64Decode(
|
||||
'ChNQcmVSZWdpc3RlclJlc3BvbnNlEisKEXZlcmlmaWNhdGlvbl9jb2RlGAEgASgJUhB2ZXJpZm'
|
||||
'ljYXRpb25Db2RlEh8KC2V4cGlyZV90aW1lGAIgASgDUgpleHBpcmVUaW1l');
|
||||
|
||||
@$core.Deprecated('Use registerRequestDescriptor instead')
|
||||
const RegisterRequest$json = {
|
||||
'1': 'RegisterRequest',
|
||||
'2': [
|
||||
{'1': 'uuid', '3': 1, '4': 1, '5': 9, '10': 'uuid'},
|
||||
{'1': 'game_user_id', '3': 2, '4': 1, '5': 9, '10': 'gameUserId'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `RegisterRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List registerRequestDescriptor = $convert.base64Decode(
|
||||
'Cg9SZWdpc3RlclJlcXVlc3QSEgoEdXVpZBgBIAEoCVIEdXVpZBIgCgxnYW1lX3VzZXJfaWQYAi'
|
||||
'ABKAlSCmdhbWVVc2VySWQ=');
|
||||
|
||||
@$core.Deprecated('Use registerResponseDescriptor instead')
|
||||
const RegisterResponse$json = {
|
||||
'1': 'RegisterResponse',
|
||||
'2': [
|
||||
{
|
||||
'1': 'party_room_secret_key',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 9,
|
||||
'10': 'partyRoomSecretKey'
|
||||
},
|
||||
{
|
||||
'1': 'user_info',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.auth.GameUserInfo',
|
||||
'10': 'userInfo'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `RegisterResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List registerResponseDescriptor = $convert.base64Decode(
|
||||
'ChBSZWdpc3RlclJlc3BvbnNlEjEKFXBhcnR5X3Jvb21fc2VjcmV0X2tleRgBIAEoCVIScGFydH'
|
||||
'lSb29tU2VjcmV0S2V5Ei8KCXVzZXJfaW5mbxgCIAEoCzISLmF1dGguR2FtZVVzZXJJbmZvUgh1'
|
||||
'c2VySW5mbw==');
|
||||
|
||||
@$core.Deprecated('Use unregisterRequestDescriptor instead')
|
||||
const UnregisterRequest$json = {
|
||||
'1': 'UnregisterRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `UnregisterRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List unregisterRequestDescriptor =
|
||||
$convert.base64Decode('ChFVbnJlZ2lzdGVyUmVxdWVzdA==');
|
||||
|
||||
@$core.Deprecated('Use unregisterResponseDescriptor instead')
|
||||
const UnregisterResponse$json = {
|
||||
'1': 'UnregisterResponse',
|
||||
'2': [
|
||||
{'1': 'success', '3': 1, '4': 1, '5': 8, '10': 'success'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `UnregisterResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List unregisterResponseDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChJVbnJlZ2lzdGVyUmVzcG9uc2USGAoHc3VjY2VzcxgBIAEoCFIHc3VjY2Vzcw==');
|
||||
|
||||
@$core.Deprecated('Use validateTokenRequestDescriptor instead')
|
||||
const ValidateTokenRequest$json = {
|
||||
'1': 'ValidateTokenRequest',
|
||||
'2': [
|
||||
{'1': 'token', '3': 1, '4': 1, '5': 9, '10': 'token'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `ValidateTokenRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List validateTokenRequestDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChRWYWxpZGF0ZVRva2VuUmVxdWVzdBIUCgV0b2tlbhgBIAEoCVIFdG9rZW4=');
|
||||
|
||||
@$core.Deprecated('Use validateTokenResponseDescriptor instead')
|
||||
const ValidateTokenResponse$json = {
|
||||
'1': 'ValidateTokenResponse',
|
||||
'2': [
|
||||
{'1': 'valid', '3': 1, '4': 1, '5': 8, '10': 'valid'},
|
||||
{'1': 'domain', '3': 2, '4': 1, '5': 9, '10': 'domain'},
|
||||
{'1': 'issued_at', '3': 3, '4': 1, '5': 3, '10': 'issuedAt'},
|
||||
{'1': 'expires_at', '3': 4, '4': 1, '5': 3, '10': 'expiresAt'},
|
||||
{'1': 'error_message', '3': 5, '4': 1, '5': 9, '10': 'errorMessage'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `ValidateTokenResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List validateTokenResponseDescriptor = $convert.base64Decode(
|
||||
'ChVWYWxpZGF0ZVRva2VuUmVzcG9uc2USFAoFdmFsaWQYASABKAhSBXZhbGlkEhYKBmRvbWFpbh'
|
||||
'gCIAEoCVIGZG9tYWluEhsKCWlzc3VlZF9hdBgDIAEoA1IIaXNzdWVkQXQSHQoKZXhwaXJlc19h'
|
||||
'dBgEIAEoA1IJZXhwaXJlc0F0EiMKDWVycm9yX21lc3NhZ2UYBSABKAlSDGVycm9yTWVzc2FnZQ'
|
||||
'==');
|
||||
|
||||
@$core.Deprecated('Use getPublicKeyRequestDescriptor instead')
|
||||
const GetPublicKeyRequest$json = {
|
||||
'1': 'GetPublicKeyRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `GetPublicKeyRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getPublicKeyRequestDescriptor =
|
||||
$convert.base64Decode('ChNHZXRQdWJsaWNLZXlSZXF1ZXN0');
|
||||
|
||||
@$core.Deprecated('Use getPublicKeyResponseDescriptor instead')
|
||||
const GetPublicKeyResponse$json = {
|
||||
'1': 'GetPublicKeyResponse',
|
||||
'2': [
|
||||
{'1': 'public_key_pem', '3': 1, '4': 1, '5': 9, '10': 'publicKeyPem'},
|
||||
{'1': 'key_id', '3': 2, '4': 1, '5': 9, '10': 'keyId'},
|
||||
{'1': 'algorithm', '3': 3, '4': 1, '5': 9, '10': 'algorithm'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GetPublicKeyResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getPublicKeyResponseDescriptor = $convert.base64Decode(
|
||||
'ChRHZXRQdWJsaWNLZXlSZXNwb25zZRIkCg5wdWJsaWNfa2V5X3BlbRgBIAEoCVIMcHVibGljS2'
|
||||
'V5UGVtEhUKBmtleV9pZBgCIAEoCVIFa2V5SWQSHAoJYWxnb3JpdGhtGAMgASgJUglhbGdvcml0'
|
||||
'aG0=');
|
||||
|
||||
@$core.Deprecated('Use jWTDomainInfoDescriptor instead')
|
||||
const JWTDomainInfo$json = {
|
||||
'1': 'JWTDomainInfo',
|
||||
'2': [
|
||||
{'1': 'domain', '3': 1, '4': 1, '5': 9, '10': 'domain'},
|
||||
{'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `JWTDomainInfo`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List jWTDomainInfoDescriptor = $convert.base64Decode(
|
||||
'Cg1KV1REb21haW5JbmZvEhYKBmRvbWFpbhgBIAEoCVIGZG9tYWluEhIKBG5hbWUYAiABKAlSBG'
|
||||
'5hbWU=');
|
||||
|
||||
@$core.Deprecated('Use getJWTDomainListRequestDescriptor instead')
|
||||
const GetJWTDomainListRequest$json = {
|
||||
'1': 'GetJWTDomainListRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `GetJWTDomainListRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getJWTDomainListRequestDescriptor =
|
||||
$convert.base64Decode('ChdHZXRKV1REb21haW5MaXN0UmVxdWVzdA==');
|
||||
|
||||
@$core.Deprecated('Use getJWTDomainListResponseDescriptor instead')
|
||||
const GetJWTDomainListResponse$json = {
|
||||
'1': 'GetJWTDomainListResponse',
|
||||
'2': [
|
||||
{
|
||||
'1': 'domains',
|
||||
'3': 1,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.auth.JWTDomainInfo',
|
||||
'10': 'domains'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GetJWTDomainListResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getJWTDomainListResponseDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChhHZXRKV1REb21haW5MaXN0UmVzcG9uc2USLQoHZG9tYWlucxgBIAMoCzITLmF1dGguSldURG'
|
||||
'9tYWluSW5mb1IHZG9tYWlucw==');
|
||||
601
lib/generated/proto/common/common.pb.dart
Normal file
601
lib/generated/proto/common/common.pb.dart
Normal file
@ -0,0 +1,601 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/common/common.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
|
||||
|
||||
/// 获取服务器时间请求
|
||||
class GetServerTimeRequest extends $pb.GeneratedMessage {
|
||||
factory GetServerTimeRequest() => create();
|
||||
|
||||
GetServerTimeRequest._();
|
||||
|
||||
factory GetServerTimeRequest.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetServerTimeRequest.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetServerTimeRequest',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetServerTimeRequest clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetServerTimeRequest copyWith(void Function(GetServerTimeRequest) updates) =>
|
||||
super.copyWith((message) => updates(message as GetServerTimeRequest))
|
||||
as GetServerTimeRequest;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetServerTimeRequest create() => GetServerTimeRequest._();
|
||||
@$core.override
|
||||
GetServerTimeRequest createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetServerTimeRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetServerTimeRequest>(create);
|
||||
static GetServerTimeRequest? _defaultInstance;
|
||||
}
|
||||
|
||||
/// 获取服务器时间响应
|
||||
class GetServerTimeResponse extends $pb.GeneratedMessage {
|
||||
factory GetServerTimeResponse({
|
||||
$fixnum.Int64? timestamp,
|
||||
$core.String? timezone,
|
||||
}) {
|
||||
final result = create();
|
||||
if (timestamp != null) result.timestamp = timestamp;
|
||||
if (timezone != null) result.timezone = timezone;
|
||||
return result;
|
||||
}
|
||||
|
||||
GetServerTimeResponse._();
|
||||
|
||||
factory GetServerTimeResponse.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetServerTimeResponse.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetServerTimeResponse',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..aInt64(1, _omitFieldNames ? '' : 'timestamp')
|
||||
..aOS(2, _omitFieldNames ? '' : 'timezone')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetServerTimeResponse clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetServerTimeResponse copyWith(
|
||||
void Function(GetServerTimeResponse) updates) =>
|
||||
super.copyWith((message) => updates(message as GetServerTimeResponse))
|
||||
as GetServerTimeResponse;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetServerTimeResponse create() => GetServerTimeResponse._();
|
||||
@$core.override
|
||||
GetServerTimeResponse createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetServerTimeResponse getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetServerTimeResponse>(create);
|
||||
static GetServerTimeResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$fixnum.Int64 get timestamp => $_getI64(0);
|
||||
@$pb.TagNumber(1)
|
||||
set timestamp($fixnum.Int64 value) => $_setInt64(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasTimestamp() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearTimestamp() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get timezone => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set timezone($core.String value) => $_setString(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasTimezone() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearTimezone() => $_clearField(2);
|
||||
}
|
||||
|
||||
/// 获取版本请求
|
||||
class GetVersionRequest extends $pb.GeneratedMessage {
|
||||
factory GetVersionRequest() => create();
|
||||
|
||||
GetVersionRequest._();
|
||||
|
||||
factory GetVersionRequest.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetVersionRequest.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetVersionRequest',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetVersionRequest clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetVersionRequest copyWith(void Function(GetVersionRequest) updates) =>
|
||||
super.copyWith((message) => updates(message as GetVersionRequest))
|
||||
as GetVersionRequest;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetVersionRequest create() => GetVersionRequest._();
|
||||
@$core.override
|
||||
GetVersionRequest createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetVersionRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetVersionRequest>(create);
|
||||
static GetVersionRequest? _defaultInstance;
|
||||
}
|
||||
|
||||
/// 获取版本响应
|
||||
class GetVersionResponse extends $pb.GeneratedMessage {
|
||||
factory GetVersionResponse({
|
||||
$core.int? version,
|
||||
$core.int? latestVersion,
|
||||
$core.int? minClientVersion,
|
||||
}) {
|
||||
final result = create();
|
||||
if (version != null) result.version = version;
|
||||
if (latestVersion != null) result.latestVersion = latestVersion;
|
||||
if (minClientVersion != null) result.minClientVersion = minClientVersion;
|
||||
return result;
|
||||
}
|
||||
|
||||
GetVersionResponse._();
|
||||
|
||||
factory GetVersionResponse.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetVersionResponse.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetVersionResponse',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..aI(1, _omitFieldNames ? '' : 'version')
|
||||
..aI(2, _omitFieldNames ? '' : 'latestVersion')
|
||||
..aI(3, _omitFieldNames ? '' : 'minClientVersion')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetVersionResponse clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetVersionResponse copyWith(void Function(GetVersionResponse) updates) =>
|
||||
super.copyWith((message) => updates(message as GetVersionResponse))
|
||||
as GetVersionResponse;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetVersionResponse create() => GetVersionResponse._();
|
||||
@$core.override
|
||||
GetVersionResponse createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetVersionResponse getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetVersionResponse>(create);
|
||||
static GetVersionResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get version => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set version($core.int value) => $_setSignedInt32(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasVersion() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearVersion() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get latestVersion => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set latestVersion($core.int value) => $_setSignedInt32(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasLatestVersion() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearLatestVersion() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get minClientVersion => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set minClientVersion($core.int value) => $_setSignedInt32(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasMinClientVersion() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearMinClientVersion() => $_clearField(3);
|
||||
}
|
||||
|
||||
/// 信号类型
|
||||
class SignalType extends $pb.GeneratedMessage {
|
||||
factory SignalType({
|
||||
$core.String? id,
|
||||
$core.String? name,
|
||||
$core.bool? isSpecial,
|
||||
}) {
|
||||
final result = create();
|
||||
if (id != null) result.id = id;
|
||||
if (name != null) result.name = name;
|
||||
if (isSpecial != null) result.isSpecial = isSpecial;
|
||||
return result;
|
||||
}
|
||||
|
||||
SignalType._();
|
||||
|
||||
factory SignalType.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory SignalType.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'SignalType',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'id')
|
||||
..aOS(2, _omitFieldNames ? '' : 'name')
|
||||
..aOB(3, _omitFieldNames ? '' : 'isSpecial')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
SignalType clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
SignalType copyWith(void Function(SignalType) updates) =>
|
||||
super.copyWith((message) => updates(message as SignalType)) as SignalType;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SignalType create() => SignalType._();
|
||||
@$core.override
|
||||
SignalType createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SignalType getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<SignalType>(create);
|
||||
static SignalType? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get id => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set id($core.String value) => $_setString(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get name => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set name($core.String value) => $_setString(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasName() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearName() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool get isSpecial => $_getBF(2);
|
||||
@$pb.TagNumber(3)
|
||||
set isSpecial($core.bool value) => $_setBool(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasIsSpecial() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearIsSpecial() => $_clearField(3);
|
||||
}
|
||||
|
||||
/// 获取信号类型请求
|
||||
class GetSignalTypesRequest extends $pb.GeneratedMessage {
|
||||
factory GetSignalTypesRequest() => create();
|
||||
|
||||
GetSignalTypesRequest._();
|
||||
|
||||
factory GetSignalTypesRequest.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetSignalTypesRequest.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetSignalTypesRequest',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetSignalTypesRequest clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetSignalTypesRequest copyWith(
|
||||
void Function(GetSignalTypesRequest) updates) =>
|
||||
super.copyWith((message) => updates(message as GetSignalTypesRequest))
|
||||
as GetSignalTypesRequest;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetSignalTypesRequest create() => GetSignalTypesRequest._();
|
||||
@$core.override
|
||||
GetSignalTypesRequest createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetSignalTypesRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetSignalTypesRequest>(create);
|
||||
static GetSignalTypesRequest? _defaultInstance;
|
||||
}
|
||||
|
||||
/// 获取信号类型响应
|
||||
class GetSignalTypesResponse extends $pb.GeneratedMessage {
|
||||
factory GetSignalTypesResponse({
|
||||
$core.Iterable<SignalType>? signals,
|
||||
}) {
|
||||
final result = create();
|
||||
if (signals != null) result.signals.addAll(signals);
|
||||
return result;
|
||||
}
|
||||
|
||||
GetSignalTypesResponse._();
|
||||
|
||||
factory GetSignalTypesResponse.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetSignalTypesResponse.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetSignalTypesResponse',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..pPM<SignalType>(1, _omitFieldNames ? '' : 'signals',
|
||||
subBuilder: SignalType.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetSignalTypesResponse clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetSignalTypesResponse copyWith(
|
||||
void Function(GetSignalTypesResponse) updates) =>
|
||||
super.copyWith((message) => updates(message as GetSignalTypesResponse))
|
||||
as GetSignalTypesResponse;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetSignalTypesResponse create() => GetSignalTypesResponse._();
|
||||
@$core.override
|
||||
GetSignalTypesResponse createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetSignalTypesResponse getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetSignalTypesResponse>(create);
|
||||
static GetSignalTypesResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$pb.PbList<SignalType> get signals => $_getList(0);
|
||||
}
|
||||
|
||||
/// 标签
|
||||
class Tag extends $pb.GeneratedMessage {
|
||||
factory Tag({
|
||||
$core.String? id,
|
||||
$core.String? name,
|
||||
$core.String? info,
|
||||
$core.String? color,
|
||||
$core.Iterable<Tag>? subTags,
|
||||
}) {
|
||||
final result = create();
|
||||
if (id != null) result.id = id;
|
||||
if (name != null) result.name = name;
|
||||
if (info != null) result.info = info;
|
||||
if (color != null) result.color = color;
|
||||
if (subTags != null) result.subTags.addAll(subTags);
|
||||
return result;
|
||||
}
|
||||
|
||||
Tag._();
|
||||
|
||||
factory Tag.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory Tag.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'Tag',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'id')
|
||||
..aOS(2, _omitFieldNames ? '' : 'name')
|
||||
..aOS(3, _omitFieldNames ? '' : 'info')
|
||||
..aOS(4, _omitFieldNames ? '' : 'color')
|
||||
..pPM<Tag>(5, _omitFieldNames ? '' : 'subTags', subBuilder: Tag.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
Tag clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
Tag copyWith(void Function(Tag) updates) =>
|
||||
super.copyWith((message) => updates(message as Tag)) as Tag;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Tag create() => Tag._();
|
||||
@$core.override
|
||||
Tag createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Tag getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Tag>(create);
|
||||
static Tag? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get id => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set id($core.String value) => $_setString(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get name => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set name($core.String value) => $_setString(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasName() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearName() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.String get info => $_getSZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set info($core.String value) => $_setString(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasInfo() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearInfo() => $_clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.String get color => $_getSZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set color($core.String value) => $_setString(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasColor() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearColor() => $_clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$pb.PbList<Tag> get subTags => $_getList(4);
|
||||
}
|
||||
|
||||
/// 获取标签请求
|
||||
class GetTagsRequest extends $pb.GeneratedMessage {
|
||||
factory GetTagsRequest() => create();
|
||||
|
||||
GetTagsRequest._();
|
||||
|
||||
factory GetTagsRequest.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetTagsRequest.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetTagsRequest',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetTagsRequest clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetTagsRequest copyWith(void Function(GetTagsRequest) updates) =>
|
||||
super.copyWith((message) => updates(message as GetTagsRequest))
|
||||
as GetTagsRequest;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetTagsRequest create() => GetTagsRequest._();
|
||||
@$core.override
|
||||
GetTagsRequest createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetTagsRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetTagsRequest>(create);
|
||||
static GetTagsRequest? _defaultInstance;
|
||||
}
|
||||
|
||||
/// 获取标签响应
|
||||
class GetTagsResponse extends $pb.GeneratedMessage {
|
||||
factory GetTagsResponse({
|
||||
$core.Iterable<Tag>? tags,
|
||||
}) {
|
||||
final result = create();
|
||||
if (tags != null) result.tags.addAll(tags);
|
||||
return result;
|
||||
}
|
||||
|
||||
GetTagsResponse._();
|
||||
|
||||
factory GetTagsResponse.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory GetTagsResponse.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'GetTagsResponse',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'common'),
|
||||
createEmptyInstance: create)
|
||||
..pPM<Tag>(1, _omitFieldNames ? '' : 'tags', subBuilder: Tag.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetTagsResponse clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
GetTagsResponse copyWith(void Function(GetTagsResponse) updates) =>
|
||||
super.copyWith((message) => updates(message as GetTagsResponse))
|
||||
as GetTagsResponse;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetTagsResponse create() => GetTagsResponse._();
|
||||
@$core.override
|
||||
GetTagsResponse createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GetTagsResponse getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<GetTagsResponse>(create);
|
||||
static GetTagsResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$pb.PbList<Tag> get tags => $_getList(0);
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const $core.bool _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
11
lib/generated/proto/common/common.pbenum.dart
Normal file
11
lib/generated/proto/common/common.pbenum.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/common/common.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
164
lib/generated/proto/common/common.pbgrpc.dart
Normal file
164
lib/generated/proto/common/common.pbgrpc.dart
Normal file
@ -0,0 +1,164 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/common/common.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'common.pb.dart' as $0;
|
||||
|
||||
export 'common.pb.dart';
|
||||
|
||||
/// 通用服务
|
||||
@$pb.GrpcServiceName('common.CommonService')
|
||||
class CommonServiceClient extends $grpc.Client {
|
||||
/// The hostname for this service.
|
||||
static const $core.String defaultHost = '';
|
||||
|
||||
/// OAuth scopes needed for the client.
|
||||
static const $core.List<$core.String> oauthScopes = [
|
||||
'',
|
||||
];
|
||||
|
||||
CommonServiceClient(super.channel, {super.options, super.interceptors});
|
||||
|
||||
/// 获取服务器时间(匿名接口)
|
||||
$grpc.ResponseFuture<$0.GetServerTimeResponse> getServerTime(
|
||||
$0.GetServerTimeRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$getServerTime, request, options: options);
|
||||
}
|
||||
|
||||
/// 获取版本信息(匿名接口)
|
||||
$grpc.ResponseFuture<$0.GetVersionResponse> getVersion(
|
||||
$0.GetVersionRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$getVersion, request, options: options);
|
||||
}
|
||||
|
||||
/// 获取信号类型列表(匿名接口)
|
||||
$grpc.ResponseFuture<$0.GetSignalTypesResponse> getSignalTypes(
|
||||
$0.GetSignalTypesRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$getSignalTypes, request, options: options);
|
||||
}
|
||||
|
||||
/// 获取房间标签(匿名接口)
|
||||
$grpc.ResponseFuture<$0.GetTagsResponse> getTags(
|
||||
$0.GetTagsRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createUnaryCall(_$getTags, request, options: options);
|
||||
}
|
||||
|
||||
// method descriptors
|
||||
|
||||
static final _$getServerTime =
|
||||
$grpc.ClientMethod<$0.GetServerTimeRequest, $0.GetServerTimeResponse>(
|
||||
'/common.CommonService/GetServerTime',
|
||||
($0.GetServerTimeRequest value) => value.writeToBuffer(),
|
||||
$0.GetServerTimeResponse.fromBuffer);
|
||||
static final _$getVersion =
|
||||
$grpc.ClientMethod<$0.GetVersionRequest, $0.GetVersionResponse>(
|
||||
'/common.CommonService/GetVersion',
|
||||
($0.GetVersionRequest value) => value.writeToBuffer(),
|
||||
$0.GetVersionResponse.fromBuffer);
|
||||
static final _$getSignalTypes =
|
||||
$grpc.ClientMethod<$0.GetSignalTypesRequest, $0.GetSignalTypesResponse>(
|
||||
'/common.CommonService/GetSignalTypes',
|
||||
($0.GetSignalTypesRequest value) => value.writeToBuffer(),
|
||||
$0.GetSignalTypesResponse.fromBuffer);
|
||||
static final _$getTags =
|
||||
$grpc.ClientMethod<$0.GetTagsRequest, $0.GetTagsResponse>(
|
||||
'/common.CommonService/GetTags',
|
||||
($0.GetTagsRequest value) => value.writeToBuffer(),
|
||||
$0.GetTagsResponse.fromBuffer);
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('common.CommonService')
|
||||
abstract class CommonServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'common.CommonService';
|
||||
|
||||
CommonServiceBase() {
|
||||
$addMethod(
|
||||
$grpc.ServiceMethod<$0.GetServerTimeRequest, $0.GetServerTimeResponse>(
|
||||
'GetServerTime',
|
||||
getServerTime_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.GetServerTimeRequest.fromBuffer(value),
|
||||
($0.GetServerTimeResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.GetVersionRequest, $0.GetVersionResponse>(
|
||||
'GetVersion',
|
||||
getVersion_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.GetVersionRequest.fromBuffer(value),
|
||||
($0.GetVersionResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.GetSignalTypesRequest,
|
||||
$0.GetSignalTypesResponse>(
|
||||
'GetSignalTypes',
|
||||
getSignalTypes_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.GetSignalTypesRequest.fromBuffer(value),
|
||||
($0.GetSignalTypesResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.GetTagsRequest, $0.GetTagsResponse>(
|
||||
'GetTags',
|
||||
getTags_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.GetTagsRequest.fromBuffer(value),
|
||||
($0.GetTagsResponse value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.GetServerTimeResponse> getServerTime_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.GetServerTimeRequest> $request) async {
|
||||
return getServerTime($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GetServerTimeResponse> getServerTime(
|
||||
$grpc.ServiceCall call, $0.GetServerTimeRequest request);
|
||||
|
||||
$async.Future<$0.GetVersionResponse> getVersion_Pre($grpc.ServiceCall $call,
|
||||
$async.Future<$0.GetVersionRequest> $request) async {
|
||||
return getVersion($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GetVersionResponse> getVersion(
|
||||
$grpc.ServiceCall call, $0.GetVersionRequest request);
|
||||
|
||||
$async.Future<$0.GetSignalTypesResponse> getSignalTypes_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.GetSignalTypesRequest> $request) async {
|
||||
return getSignalTypes($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GetSignalTypesResponse> getSignalTypes(
|
||||
$grpc.ServiceCall call, $0.GetSignalTypesRequest request);
|
||||
|
||||
$async.Future<$0.GetTagsResponse> getTags_Pre($grpc.ServiceCall $call,
|
||||
$async.Future<$0.GetTagsRequest> $request) async {
|
||||
return getTags($call, await $request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GetTagsResponse> getTags(
|
||||
$grpc.ServiceCall call, $0.GetTagsRequest request);
|
||||
}
|
||||
161
lib/generated/proto/common/common.pbjson.dart
Normal file
161
lib/generated/proto/common/common.pbjson.dart
Normal file
@ -0,0 +1,161 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/common/common.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:convert' as $convert;
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
|
||||
@$core.Deprecated('Use getServerTimeRequestDescriptor instead')
|
||||
const GetServerTimeRequest$json = {
|
||||
'1': 'GetServerTimeRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `GetServerTimeRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getServerTimeRequestDescriptor =
|
||||
$convert.base64Decode('ChRHZXRTZXJ2ZXJUaW1lUmVxdWVzdA==');
|
||||
|
||||
@$core.Deprecated('Use getServerTimeResponseDescriptor instead')
|
||||
const GetServerTimeResponse$json = {
|
||||
'1': 'GetServerTimeResponse',
|
||||
'2': [
|
||||
{'1': 'timestamp', '3': 1, '4': 1, '5': 3, '10': 'timestamp'},
|
||||
{'1': 'timezone', '3': 2, '4': 1, '5': 9, '10': 'timezone'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GetServerTimeResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getServerTimeResponseDescriptor = $convert.base64Decode(
|
||||
'ChVHZXRTZXJ2ZXJUaW1lUmVzcG9uc2USHAoJdGltZXN0YW1wGAEgASgDUgl0aW1lc3RhbXASGg'
|
||||
'oIdGltZXpvbmUYAiABKAlSCHRpbWV6b25l');
|
||||
|
||||
@$core.Deprecated('Use getVersionRequestDescriptor instead')
|
||||
const GetVersionRequest$json = {
|
||||
'1': 'GetVersionRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `GetVersionRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getVersionRequestDescriptor =
|
||||
$convert.base64Decode('ChFHZXRWZXJzaW9uUmVxdWVzdA==');
|
||||
|
||||
@$core.Deprecated('Use getVersionResponseDescriptor instead')
|
||||
const GetVersionResponse$json = {
|
||||
'1': 'GetVersionResponse',
|
||||
'2': [
|
||||
{'1': 'version', '3': 1, '4': 1, '5': 5, '10': 'version'},
|
||||
{'1': 'latest_version', '3': 2, '4': 1, '5': 5, '10': 'latestVersion'},
|
||||
{
|
||||
'1': 'min_client_version',
|
||||
'3': 3,
|
||||
'4': 1,
|
||||
'5': 5,
|
||||
'10': 'minClientVersion'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GetVersionResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getVersionResponseDescriptor = $convert.base64Decode(
|
||||
'ChJHZXRWZXJzaW9uUmVzcG9uc2USGAoHdmVyc2lvbhgBIAEoBVIHdmVyc2lvbhIlCg5sYXRlc3'
|
||||
'RfdmVyc2lvbhgCIAEoBVINbGF0ZXN0VmVyc2lvbhIsChJtaW5fY2xpZW50X3ZlcnNpb24YAyAB'
|
||||
'KAVSEG1pbkNsaWVudFZlcnNpb24=');
|
||||
|
||||
@$core.Deprecated('Use signalTypeDescriptor instead')
|
||||
const SignalType$json = {
|
||||
'1': 'SignalType',
|
||||
'2': [
|
||||
{'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
|
||||
{'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
|
||||
{'1': 'is_special', '3': 3, '4': 1, '5': 8, '10': 'isSpecial'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SignalType`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List signalTypeDescriptor = $convert.base64Decode(
|
||||
'CgpTaWduYWxUeXBlEg4KAmlkGAEgASgJUgJpZBISCgRuYW1lGAIgASgJUgRuYW1lEh0KCmlzX3'
|
||||
'NwZWNpYWwYAyABKAhSCWlzU3BlY2lhbA==');
|
||||
|
||||
@$core.Deprecated('Use getSignalTypesRequestDescriptor instead')
|
||||
const GetSignalTypesRequest$json = {
|
||||
'1': 'GetSignalTypesRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `GetSignalTypesRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getSignalTypesRequestDescriptor =
|
||||
$convert.base64Decode('ChVHZXRTaWduYWxUeXBlc1JlcXVlc3Q=');
|
||||
|
||||
@$core.Deprecated('Use getSignalTypesResponseDescriptor instead')
|
||||
const GetSignalTypesResponse$json = {
|
||||
'1': 'GetSignalTypesResponse',
|
||||
'2': [
|
||||
{
|
||||
'1': 'signals',
|
||||
'3': 1,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.common.SignalType',
|
||||
'10': 'signals'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GetSignalTypesResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getSignalTypesResponseDescriptor =
|
||||
$convert.base64Decode(
|
||||
'ChZHZXRTaWduYWxUeXBlc1Jlc3BvbnNlEiwKB3NpZ25hbHMYASADKAsyEi5jb21tb24uU2lnbm'
|
||||
'FsVHlwZVIHc2lnbmFscw==');
|
||||
|
||||
@$core.Deprecated('Use tagDescriptor instead')
|
||||
const Tag$json = {
|
||||
'1': 'Tag',
|
||||
'2': [
|
||||
{'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
|
||||
{'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
|
||||
{'1': 'info', '3': 3, '4': 1, '5': 9, '10': 'info'},
|
||||
{'1': 'color', '3': 4, '4': 1, '5': 9, '10': 'color'},
|
||||
{
|
||||
'1': 'sub_tags',
|
||||
'3': 5,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.common.Tag',
|
||||
'10': 'subTags'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `Tag`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List tagDescriptor = $convert.base64Decode(
|
||||
'CgNUYWcSDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEaW5mbxgDIAEoCV'
|
||||
'IEaW5mbxIUCgVjb2xvchgEIAEoCVIFY29sb3ISJgoIc3ViX3RhZ3MYBSADKAsyCy5jb21tb24u'
|
||||
'VGFnUgdzdWJUYWdz');
|
||||
|
||||
@$core.Deprecated('Use getTagsRequestDescriptor instead')
|
||||
const GetTagsRequest$json = {
|
||||
'1': 'GetTagsRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `GetTagsRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getTagsRequestDescriptor =
|
||||
$convert.base64Decode('Cg5HZXRUYWdzUmVxdWVzdA==');
|
||||
|
||||
@$core.Deprecated('Use getTagsResponseDescriptor instead')
|
||||
const GetTagsResponse$json = {
|
||||
'1': 'GetTagsResponse',
|
||||
'2': [
|
||||
{'1': 'tags', '3': 1, '4': 3, '5': 11, '6': '.common.Tag', '10': 'tags'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GetTagsResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List getTagsResponseDescriptor = $convert.base64Decode(
|
||||
'Cg9HZXRUYWdzUmVzcG9uc2USHwoEdGFncxgBIAMoCzILLmNvbW1vbi5UYWdSBHRhZ3M=');
|
||||
2980
lib/generated/proto/partroom/partroom.pb.dart
Normal file
2980
lib/generated/proto/partroom/partroom.pb.dart
Normal file
File diff suppressed because it is too large
Load Diff
56
lib/generated/proto/partroom/partroom.pbenum.dart
Normal file
56
lib/generated/proto/partroom/partroom.pbenum.dart
Normal file
@ -0,0 +1,56 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from proto/partroom/partroom.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
/// 房间事件类型
|
||||
class RoomEventType extends $pb.ProtobufEnum {
|
||||
static const RoomEventType MEMBER_JOINED =
|
||||
RoomEventType._(0, _omitEnumNames ? '' : 'MEMBER_JOINED');
|
||||
static const RoomEventType MEMBER_LEFT =
|
||||
RoomEventType._(1, _omitEnumNames ? '' : 'MEMBER_LEFT');
|
||||
static const RoomEventType OWNER_CHANGED =
|
||||
RoomEventType._(2, _omitEnumNames ? '' : 'OWNER_CHANGED');
|
||||
static const RoomEventType ROOM_UPDATED =
|
||||
RoomEventType._(3, _omitEnumNames ? '' : 'ROOM_UPDATED');
|
||||
static const RoomEventType MEMBER_STATUS_UPDATED =
|
||||
RoomEventType._(4, _omitEnumNames ? '' : 'MEMBER_STATUS_UPDATED');
|
||||
static const RoomEventType SIGNAL_BROADCAST =
|
||||
RoomEventType._(5, _omitEnumNames ? '' : 'SIGNAL_BROADCAST');
|
||||
static const RoomEventType ROOM_DISMISSED =
|
||||
RoomEventType._(6, _omitEnumNames ? '' : 'ROOM_DISMISSED');
|
||||
static const RoomEventType MEMBER_KICKED =
|
||||
RoomEventType._(7, _omitEnumNames ? '' : 'MEMBER_KICKED');
|
||||
|
||||
static const $core.List<RoomEventType> values = <RoomEventType>[
|
||||
MEMBER_JOINED,
|
||||
MEMBER_LEFT,
|
||||
OWNER_CHANGED,
|
||||
ROOM_UPDATED,
|
||||
MEMBER_STATUS_UPDATED,
|
||||
SIGNAL_BROADCAST,
|
||||
ROOM_DISMISSED,
|
||||
MEMBER_KICKED,
|
||||
];
|
||||
|
||||
static final $core.List<RoomEventType?> _byValue =
|
||||
$pb.ProtobufEnum.$_initByValueList(values, 7);
|
||||
static RoomEventType? valueOf($core.int value) =>
|
||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||
|
||||
const RoomEventType._(super.value, super.name);
|
||||
}
|
||||
|
||||
const $core.bool _omitEnumNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_enum_names');
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user