mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-08 08:45:37 +00:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57f73684e8 | ||
|
|
7833cdebb2 | ||
|
|
67a5ae2985 | ||
|
|
f58c52d26b | ||
|
|
41e7739461 | ||
|
|
332152b677 | ||
|
|
85b49fe1f0 | ||
|
|
e7924532be | ||
|
|
475d950ad6 | ||
|
|
e6cfb29c6f | ||
|
|
dee6e86db1 | ||
|
|
72f088331f | ||
|
|
bbf536d10e | ||
|
|
149ac98297 | ||
|
|
b90a2910c9 | ||
|
|
c4da8a3a8d | ||
|
|
3ca75583d2 | ||
|
|
5f4607ae6f | ||
|
|
d880c6873f | ||
|
|
937649b2ed | ||
|
|
78e912c886 | ||
|
|
696c7d2cd1 | ||
|
|
49c0cb026b | ||
|
|
7091341b4b | ||
|
|
8795ce6af3 | ||
|
|
239b34d15a | ||
|
|
729a5ad1a9 | ||
|
|
8febc2476b | ||
|
|
84b1317927 | ||
|
|
bfb504e5db | ||
|
|
9975deacfb | ||
|
|
f77f071003 | ||
|
|
4a8d55e83d | ||
|
|
9a99f40e2a | ||
|
|
428fa8035c | ||
|
|
745f3dee17 | ||
|
|
9907cc1875 | ||
|
|
130cd780a2 | ||
|
|
a808e5d1a5 | ||
|
|
b926117e26 | ||
|
|
fdf238accf | ||
|
|
4e1c27e8a3 | ||
|
|
a3e51a0ac5 | ||
|
|
d27bb0d54f | ||
|
|
f6a750f06b | ||
|
|
c4e2f63e69 | ||
|
|
675ffc8f42 | ||
|
|
cdc4221175 | ||
|
|
843f3363fd | ||
|
|
17653a6374 | ||
|
|
7d860533a0 | ||
|
|
0c751b3ced | ||
|
|
80a522ab06 | ||
|
|
0718d76e00 | ||
|
|
a4887c5358 | ||
|
|
2ad5e122ff | ||
|
|
832d874a0e | ||
|
|
6a133d4dbd | ||
|
|
d96dfa63c9 | ||
|
|
d5c8c05426 | ||
|
|
b6bb4647c7 | ||
|
|
a81f06b743 | ||
|
|
cb41a33546 | ||
|
|
005068de9c | ||
|
|
d8c7b1e070 | ||
|
|
4835dfcc42 | ||
|
|
15bceb09a2 | ||
|
|
13e818abfd | ||
|
|
9c5f6049b5 | ||
|
|
2095b619a4 | ||
|
|
df155cbc1b | ||
|
|
3781729fd1 | ||
|
|
d03c634ec0 | ||
|
|
93c979c218 | ||
|
|
90fef94583 | ||
|
|
0b8eec2263 | ||
|
|
9511af4e6d | ||
|
|
7deaf1fd9f | ||
|
|
43cc5bd520 | ||
|
|
68b58aa520 | ||
|
|
dbadd09af3 | ||
|
|
fcbe2c754f | ||
|
|
ad4580677b | ||
|
|
01a6cc7d92 | ||
|
|
95ce95a187 | ||
|
|
ce8e6fbfb0 | ||
|
|
a20eaed76b | ||
|
|
419af72028 | ||
|
|
8e499522f6 | ||
|
|
84321b847e | ||
|
|
c969a4d465 | ||
|
|
0e12c850b6 | ||
|
|
442835dd9b | ||
|
|
b4ff99cb2e | ||
|
|
aa0ecbc998 | ||
|
|
cc412e93c0 | ||
|
|
1d36fadbfa | ||
|
|
56182bd87d | ||
|
|
d290ff92b3 | ||
|
|
298c5fd3b8 | ||
|
|
e365c43781 |
@@ -1,12 +1,7 @@
|
||||
FROM elixir:1.16-otp-25
|
||||
FROM elixir:1.17-otp-27
|
||||
|
||||
RUN apt update -yq
|
||||
RUN apt install -yq curl gnupg mc inotify-tools
|
||||
RUN apt install -yq curl gnupg
|
||||
RUN apt --fix-broken install
|
||||
RUN apt remove -y nodejs nodejs-doc
|
||||
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
|
||||
RUN apt install -y nodejs
|
||||
RUN npm install --global yarn
|
||||
|
||||
RUN mix local.hex --force
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "0.1"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:14.3
|
||||
image: postgres:13-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
@@ -10,13 +10,13 @@ services:
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- db:/var/lib/postgresql/data
|
||||
- db-new:/var/lib/postgresql/data
|
||||
|
||||
wanderer:
|
||||
environment:
|
||||
PORT: 8000
|
||||
DB_HOST: db
|
||||
WEB_APP_URL: "http://localhost:4444"
|
||||
WEB_APP_URL: "http://localhost:8000"
|
||||
ERL_AFLAGS: "-kernel shell_history enabled"
|
||||
build:
|
||||
context: .
|
||||
@@ -33,4 +33,4 @@ services:
|
||||
|
||||
volumes:
|
||||
elixir-artifacts: {}
|
||||
db: {}
|
||||
db-new: {}
|
||||
|
||||
32
.github/workflows/build.yml
vendored
32
.github/workflows/build.yml
vendored
@@ -58,6 +58,8 @@ jobs:
|
||||
otp: ["27"]
|
||||
elixir: ["1.17"]
|
||||
node-version: ["18.x"]
|
||||
outputs:
|
||||
commit_hash: ${{ steps.generate-changelog.outputs.commit_hash }}
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
@@ -108,16 +110,17 @@ jobs:
|
||||
run: mix compile
|
||||
|
||||
- name: Generate Changelog & Update Tag Version
|
||||
id: generate-changelog
|
||||
run: |
|
||||
git config --global user.name 'CI'
|
||||
git config --global user.email 'ci@users.noreply.github.com'
|
||||
mix git_ops.release --force-patch --yes
|
||||
git push --follow-tags
|
||||
echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
docker:
|
||||
name: 🛠 Build Docker Images
|
||||
needs:
|
||||
- build
|
||||
needs: build
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
@@ -141,6 +144,7 @@ jobs:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ needs.build.outputs.commit_hash }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare Changelog
|
||||
@@ -189,6 +193,30 @@ jobs:
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.build.outputs.digest }}
|
||||
|
||||
- uses: markpatterson27/markdown-to-output@v1
|
||||
id: extract-changelog
|
||||
with:
|
||||
filepath: CHANGELOG.md
|
||||
|
||||
- name: Get content
|
||||
uses: 2428392/gh-truncate-string-action@v1.3.0
|
||||
id: get-content
|
||||
with:
|
||||
stringToTruncate: |
|
||||
📣 Wanderer new release available 🎉
|
||||
|
||||
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
|
||||
${{ steps.extract-changelog.outputs.body }}
|
||||
maxLength: 500
|
||||
truncationSymbol: "…"
|
||||
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ steps.get-content.outputs.string }}
|
||||
|
||||
create-release:
|
||||
name: 🏷 Create Release
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
196
CHANGELOG.md
196
CHANGELOG.md
@@ -2,25 +2,138 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.8.0](https://github.com/wanderer-industries/wanderer/compare/v1.7.0...v1.8.0) (2024-10-13)
|
||||
## [v1.15.2](https://github.com/wanderer-industries/wanderer/compare/v1.15.1...v1.15.2) (2024-11-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.15.1](https://github.com/wanderer-industries/wanderer/compare/v1.15.0...v1.15.1) (2024-11-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Dev: Update .devcontainer instructions
|
||||
|
||||
## [v1.15.0](https://github.com/wanderer-industries/wanderer/compare/v1.14.1...v1.15.0) (2024-11-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Link signature on splash
|
||||
* Connections: Add connection mark EOL time (#56)
|
||||
|
||||
## [v1.7.0](https://github.com/wanderer-industries/wanderer/compare/v1.6.0...v1.7.0) (2024-10-13)
|
||||
## [v1.14.1](https://github.com/wanderer-industries/wanderer/compare/v1.14.0...v1.14.1) (2024-11-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fix character tracking permissions
|
||||
|
||||
## [v1.14.0](https://github.com/wanderer-industries/wanderer/compare/v1.13.12...v1.14.0) (2024-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Link signature on splash
|
||||
* ACL: Add an ability to assign member role without DnD
|
||||
|
||||
## [v1.6.0](https://github.com/wanderer-industries/wanderer/compare/v1.5.0...v1.6.0) (2024-10-13)
|
||||
## [v1.13.12](https://github.com/wanderer-industries/wanderer/compare/v1.13.11...v1.13.12) (2024-11-04)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix system revert issues
|
||||
|
||||
## [v1.13.11](https://github.com/wanderer-industries/wanderer/compare/v1.13.10...v1.13.11) (2024-11-02)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix system revert issues
|
||||
|
||||
## [v1.13.10](https://github.com/wanderer-industries/wanderer/compare/v1.13.9...v1.13.10) (2024-11-01)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix system revert issues
|
||||
|
||||
## [v1.13.9](https://github.com/wanderer-industries/wanderer/compare/v1.13.8...v1.13.9) (2024-11-01)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.13.8](https://github.com/wanderer-industries/wanderer/compare/v1.13.7...v1.13.8) (2024-10-28)
|
||||
|
||||
|
||||
|
||||
## [v1.13.0](https://github.com/wanderer-industries/wanderer/compare/v1.12.11...v1.13.0) (2024-10-28)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Use ESI /characters/affiliation API
|
||||
|
||||
## [v1.12.4](https://github.com/wanderer-industries/wanderer/compare/v1.12.3...v1.12.4) (2024-10-21)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix systems cleanup
|
||||
|
||||
## [v1.12.3](https://github.com/wanderer-industries/wanderer/compare/v1.12.2...v1.12.3) (2024-10-18)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix regression issues
|
||||
|
||||
## [v1.12.1](https://github.com/wanderer-industries/wanderer/compare/v1.12.0...v1.12.1) (2024-10-16)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix system add error after map page refresh
|
||||
|
||||
## [v1.12.0](https://github.com/wanderer-industries/wanderer/compare/v1.11.5...v1.12.0) (2024-10-16)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Prettify user settings
|
||||
|
||||
## [v1.11.0](https://github.com/wanderer-industries/wanderer/compare/v1.10.0...v1.11.0) (2024-10-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Add map level option to store custom labels
|
||||
|
||||
## [v1.10.0](https://github.com/wanderer-industries/wanderer/compare/v1.9.0...v1.10.0) (2024-10-13)
|
||||
|
||||
|
||||
|
||||
@@ -34,15 +147,6 @@
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Follow Character on Map and auto select their current system
|
||||
|
||||
## [v1.4.0](https://github.com/wanderer-industries/wanderer/compare/v1.3.6...v1.4.0) (2024-10-11)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Follow Character on Map and auto select their current system
|
||||
@@ -56,35 +160,6 @@
|
||||
|
||||
* Signatures: Signatures update fixes
|
||||
|
||||
## [v1.3.5](https://github.com/wanderer-industries/wanderer/compare/v1.3.4...v1.3.5) (2024-10-09)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Signatures: Signatures update fixes
|
||||
|
||||
## [v1.3.4](https://github.com/wanderer-industries/wanderer/compare/v1.3.3...v1.3.4) (2024-10-09)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.3.3](https://github.com/wanderer-industries/wanderer/compare/v1.3.2...v1.3.3) (2024-10-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.3.2](https://github.com/wanderer-industries/wanderer/compare/v1.3.1...v1.3.2) (2024-10-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.3.1](https://github.com/wanderer-industries/wanderer/compare/v1.3.0...v1.3.1) (2024-10-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.3.0](https://github.com/wanderer-industries/wanderer/compare/v1.2.10...v1.3.0) (2024-10-07)
|
||||
|
||||
|
||||
@@ -98,26 +173,6 @@
|
||||
|
||||
* Map: Revision of sorting from also adding ability to sort all columns
|
||||
|
||||
## [v1.2.10](https://github.com/wanderer-industries/wanderer/compare/v1.2.9...v1.2.10) (2024-10-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.2.9](https://github.com/wanderer-industries/wanderer/compare/v1.2.8...v1.2.9) (2024-10-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.2.8](https://github.com/wanderer-industries/wanderer/compare/v1.2.7...v1.2.8) (2024-10-06)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.2.7](https://github.com/wanderer-industries/wanderer/compare/v1.2.6...v1.2.7) (2024-10-05)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.2.6](https://github.com/wanderer-industries/wanderer/compare/v1.2.5...v1.2.6) (2024-10-05)
|
||||
|
||||
|
||||
@@ -154,11 +209,6 @@
|
||||
|
||||
* Map: Fix map loading after select a different map.
|
||||
|
||||
## [v1.2.2](https://github.com/wanderer-industries/wanderer/compare/v1.2.1...v1.2.2) (2024-10-02)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.2.1](https://github.com/wanderer-industries/wanderer/compare/v1.2.0...v1.2.1) (2024-10-02)
|
||||
|
||||
|
||||
@@ -275,20 +325,10 @@
|
||||
|
||||
* docker: Fix DB connection in docker-compose internal network
|
||||
|
||||
## [v1.0.7](https://github.com/wanderer-industries/wanderer/compare/v1.0.6...v1.0.7) (2024-09-19)
|
||||
|
||||
## [v1.0.6](https://github.com/wanderer-industries/wanderer/compare/v1.0.5...v1.0.6) (2024-09-18)
|
||||
|
||||
## [v1.0.5](https://github.com/wanderer-industries/wanderer/compare/v1.0.4...v1.0.5) (2024-09-18)
|
||||
|
||||
## [v1.0.4](https://github.com/wanderer-industries/wanderer/compare/v1.0.3...v1.0.4) (2024-09-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* core: skip search results for failed character info request
|
||||
|
||||
## [v1.0.3](https://github.com/wanderer-industries/wanderer/compare/v1.0.2...v1.0.3) (2024-09-18)
|
||||
|
||||
## [v1.0.2](https://github.com/wanderer-industries/wanderer/compare/v1.0.1...v1.0.2) (2024-09-18)
|
||||
|
||||
## [v1.0.1](https://github.com/wanderer-industries/wanderer/compare/v1.0.0...v1.0.1) (2024-09-18)
|
||||
|
||||
18
README.md
18
README.md
@@ -20,11 +20,11 @@ Interested to learn more? [Check more on our website](https://wanderer.ltd/news)
|
||||
|
||||
Wanderer is open source project and we have a free as in beer and self-hosted solution called [Wanderer Community Edition (CE)](https://wanderer.ltd/news/community-edition). Here are the differences between Wanderer and Wanderer CE:
|
||||
|
||||
| | Wanderer Cloud | Wanderer Community Edition |
|
||||
| ------------- | ------------- | ------------- |
|
||||
| **Infrastructure management** | Easy and convenient. It takes 2 minutes to register your character and create a map. We manage everything so you don’t have to worry about anything and can focus on gameplay. | You do it all yourself. You need to get a server and you need to manage your infrastructure. You are responsible for installation, maintenance, upgrades, server capacity, uptime, backup, security, stability, consistency, loading time and so on.|
|
||||
| **Release schedule** | Continuously developed and improved with new features and updates multiple times per week. | Latest features and improvements won't be immediately available.|
|
||||
| **Server location** | All visitor data is exclusively processed on EU-owned cloud infrastructure. We keep your site data on a secure, encrypted and green energy powered server in Germany. This ensures that your site data is protected by the strict European Union data privacy laws and ensures compliance with GDPR. Your website data never leaves the EU. | You have full control and can host your instance on any server in any country that you wish. Host it on a server in your basement or host it with any cloud provider wherever you want, even those that are not GDPR compliant.|
|
||||
| | Wanderer Cloud | Wanderer Community Edition |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Infrastructure management** | Easy and convenient. It takes 2 minutes to register your character and create a map. We manage everything so you don’t have to worry about anything and can focus on gameplay. | You do it all yourself. You need to get a server and you need to manage your infrastructure. You are responsible for installation, maintenance, upgrades, server capacity, uptime, backup, security, stability, consistency, loading time and so on. |
|
||||
| **Release schedule** | Continuously developed and improved with new features and updates multiple times per week. | Latest features and improvements won't be immediately available. |
|
||||
| **Server location** | All visitor data is exclusively processed on EU-owned cloud infrastructure. We keep your site data on a secure, encrypted and green energy powered server in Germany. This ensures that your site data is protected by the strict European Union data privacy laws and ensures compliance with GDPR. Your website data never leaves the EU. | You have full control and can host your instance on any server in any country that you wish. Host it on a server in your basement or host it with any cloud provider wherever you want, even those that are not GDPR compliant. |
|
||||
|
||||
Interested in self-hosting Wanderer CE on your server? Take a look at our [Wanderer CE installation instructions](https://github.com/wanderer-industries/community-edition/).
|
||||
|
||||
@@ -54,7 +54,13 @@ Now you can visit [`localhost:8000`](http://localhost:8000) from your browser.
|
||||
#### Using .devcontainer
|
||||
|
||||
- Run devcontainer
|
||||
- See how to start server in #setup section
|
||||
- Install additional dependencies inside Dev container
|
||||
- `root@0d0a785313b6:/app# apt update`
|
||||
- `root@0d0a785313b6:/app# curl -sL https://deb.nodesource.com/setup_18.x | bash -`
|
||||
- `root@0d0a785313b6:/app# apt-get install nodejs inotify-tools -y`
|
||||
- `root@0d0a785313b6:/app# mix setup`
|
||||
|
||||
- See how to run server in #Run section
|
||||
|
||||
#### Using nix flakes
|
||||
|
||||
|
||||
@@ -85,3 +85,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
.p-dropdown-label, .p-inputtext {
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.p-dropdown-item {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.p-dropdown-item-group {
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.p-dropdown-trigger {
|
||||
width: 14px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.p-dropdown-empty-message {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Основной класс диалога */
|
||||
.p-dialog {
|
||||
body .p-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//position: absolute;
|
||||
@@ -7,11 +7,26 @@
|
||||
left: 0;
|
||||
//visibility: hidden;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2);
|
||||
transition: box-shadow 0.3s;
|
||||
background: #fff;
|
||||
z-index: 1000;
|
||||
border: 1px solid #212121;
|
||||
background: var(--surface-h);
|
||||
color: var(--text-color);
|
||||
|
||||
.p-dialog-header {
|
||||
background: #171717 !important;
|
||||
color: var(--text-color);
|
||||
|
||||
.p-dialog-header-icon:focus-visible {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.p-dialog-footer {
|
||||
border-top: 1px solid var(--surface-d);
|
||||
}
|
||||
}
|
||||
|
||||
/* Стиль видимого диалога */
|
||||
@@ -45,12 +60,12 @@
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
background: #f4f4f4;
|
||||
border-bottom: 1px solid #ddd;
|
||||
//border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Содержимое диалога */
|
||||
.p-dialog-content {
|
||||
padding: 1rem;
|
||||
padding: 0.5rem;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
@@ -78,23 +93,3 @@
|
||||
.p-dialog-header-close .pi {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* Тема Saga Blue (пример) */
|
||||
body .p-dialog {
|
||||
background: var(--surface-a);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
body .p-dialog .p-dialog-header,
|
||||
body .p-dialog .p-dialog-footer {
|
||||
background: var(--surface-b);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
body .p-dialog .p-dialog-header {
|
||||
border-bottom: 1px solid var(--surface-d);
|
||||
}
|
||||
|
||||
body .p-dialog .p-dialog-footer {
|
||||
border-top: 1px solid var(--surface-d);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
--surface-d: #3f4b5b;
|
||||
--surface-e: #2a323d;
|
||||
--surface-f: #2a323d;
|
||||
--surface-h: #171717;
|
||||
--text-color: rgba(255, 255, 255, 0.87);
|
||||
--text-color-secondary: rgba(255, 255, 255, 0.6);
|
||||
--primary-color: #8dd0ff;
|
||||
|
||||
@@ -46,7 +46,7 @@ export const useLabelsMenu = (
|
||||
}
|
||||
|
||||
// const labels = getLabels(system.labels);
|
||||
const hasLabels = labels.list.length > 0;
|
||||
const hasLabels = labels?.list?.length > 0;
|
||||
const statusList = hasLabels ? LABELS_ORDER : LABELS_ORDER.slice(1);
|
||||
|
||||
return [
|
||||
|
||||
@@ -18,7 +18,7 @@ export const useTagMenu = (
|
||||
ref.current = { onSystemTag, systems, systemId };
|
||||
|
||||
return useCallback(() => {
|
||||
const { onSystemTag, systemId , systems} = ref.current;
|
||||
const { onSystemTag, systemId, systems } = ref.current;
|
||||
const system = systemId ? getSystemById(systems, systemId) : undefined;
|
||||
|
||||
const isSelectedLetters = AVAILABLE_LETTERS.includes(system?.tag ?? '');
|
||||
|
||||
@@ -4,6 +4,7 @@ import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
|
||||
interface UseContextMenuSystemHandlersProps {
|
||||
hubs: string[];
|
||||
@@ -16,8 +17,10 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
|
||||
const [system, setSystem] = useState<string>();
|
||||
|
||||
const ref = useRef({ hubs, system, systems, outCommand });
|
||||
ref.current = { hubs, system, systems, outCommand };
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
|
||||
const ref = useRef({ hubs, system, systems, outCommand, deleteSystems });
|
||||
ref.current = { hubs, system, systems, outCommand, deleteSystems };
|
||||
|
||||
const open = useCallback((ev: any, systemId: string) => {
|
||||
setSystem(systemId);
|
||||
@@ -27,12 +30,12 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
}, []);
|
||||
|
||||
const onDeleteSystem = useCallback(() => {
|
||||
const { system, outCommand } = ref.current;
|
||||
const { system, deleteSystems } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({ type: OutCommand.deleteSystems, data: [system] });
|
||||
deleteSystems([system]);
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks/useJumpPlannerMenu';
|
||||
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { Route } from '@/hooks/Mapper/types/routes.ts';
|
||||
|
||||
export interface ContextMenuSystemInfoProps {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Node } from 'reactflow';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
|
||||
export const useContextMenuSystemMultipleHandlers = () => {
|
||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||
const { outCommand } = useMapRootState();
|
||||
const [systems, setSystems] = useState<Node<SolarSystemRawType>[]>();
|
||||
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
|
||||
const handleSystemMultipleContext: NodeSelectionMouseHandler = (ev, systems_) => {
|
||||
setSystems(systems_);
|
||||
ev.preventDefault();
|
||||
@@ -19,7 +19,7 @@ export const useContextMenuSystemMultipleHandlers = () => {
|
||||
contextMenuRef.current?.show(ev);
|
||||
};
|
||||
|
||||
const onDeleteSystems = () => {
|
||||
const onDeleteSystems = useCallback(() => {
|
||||
if (!systems) {
|
||||
return;
|
||||
}
|
||||
@@ -29,12 +29,11 @@ export const useContextMenuSystemMultipleHandlers = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({ type: OutCommand.deleteSystems, data: sysToDel });
|
||||
};
|
||||
deleteSystems(sysToDel);
|
||||
}, [deleteSystems, systems]);
|
||||
|
||||
return {
|
||||
handleSystemMultipleContext,
|
||||
|
||||
contextMenuRef,
|
||||
onDeleteSystems,
|
||||
};
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './useWaypointMenu';
|
||||
export * from './useJumpPlannerMenu';
|
||||
export * from './useDeleteSystems';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
export const useDeleteSystems = () => {
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
const deleteSystems = (systemIds: string[]) => {
|
||||
if (!systemIds || !systemIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({ type: OutCommand.deleteSystems, data: systemIds });
|
||||
};
|
||||
|
||||
return {
|
||||
deleteSystems,
|
||||
};
|
||||
};
|
||||
2
assets/js/hooks/Mapper/components/hooks/index.ts
Normal file
2
assets/js/hooks/Mapper/components/hooks/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './useSystemInfo';
|
||||
export * from './useGetOwnOnlineCharacters';
|
||||
33
assets/js/hooks/Mapper/components/hooks/useSystemInfo.ts
Normal file
33
assets/js/hooks/Mapper/components/hooks/useSystemInfo.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMemo } from 'react';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
|
||||
interface UseSystemInfoProps {
|
||||
systemId: string;
|
||||
}
|
||||
|
||||
export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
|
||||
const {
|
||||
data: { systems, connections },
|
||||
} = useMapRootState();
|
||||
|
||||
const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] });
|
||||
|
||||
return useMemo(() => {
|
||||
const staticInfo = systemStatics.get(parseInt(systemId));
|
||||
const dynamicInfo = getSystemById(systems, systemId);
|
||||
|
||||
if (!staticInfo || !dynamicInfo) {
|
||||
throw new Error(`Error on getting system ${systemId}`);
|
||||
}
|
||||
|
||||
const leadsTo = connections
|
||||
.filter(x => [x.source, x.target].includes(systemId))
|
||||
.map(x => [x.source, x.target])
|
||||
.flat()
|
||||
.filter(x => x !== systemId);
|
||||
|
||||
return { dynamicInfo, staticInfo, leadsTo };
|
||||
}, [systemStatics, systemId, systems, connections]);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect } from 'react';
|
||||
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useRef } from 'react';
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
ConnectionMode,
|
||||
@@ -13,12 +13,15 @@ import ReactFlow, {
|
||||
SelectionMode,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
NodeChange,
|
||||
useReactFlow,
|
||||
} from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
import classes from './Map.module.scss';
|
||||
import './styles/neon-theme.scss';
|
||||
import './styles/eve-common.scss';
|
||||
import { MapProvider, useMapState } from './MapProvider';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapHandlers, useUpdateNodes } from './hooks';
|
||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import {
|
||||
@@ -34,6 +37,7 @@ import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
|
||||
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
||||
|
||||
@@ -108,6 +112,7 @@ const MapComp = ({
|
||||
isShowMinimap,
|
||||
showKSpaceBG,
|
||||
}: MapCompProps) => {
|
||||
const { getNode } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges);
|
||||
|
||||
@@ -115,8 +120,15 @@ const MapComp = ({
|
||||
useUpdateNodes(nodes);
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
|
||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||
|
||||
const { update } = useMapState();
|
||||
const {
|
||||
data: { systems },
|
||||
} = useMapRootState();
|
||||
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
|
||||
const systemsRef = useRef({ systems });
|
||||
systemsRef.current = { systems };
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
params => {
|
||||
@@ -171,6 +183,32 @@ const MapComp = ({
|
||||
localStorage.setItem(SESSION_KEY.viewPort, JSON.stringify(viewport));
|
||||
};
|
||||
|
||||
const handleNodesChange = useCallback(
|
||||
(changes: NodeChange[]) => {
|
||||
const systemsIdsToRemove: string[] = [];
|
||||
const nextChanges = changes.reduce((acc, change) => {
|
||||
if (change.type === 'remove') {
|
||||
const node = getNode(change.id);
|
||||
const { systems = [] } = systemsRef.current;
|
||||
if (node?.data?.id && !systems.map(s => s.id).includes(node?.data?.id)) {
|
||||
return [...acc, change];
|
||||
} else if (!node?.data?.locked) {
|
||||
systemsIdsToRemove.push(node?.data?.id);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
return [...acc, change];
|
||||
}, [] as NodeChange[]);
|
||||
|
||||
if (systemsIdsToRemove.length) {
|
||||
deleteSystems(systemsIdsToRemove);
|
||||
}
|
||||
|
||||
onNodesChange(nextChanges);
|
||||
},
|
||||
[deleteSystems, getNode, onNodesChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
update(x => ({
|
||||
...x,
|
||||
@@ -184,7 +222,7 @@ const MapComp = ({
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
// TODO we need save into session all of this
|
||||
@@ -219,10 +257,10 @@ const MapComp = ({
|
||||
minZoom={0.2}
|
||||
maxZoom={1.5}
|
||||
elevateNodesOnSelect
|
||||
deleteKeyCode={['Delete']}
|
||||
// TODO need create clear example with problem with that flag
|
||||
// if system is not visible edge not drawing (and any render in Custom node is not happening)
|
||||
// onlyRenderVisibleElements
|
||||
deleteKeyCode={null}
|
||||
selectionMode={SelectionMode.Partial}
|
||||
>
|
||||
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "@/hooks/Mapper/components/map/styles/eve-common-variables";
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
@@ -25,9 +25,11 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
&.Mataria, &.Amarria, &.Gallente, &.Caldaria {
|
||||
&::Before {
|
||||
&.Mataria,
|
||||
&.Amarria,
|
||||
&.Gallente,
|
||||
&.Caldaria {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -44,42 +46,40 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
|
||||
&.Mataria {
|
||||
&::before {
|
||||
background-image: url("/images/mataria.png");
|
||||
background-image: url('/images/mataria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: -28px;
|
||||
background-position-y: -3px;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Caldaria {
|
||||
&::before {
|
||||
background-image: url("/images/caldaria.png");
|
||||
background-image: url('/images/caldaria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: -16px;
|
||||
background-position-y: -17px;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Amarria {
|
||||
&::before {
|
||||
opacity: 0.45;
|
||||
background-image: url("/images/amarr.png");
|
||||
background-position-x: 0px;
|
||||
background-position-y: -1px;
|
||||
width: calc(100% + 10px)
|
||||
background-image: url('/images/amarr-180.png');
|
||||
background-position-x: 0;
|
||||
background-position-y: -13px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Gallente {
|
||||
&::before {
|
||||
opacity: 0.6;
|
||||
background-image: url("/images/gallente.png");
|
||||
background-position-x: -1px;
|
||||
background-position-y: -10px;
|
||||
opacity: 0.5;
|
||||
background-image: url('/images/gallente-180.png');
|
||||
background-position-x: 1px;
|
||||
background-position-y: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
@@ -95,7 +95,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
|
||||
&.eve-system-status-home {
|
||||
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
|
||||
background-image: linear-gradient(45deg, $eve-solar-system-status-friendly, transparent);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
|
||||
|
||||
&.selected {
|
||||
border-color: $eve-solar-system-status-color-home;
|
||||
@@ -104,7 +104,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
|
||||
&.eve-system-status-friendly {
|
||||
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
|
||||
background-image: linear-gradient(45deg, darken($eve-solar-system-status-friendly, 30%), transparent);
|
||||
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
|
||||
|
||||
&.selected {
|
||||
border-color: darken($eve-solar-system-status-color-friendly, 5%);
|
||||
@@ -113,7 +113,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
|
||||
&.eve-system-status-lookingFor {
|
||||
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
|
||||
background-image: linear-gradient(45deg, #45ff8f2f, #457fff2f);
|
||||
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
@@ -121,17 +121,16 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
}
|
||||
|
||||
&.eve-system-status-warning {
|
||||
background-image: linear-gradient(45deg, $eve-solar-system-status-warning, transparent);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
|
||||
}
|
||||
|
||||
&.eve-system-status-dangerous {
|
||||
background-image: linear-gradient(45deg, $eve-solar-system-status-dangerous, transparent);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
|
||||
}
|
||||
|
||||
&.eve-system-status-target {
|
||||
background-image: linear-gradient(45deg, $eve-solar-system-status-target, transparent);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.Bookmarks {
|
||||
@@ -158,7 +157,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
//background-color: #833ca4;
|
||||
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, .3);
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +180,6 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.icon {
|
||||
@@ -219,9 +217,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
}
|
||||
|
||||
.solarSystemName {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.BottomRow {
|
||||
@@ -288,11 +284,19 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
border-color: $pastel-pink;
|
||||
}
|
||||
|
||||
&.HandleTop { top: -2px }
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&.HandleRight { right: -2px }
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
&.HandleBottom { bottom: -2px }
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
&.HandleLeft { left: -2px }
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ const SpaceToClass: Record<string, string> = {
|
||||
};
|
||||
|
||||
const sortedLabels = (labels: string[]) => {
|
||||
if (labels === null) {
|
||||
if (!labels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
<div className={classes.Bookmarks}>
|
||||
{labelCustom !== '' && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
|
||||
<div>{labelCustom}</div>
|
||||
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -168,14 +168,16 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
{visible && (
|
||||
<>
|
||||
<div className={classes.HeadRow}>
|
||||
<div className={clsx(classes.classTitle, classTitleColor)}>{class_title ?? '-'}</div>
|
||||
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
|
||||
{class_title ?? '-'}
|
||||
</div>
|
||||
{tag != null && tag !== '' && (
|
||||
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{tag}</div>
|
||||
)}
|
||||
<div
|
||||
className={clsx(
|
||||
classes.classSystemName,
|
||||
'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||
)}
|
||||
>
|
||||
{solar_system_name}
|
||||
@@ -196,16 +198,16 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||
{customName && (
|
||||
<div className="text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">{customName}</div>
|
||||
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
|
||||
{customName}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && (
|
||||
<div
|
||||
className={clsx('text-stone-400 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5', {
|
||||
['text-teal-100 font-bold']: space === Spaces.Caldari,
|
||||
['text-yellow-100 font-bold']: space === Spaces.Amarr || space === Spaces.Matar,
|
||||
['text-lime-200/80 font-bold']: space === Spaces.Gallente,
|
||||
})}
|
||||
className={clsx(
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||
)}
|
||||
>
|
||||
{region_name}
|
||||
</div>
|
||||
@@ -215,10 +217,10 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
|
||||
<div className="flex items-center justify-end">
|
||||
<div className="flex gap-1 items-center">
|
||||
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem' }}></i>}
|
||||
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>}
|
||||
|
||||
{hubs.includes(solar_system_id.toString()) && (
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem' }}></i>
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
|
||||
)}
|
||||
|
||||
{charactersInSystem.length > 0 && (
|
||||
|
||||
@@ -18,5 +18,9 @@ export const WormholeClassComp = ({ id }: WormholeClassComp) => {
|
||||
}
|
||||
|
||||
const colorClass = WORMHOLE_CLASS_STYLES[wormholeDataAdditional.wormholeClassID.toString()];
|
||||
return <div className={clsx(colorClass)}>{wormholeDataAdditional.shortName}</div>;
|
||||
return (
|
||||
<div className={clsx(colorClass, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
|
||||
{wormholeDataAdditional.shortName}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,11 +30,77 @@ export enum SOLAR_SYSTEM_CLASS_IDS {
|
||||
zarzakh = 10100,
|
||||
}
|
||||
|
||||
export enum SOLAR_SYSTEM_CLASS_GROUPS {
|
||||
ccp = 'ccp',
|
||||
c1 = 'c1',
|
||||
c2 = 'c2',
|
||||
c3 = 'c3',
|
||||
c4 = 'c4',
|
||||
c5 = 'c5',
|
||||
c6 = 'c6',
|
||||
hs = 'hs',
|
||||
ls = 'ls',
|
||||
ns = 'ns',
|
||||
thera = 'thera',
|
||||
c13 = 'c13',
|
||||
drifter = 'drifter',
|
||||
unknown = 'unknown',
|
||||
pochven = 'pochven',
|
||||
jovian = 'jovian',
|
||||
}
|
||||
|
||||
export const SOLAR_SYSTEM_TO_CLASS_GROUPS_CLASSES = {
|
||||
c1: ['c1'],
|
||||
c2: ['c2'],
|
||||
c3: ['c3'],
|
||||
c4: ['c4'],
|
||||
c5: ['c5'],
|
||||
c6: ['c6'],
|
||||
hs: ['hs'],
|
||||
ls: ['ls'],
|
||||
ns: ['ns'],
|
||||
thera: ['thera'],
|
||||
c13: ['c13'],
|
||||
pochven: ['pochven'],
|
||||
drifter: ['sentinel', 'barbican', 'vidette', 'conflux', 'redoubt'],
|
||||
jove: ['jove'],
|
||||
};
|
||||
|
||||
export const SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS = {
|
||||
ccp1: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
|
||||
c1: SOLAR_SYSTEM_CLASS_GROUPS.c1,
|
||||
c2: SOLAR_SYSTEM_CLASS_GROUPS.c2,
|
||||
c3: SOLAR_SYSTEM_CLASS_GROUPS.c3,
|
||||
c4: SOLAR_SYSTEM_CLASS_GROUPS.c4,
|
||||
c5: SOLAR_SYSTEM_CLASS_GROUPS.c5,
|
||||
c6: SOLAR_SYSTEM_CLASS_GROUPS.c6,
|
||||
hs: SOLAR_SYSTEM_CLASS_GROUPS.hs,
|
||||
ls: SOLAR_SYSTEM_CLASS_GROUPS.ls,
|
||||
ns: SOLAR_SYSTEM_CLASS_GROUPS.ns,
|
||||
ccp2: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
|
||||
ccp3: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
|
||||
thera: SOLAR_SYSTEM_CLASS_GROUPS.thera,
|
||||
c13: SOLAR_SYSTEM_CLASS_GROUPS.c13,
|
||||
sentinel: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
|
||||
baribican: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
|
||||
vidette: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
|
||||
conflux: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
|
||||
redoubt: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
|
||||
a1: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
|
||||
a2: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
|
||||
a3: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
|
||||
a4: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
|
||||
a5: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
|
||||
ccp4: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
|
||||
pochven: SOLAR_SYSTEM_CLASS_GROUPS.pochven,
|
||||
};
|
||||
|
||||
type WormholesAdditionalInfoType = {
|
||||
id: string;
|
||||
shortName: string;
|
||||
wormholeClassID: number;
|
||||
title: string;
|
||||
shortTitle: string;
|
||||
effectPower?: number;
|
||||
};
|
||||
|
||||
@@ -45,6 +111,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
shortName: 'CCP',
|
||||
wormholeClassID: -1,
|
||||
title: 'CCP System',
|
||||
shortTitle: 'CCP',
|
||||
},
|
||||
{
|
||||
id: 'c1',
|
||||
@@ -52,6 +119,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 1,
|
||||
effectPower: 1,
|
||||
title: 'Class 1',
|
||||
shortTitle: 'C1',
|
||||
},
|
||||
{
|
||||
id: 'c2',
|
||||
@@ -59,6 +127,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 2,
|
||||
effectPower: 2,
|
||||
title: 'Class 2',
|
||||
shortTitle: 'C2',
|
||||
},
|
||||
{
|
||||
id: 'c3',
|
||||
@@ -66,6 +135,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 3,
|
||||
effectPower: 3,
|
||||
title: 'Class 3',
|
||||
shortTitle: 'C3',
|
||||
},
|
||||
{
|
||||
id: 'c4',
|
||||
@@ -73,6 +143,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 4,
|
||||
effectPower: 4,
|
||||
title: 'Class 4',
|
||||
shortTitle: 'C4',
|
||||
},
|
||||
{
|
||||
id: 'c5',
|
||||
@@ -80,6 +151,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 5,
|
||||
effectPower: 5,
|
||||
title: 'Class 5',
|
||||
shortTitle: 'C5',
|
||||
},
|
||||
{
|
||||
id: 'c6',
|
||||
@@ -87,42 +159,49 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 6,
|
||||
effectPower: 6,
|
||||
title: 'Class 6',
|
||||
shortTitle: 'C6',
|
||||
},
|
||||
{
|
||||
id: 'hs',
|
||||
shortName: 'H',
|
||||
wormholeClassID: 7,
|
||||
title: 'High-sec',
|
||||
shortTitle: 'High-sec',
|
||||
},
|
||||
{
|
||||
id: 'ls',
|
||||
shortName: 'L',
|
||||
wormholeClassID: 8,
|
||||
title: 'Low-sec',
|
||||
shortTitle: 'Low-sec',
|
||||
},
|
||||
{
|
||||
id: 'ns',
|
||||
shortName: 'N',
|
||||
wormholeClassID: 9,
|
||||
title: 'Null-sec',
|
||||
shortTitle: 'Null-sec',
|
||||
},
|
||||
{
|
||||
id: 'ccp2',
|
||||
shortName: 'CCP',
|
||||
wormholeClassID: 10,
|
||||
title: 'CCP System',
|
||||
shortTitle: 'CCP',
|
||||
},
|
||||
{
|
||||
id: 'ccp3',
|
||||
shortName: 'CCP',
|
||||
wormholeClassID: 11,
|
||||
title: 'CCP System',
|
||||
shortTitle: 'CCP',
|
||||
},
|
||||
{
|
||||
id: 'thera',
|
||||
shortName: 'T',
|
||||
wormholeClassID: 12,
|
||||
title: 'Class 12 (Thera)',
|
||||
shortTitle: 'Thera',
|
||||
},
|
||||
{
|
||||
id: 'c13',
|
||||
@@ -130,6 +209,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 13,
|
||||
effectPower: 6,
|
||||
title: 'Class 13 (Shattered Frigate)',
|
||||
shortTitle: 'C13',
|
||||
},
|
||||
{
|
||||
id: 'sentinel',
|
||||
@@ -137,6 +217,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 14,
|
||||
effectPower: 2,
|
||||
title: 'Class 14 (Sentinel Drifter)',
|
||||
shortTitle: 'Sentinel',
|
||||
},
|
||||
{
|
||||
id: 'barbican',
|
||||
@@ -144,6 +225,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 15,
|
||||
effectPower: 2,
|
||||
title: 'Class 15 (Barbican Drifter)',
|
||||
shortTitle: 'Barbican',
|
||||
},
|
||||
{
|
||||
id: 'vidette',
|
||||
@@ -151,6 +233,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 16,
|
||||
effectPower: 2,
|
||||
title: 'Class 16 (Vidette Drifter)',
|
||||
shortTitle: 'Vidette',
|
||||
},
|
||||
{
|
||||
id: 'conflux',
|
||||
@@ -158,6 +241,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 17,
|
||||
effectPower: 2,
|
||||
title: 'Class 17 (Conflux Drifter)',
|
||||
shortTitle: 'Conflux',
|
||||
},
|
||||
{
|
||||
id: 'redoubt',
|
||||
@@ -165,59 +249,79 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
|
||||
wormholeClassID: 18,
|
||||
effectPower: 2,
|
||||
title: 'Class 18 (Redoubt Drifter)',
|
||||
shortTitle: 'Redoubt',
|
||||
},
|
||||
{
|
||||
id: 'a1',
|
||||
shortName: 'A1',
|
||||
wormholeClassID: 19,
|
||||
title: '(Abyssal class 1)',
|
||||
shortTitle: 'A1',
|
||||
},
|
||||
{
|
||||
id: 'a2',
|
||||
shortName: 'A2',
|
||||
wormholeClassID: 20,
|
||||
title: '(Abyssal class 2)',
|
||||
shortTitle: 'A2',
|
||||
},
|
||||
{
|
||||
id: 'a3',
|
||||
shortName: 'A3',
|
||||
wormholeClassID: 21,
|
||||
title: '(Abyssal class 3)',
|
||||
shortTitle: 'A3',
|
||||
},
|
||||
{
|
||||
id: 'a4',
|
||||
shortName: 'A4',
|
||||
wormholeClassID: 22,
|
||||
title: '(Abyssal class 4)',
|
||||
shortTitle: 'A4',
|
||||
},
|
||||
{
|
||||
id: 'a5',
|
||||
shortName: 'A5',
|
||||
wormholeClassID: 23,
|
||||
title: '(Abyssal class 5)',
|
||||
shortTitle: 'A5',
|
||||
},
|
||||
{
|
||||
id: 'ccp4',
|
||||
shortName: 'CCP',
|
||||
wormholeClassID: 24,
|
||||
title: 'CCP System (Penalty)',
|
||||
shortTitle: 'CCP',
|
||||
},
|
||||
{
|
||||
id: 'pochven',
|
||||
shortName: 'P',
|
||||
wormholeClassID: 25,
|
||||
title: 'Triglavian space (Pochven)',
|
||||
shortTitle: 'Pochven',
|
||||
},
|
||||
{
|
||||
id: 'zarzakh',
|
||||
shortName: 'N',
|
||||
wormholeClassID: 10100,
|
||||
title: 'Pirate space',
|
||||
shortTitle: 'Zarzakh',
|
||||
},
|
||||
{
|
||||
id: 'k162',
|
||||
shortName: 'K162',
|
||||
wormholeClassID: 10101,
|
||||
title: 'Reverse',
|
||||
shortTitle: 'K162',
|
||||
},
|
||||
];
|
||||
|
||||
export const WORMHOLES_ADDITIONAL_INFO: Record<string, WormholesAdditionalInfoType> =
|
||||
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.id]: x }), {});
|
||||
|
||||
export const WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID: Record<string, WormholesAdditionalInfoType> =
|
||||
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.wormholeClassID]: x }), {});
|
||||
|
||||
// export const SOLAR_SYSTEM_CLASS_NAMES = {
|
||||
// ccp1 = ,
|
||||
// c1 = ,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandCenterSystem } from '@/hooks/Mapper/types';
|
||||
|
||||
export const useCenterSystem = () => {
|
||||
const rf = useReactFlow();
|
||||
|
||||
const ref = useRef({ rf });
|
||||
ref.current = { rf };
|
||||
|
||||
return useCallback((systemId: CommandCenterSystem) => {
|
||||
if (!rf) {
|
||||
return;
|
||||
}
|
||||
const systemNode = rf.getNodes().find(x => x.data.id === systemId);
|
||||
const systemNode = ref.current.rf.getNodes().find(x => x.data.id === systemId);
|
||||
if (!systemNode) {
|
||||
return;
|
||||
}
|
||||
rf.setCenter(systemNode.position.x, systemNode.position.y, { duration: 1000 });
|
||||
ref.current.rf.setCenter(systemNode.position.x, systemNode.position.y, { duration: 1000 });
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -2,28 +2,17 @@ import { Node, useReactFlow } from 'reactflow';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandAddSystems } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { convertSystem2Node } from '../../helpers';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
|
||||
export const useMapAddSystems = () => {
|
||||
const rf = useReactFlow();
|
||||
const {
|
||||
data: { systems },
|
||||
update,
|
||||
} = useMapState();
|
||||
|
||||
const ref = useRef({ rf, systems, update });
|
||||
ref.current = { update, systems, rf };
|
||||
const ref = useRef({ rf });
|
||||
ref.current = { rf };
|
||||
|
||||
return useCallback(
|
||||
(systems: CommandAddSystems) => {
|
||||
const nodes = rf.getNodes();
|
||||
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
|
||||
rf.addNodes(prepared);
|
||||
|
||||
ref.current.update({
|
||||
systems: [...ref.current.systems.filter(sys => systems.some(x => sys.id !== x.id)), ...systems],
|
||||
});
|
||||
},
|
||||
[rf],
|
||||
);
|
||||
return useCallback((systems: CommandAddSystems) => {
|
||||
const { rf } = ref.current;
|
||||
const nodes = rf.getNodes();
|
||||
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
|
||||
rf.addNodes(prepared);
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandSelectSystem } from '@/hooks/Mapper/types';
|
||||
|
||||
export const useSelectSystem = () => {
|
||||
const rf = useReactFlow();
|
||||
|
||||
const ref = useRef({ rf });
|
||||
ref.current = { rf };
|
||||
|
||||
return useCallback((systemId: CommandSelectSystem) => {
|
||||
if (!rf) {
|
||||
return;
|
||||
}
|
||||
rf.setNodes(nds =>
|
||||
ref.current.rf.setNodes(nds =>
|
||||
nds.map(node => {
|
||||
return {
|
||||
...node,
|
||||
|
||||
@@ -18,6 +18,9 @@ import {
|
||||
CommandUpdateSystems,
|
||||
MapHandlers,
|
||||
} from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
|
||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||
|
||||
import {
|
||||
useCommandsCharacters,
|
||||
useCommandsConnections,
|
||||
@@ -57,16 +60,13 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
mapInit(data as CommandInit);
|
||||
break;
|
||||
case Commands.addSystems:
|
||||
mapAddSystems(data as CommandAddSystems);
|
||||
break;
|
||||
case Commands.updateSystems:
|
||||
mapUpdateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems:
|
||||
removeSystems(data as CommandRemoveSystems);
|
||||
break;
|
||||
case Commands.addConnections:
|
||||
addConnections(data as CommandAddConnections);
|
||||
break;
|
||||
case Commands.removeConnections:
|
||||
removeConnections(data as CommandRemoveConnections);
|
||||
@@ -131,4 +131,20 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useMapEventListener(event => {
|
||||
switch (event.name) {
|
||||
case Commands.addConnections:
|
||||
addConnections(event.data as CommandAddConnections);
|
||||
break;
|
||||
case Commands.addSystems:
|
||||
mapAddSystems(event.data as CommandAddSystems);
|
||||
break;
|
||||
case Commands.removeSystems:
|
||||
removeSystems(event.data as CommandRemoveSystems);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Button } from 'primereact/button';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
|
||||
import { WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { WdImageSize, WdImgButton, TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
interface SystemCustomLabelDialog {
|
||||
systemId: string;
|
||||
@@ -79,14 +79,14 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
|
||||
|
||||
// @ts-ignore
|
||||
const handleInput = useCallback(e => {
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '');
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="Edit label"
|
||||
visible={visible}
|
||||
draggable={false}
|
||||
draggable={true}
|
||||
style={{ width: '250px' }}
|
||||
onHide={onHide}
|
||||
onShow={onShow}
|
||||
@@ -100,9 +100,13 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
|
||||
<IconField>
|
||||
{label !== '' && (
|
||||
<WdImgButton
|
||||
className="pi pi-trash p-input-icon"
|
||||
className="pi pi-trash text-red-400"
|
||||
textSize={WdImageSize.large}
|
||||
tooltip={{ content: 'Reset label' }}
|
||||
tooltip={{
|
||||
content: 'Remove custom label',
|
||||
className: 'pi p-input-icon',
|
||||
position: TooltipPosition.top,
|
||||
}}
|
||||
onClick={handleReset}
|
||||
/>
|
||||
)}
|
||||
@@ -111,7 +115,7 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
|
||||
aria-describedby="username-help"
|
||||
autoComplete="off"
|
||||
value={label}
|
||||
maxLength={3}
|
||||
maxLength={5}
|
||||
onChange={e => setLabel(e.target.value)}
|
||||
// @ts-expect-error
|
||||
ref={inputRef}
|
||||
|
||||
@@ -21,8 +21,6 @@ const signatureSettings: Setting[] = [{ key: COSMIC_SIGNATURE, name: 'Show Cosmi
|
||||
export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => {
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
console.log(data);
|
||||
|
||||
const ref = useRef({ outCommand });
|
||||
ref.current = { outCommand };
|
||||
|
||||
@@ -31,8 +29,8 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
|
||||
}, [setVisible]);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(signatures: SystemSignature[]) => {
|
||||
if (!signatures.length) {
|
||||
(signature: SystemSignature) => {
|
||||
if (!signature) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,12 +40,12 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
|
||||
type: OutCommand.linkSignatureToSystem,
|
||||
data: {
|
||||
...data,
|
||||
signature_eve_id: signatures[0].eve_id,
|
||||
signature_eve_id: signature.eve_id,
|
||||
},
|
||||
});
|
||||
setVisible(false);
|
||||
},
|
||||
[setVisible],
|
||||
[data, setVisible],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -90,7 +90,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
}, []);
|
||||
|
||||
const handleInput = useCallback((e: any) => {
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '');
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -160,7 +160,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
aria-describedby="label"
|
||||
autoComplete="off"
|
||||
value={label}
|
||||
maxLength={3}
|
||||
maxLength={5}
|
||||
onChange={e => setLabel(e.target.value)}
|
||||
onInput={handleInput}
|
||||
/>
|
||||
|
||||
@@ -13,9 +13,10 @@ export const SystemInfoContent = ({ systemId }: SystemInfoContentProps) => {
|
||||
data: { systems, wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const sys = getSystemById(systems, systemId)!;
|
||||
const sys = getSystemById(systems, systemId)! || {};
|
||||
const { description } = sys;
|
||||
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } = sys.system_static_info;
|
||||
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } =
|
||||
sys.system_static_info || {};
|
||||
const isWH = isWormholeSpace(system_class);
|
||||
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
|
||||
import { parseSignatures } from '@/hooks/Mapper/helpers';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
import { DataTable, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
|
||||
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import useRefState from 'react-usestateref';
|
||||
@@ -22,12 +22,14 @@ import {
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
|
||||
import {
|
||||
renderIcon,
|
||||
renderName,
|
||||
renderInfoColumn,
|
||||
renderTimeLeft,
|
||||
renderLinkedSystem,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
// import { PrimeIcons } from 'primereact/api';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
|
||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
type SystemSignaturesSortSettings = {
|
||||
sortField: string;
|
||||
@@ -43,7 +45,7 @@ interface SystemSignaturesContentProps {
|
||||
systemId: string;
|
||||
settings: Setting[];
|
||||
selectable?: boolean;
|
||||
onSelect?: (signatures: SystemSignature[]) => void;
|
||||
onSelect?: (signature: SystemSignature) => void;
|
||||
}
|
||||
export const SystemSignaturesContent = ({ systemId, settings, selectable, onSelect }: SystemSignaturesContentProps) => {
|
||||
const { outCommand } = useMapRootState();
|
||||
@@ -53,6 +55,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
|
||||
const [parsedSignatures, setParsedSignatures] = useState<SystemSignature[]>([]);
|
||||
const [askUser, setAskUser] = useState(false);
|
||||
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
|
||||
|
||||
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
|
||||
|
||||
@@ -164,6 +167,8 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
}, [parsedSignatures, handleUpdateSignatures]);
|
||||
|
||||
const handleSelectSignatures = useCallback(
|
||||
// TODO still will be good to define types if we use typescript
|
||||
// @ts-ignore
|
||||
e => {
|
||||
if (selectable) {
|
||||
onSelect?.(e.value);
|
||||
@@ -212,6 +217,18 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
handleGetSignatures();
|
||||
}, [systemId]);
|
||||
|
||||
useMapEventListener(event => {
|
||||
switch (event.name) {
|
||||
case Commands.signaturesUpdated:
|
||||
if (event.data?.toString() !== systemId.toString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleGetSignatures();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new ResizeObserver(handleResize);
|
||||
if (tableRef.current) {
|
||||
@@ -240,13 +257,22 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
setHoveredSig(null);
|
||||
}, []);
|
||||
|
||||
// const renderToolbar = (/*row: SystemSignature*/) => {
|
||||
// return (
|
||||
// <div className="flex justify-end items-center gap-2">
|
||||
// <span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
const renderToolbar = (/*row: SystemSignature*/) => {
|
||||
return (
|
||||
<div className="flex justify-end items-center gap-2 mr-[4px]">
|
||||
<WdTooltipWrapper content="To Edit Signature do double click">
|
||||
<span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span>
|
||||
</WdTooltipWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const [showSignatureSettings, setShowSignatureSettings] = useState(false);
|
||||
|
||||
const handleRowClick = (e: DataTableRowClickEvent) => {
|
||||
setSelectedSignature(e.data as SystemSignature);
|
||||
setShowSignatureSettings(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -257,6 +283,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
<DataTable
|
||||
className={classes.Table}
|
||||
value={filteredSignatures}
|
||||
@@ -268,6 +295,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
dataKey="eve_id"
|
||||
tableClassName="w-full select-none"
|
||||
resizableColumns={false}
|
||||
onRowDoubleClick={handleRowClick}
|
||||
rowHover
|
||||
selectAll
|
||||
sortField={sortSettings.sortField}
|
||||
@@ -291,7 +319,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
<Column
|
||||
bodyClassName="p-0 px-1"
|
||||
field="group"
|
||||
body={renderIcon}
|
||||
body={x => renderIcon(x)}
|
||||
style={{ maxWidth: 26, minWidth: 26, width: 26, height: 25 }}
|
||||
></Column>
|
||||
|
||||
@@ -310,40 +338,29 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
sortable
|
||||
></Column>
|
||||
<Column
|
||||
field="name"
|
||||
header="Name"
|
||||
field="info"
|
||||
// header="Info"
|
||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderName}
|
||||
body={renderInfoColumn}
|
||||
style={{ maxWidth: nameColumnWidth }}
|
||||
hidden={compact || medium}
|
||||
sortable
|
||||
></Column>
|
||||
<Column
|
||||
field="linked_system"
|
||||
header="Linked System"
|
||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderLinkedSystem}
|
||||
style={{ maxWidth: nameColumnWidth }}
|
||||
hidden={compact}
|
||||
sortable
|
||||
></Column>
|
||||
|
||||
<Column
|
||||
field="updated_at"
|
||||
header="Updated"
|
||||
dataType="date"
|
||||
bodyClassName="w-[80px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderTimeLeft}
|
||||
sortable
|
||||
></Column>
|
||||
|
||||
{/*<Column*/}
|
||||
{/* bodyClassName="p-0 pl-1 pr-2"*/}
|
||||
{/* field="group"*/}
|
||||
{/* body={renderToolbar}*/}
|
||||
{/* headerClassName={headerClasses}*/}
|
||||
{/* style={{ maxWidth: 26, minWidth: 26, width: 26 }}*/}
|
||||
{/*></Column>*/}
|
||||
<Column
|
||||
bodyClassName="p-0 pl-1 pr-2"
|
||||
field="group"
|
||||
body={renderToolbar}
|
||||
// headerClassName={headerClasses}
|
||||
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
|
||||
></Column>
|
||||
</DataTable>
|
||||
</>
|
||||
)}
|
||||
@@ -352,6 +369,14 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
ref={tooltipRef}
|
||||
content={hoveredSig ? <SignatureView {...hoveredSig} /> : null}
|
||||
/>
|
||||
|
||||
<SignatureSettings
|
||||
systemId={systemId}
|
||||
show={showSignatureSettings}
|
||||
onHide={() => setShowSignatureSettings(false)}
|
||||
signatureData={selectedSignature}
|
||||
/>
|
||||
|
||||
{askUser && (
|
||||
<div className="absolute left-[1px] top-[29px] h-[calc(100%-30px)] w-[calc(100%-3px)] bg-stone-900/10 backdrop-blur-sm">
|
||||
<div className="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center">
|
||||
@@ -364,7 +389,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
||||
</button>
|
||||
<button className="p-button p-component p-button-outlined p-button-sm btn-wide">
|
||||
<span className="p-button-label p-c" onClick={handleReplaceAll}>
|
||||
Update & Delete
|
||||
Update & Delete missing
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from './renderIcon';
|
||||
export * from './renderName';
|
||||
export * from './renderTimeLeft';
|
||||
export * from './renderLinkedSystem';
|
||||
export * from './renderInfoColumn';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { GroupType, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { GROUPS } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
|
||||
export const renderIcon = (row: SystemSignature) => {
|
||||
export const renderIcon = (row: SystemSignature, customSize?: Omit<GroupType, 'icon' | 'id'>) => {
|
||||
if (row.group == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export const renderIcon = (row: SystemSignature) => {
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<img src={group.icon} style={{ width: group.w, height: group.h }} />
|
||||
<img src={group.icon} style={{ width: customSize?.w ?? group.w, height: customSize?.h ?? group.h }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.whFontSize {
|
||||
font-size: 11px !important;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SystemViewStandalone, WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import clsx from 'clsx';
|
||||
import { renderName } from './renderName.tsx';
|
||||
import classes from './renderInfoColumn.module.scss';
|
||||
|
||||
export const renderInfoColumn = (row: SystemSignature) => {
|
||||
if (!row.group || row.group === SignatureGroup.Wormhole) {
|
||||
return (
|
||||
<div className="flex justify-start items-center gap-[6px]">
|
||||
{row.type && (
|
||||
<WHClassView
|
||||
className="text-[11px]"
|
||||
classNameWh={classes.whFontSize}
|
||||
highlightName
|
||||
hideWhClass={!!row.linked_system}
|
||||
whClassName={row.type}
|
||||
noOffset
|
||||
useShortTitle
|
||||
/>
|
||||
)}
|
||||
|
||||
{row.linked_system && (
|
||||
<>
|
||||
{/*<span className="w-4 h-4 hero-arrow-long-right"></span>*/}
|
||||
<span title={row.linked_system?.solar_system_name}>
|
||||
<SystemViewStandalone
|
||||
className={clsx('select-none text-center cursor-context-menu')}
|
||||
hideRegion
|
||||
{...row.linked_system}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (row.description != null && row.description.length > 0) {
|
||||
return <span title={row.description}>{row.description}</span>;
|
||||
}
|
||||
|
||||
return renderName(row);
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { useCallback, useState } from 'react';
|
||||
import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components';
|
||||
import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx';
|
||||
import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu';
|
||||
import { MapSettings } from "@/hooks/Mapper/components/mapRootContent/components/MapSettings";
|
||||
|
||||
export interface MapRootContentProps {}
|
||||
|
||||
@@ -16,9 +17,11 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
const { isShowMenu } = interfaceSettings;
|
||||
|
||||
const [showOnTheMap, setShowOnTheMap] = useState(false);
|
||||
const [showMapSettings, setShowMapSettings] = useState(false);
|
||||
const mapInterface = <MapInterface />;
|
||||
|
||||
const handleShowOnTheMap = useCallback(() => setShowOnTheMap(true), []);
|
||||
const handleShowMapSettings = useCallback(() => setShowMapSettings(true), []);
|
||||
|
||||
useSkipContextMenu();
|
||||
|
||||
@@ -31,18 +34,19 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} />
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} />
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import classes from './Connections.module.scss';
|
||||
import { Sidebar } from 'primereact/sidebar';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState, useCallback } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
||||
import clsx from 'clsx';
|
||||
import { ConnectionOutput, OutCommand, Passage, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import {
|
||||
ConnectionOutput,
|
||||
ConnectionInfoOutput,
|
||||
OutCommand,
|
||||
Passage,
|
||||
SolarSystemConnection,
|
||||
} from '@/hooks/Mapper/types';
|
||||
import { PassageCard } from './PassageCard';
|
||||
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
|
||||
import { TimeAgo } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
const sortByDate = (a: string, b: string) => new Date(a).getTime() - new Date(b).getTime();
|
||||
|
||||
@@ -69,25 +76,44 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
}, [connections, selectedConnection]);
|
||||
|
||||
const [passages, setPassages] = useState<Passage[]>([]);
|
||||
const [info, setInfo] = useState<ConnectionInfoOutput>(null);
|
||||
|
||||
const loadInfo = useCallback(
|
||||
async (connection: SolarSystemConnection) => {
|
||||
const result = await outCommand<ConnectionInfoOutput>({
|
||||
type: OutCommand.getConnectionInfo,
|
||||
data: {
|
||||
from: connection.source,
|
||||
to: connection.target,
|
||||
},
|
||||
});
|
||||
|
||||
setInfo(result);
|
||||
},
|
||||
[outCommand],
|
||||
);
|
||||
|
||||
const loadPassages = useCallback(
|
||||
async (connection: SolarSystemConnection) => {
|
||||
const result = await outCommand<ConnectionOutput>({
|
||||
type: OutCommand.getPassages,
|
||||
data: {
|
||||
from: connection.source,
|
||||
to: connection.target,
|
||||
},
|
||||
});
|
||||
|
||||
setPassages(result.passages.sort((a, b) => sortByDate(b.inserted_at, a.inserted_at)));
|
||||
},
|
||||
[outCommand],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadInfo = async () => {
|
||||
const result = await outCommand<ConnectionOutput>({
|
||||
type: OutCommand.getPassages,
|
||||
data: {
|
||||
from: selectedConnection.source,
|
||||
to: selectedConnection.target,
|
||||
},
|
||||
});
|
||||
|
||||
setPassages(result.passages.sort((a, b) => sortByDate(b.inserted_at, a.inserted_at)));
|
||||
};
|
||||
|
||||
loadInfo();
|
||||
loadInfo(selectedConnection);
|
||||
loadPassages(selectedConnection);
|
||||
}, [selectedConnection]);
|
||||
|
||||
const approximateMass = useMemo(() => {
|
||||
@@ -132,6 +158,10 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
{kgToTons(approximateMass)}
|
||||
</InfoDrawer>
|
||||
|
||||
<InfoDrawer title="Mark EOL Time" rightSide>
|
||||
{info?.marl_eol_time ? <TimeAgo timestamp={info.marl_eol_time} /> : ' unknown '}
|
||||
</InfoDrawer>
|
||||
|
||||
<div className="flex gap-2"></div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,10 +8,11 @@ import { MenuItem } from 'primereact/menuitem';
|
||||
|
||||
export interface MapContextMenuProps {
|
||||
onShowOnTheMap?: () => void;
|
||||
onShowMapSettings?: () => void;
|
||||
}
|
||||
|
||||
export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
|
||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
|
||||
const { outCommand, setInterfaceSettings } = useMapRootState();
|
||||
|
||||
const menuRight = useRef<Menu>(null);
|
||||
|
||||
@@ -22,13 +23,6 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
|
||||
});
|
||||
}, [outCommand]);
|
||||
|
||||
const toggleMinimap = useCallback(() => {
|
||||
setInterfaceSettings(x => ({
|
||||
...x,
|
||||
isShowMinimap: !x.isShowMinimap,
|
||||
}));
|
||||
}, [setInterfaceSettings]);
|
||||
|
||||
const items = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@@ -43,9 +37,9 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
label: interfaceSettings.isShowMinimap ? 'Hide minimap' : 'Show minimap',
|
||||
icon: `pi ${interfaceSettings.isShowMinimap ? 'pi-eye-slash' : 'pi-eye'}`,
|
||||
command: toggleMinimap,
|
||||
label: 'Settings',
|
||||
icon: `pi pi-cog`,
|
||||
command: onShowMapSettings,
|
||||
},
|
||||
{
|
||||
label: 'Dock menu',
|
||||
@@ -57,7 +51,7 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
|
||||
})),
|
||||
},
|
||||
] as MenuItem[];
|
||||
}, [handleAddCharacter, interfaceSettings.isShowMinimap, onShowOnTheMap, setInterfaceSettings, toggleMinimap]);
|
||||
}, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
||||
|
||||
return (
|
||||
<div className="ml-1">
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
.verticalTabsContainer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
|
||||
:global {
|
||||
.p-tabview {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.p-tabview-panels {
|
||||
padding: 6px 1rem !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.p-tabview-nav-container {
|
||||
border-right: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.p-tabview-nav {
|
||||
flex-direction: column;
|
||||
width: 150px;
|
||||
min-height: 100%;
|
||||
border: none;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
border-right: 4px solid var(--surface-hover);
|
||||
background-color: var(--surface-card);
|
||||
|
||||
transition: background-color 200ms, border-right-color 200ms;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-hover);
|
||||
border-right: 4px solid var(--surface-100);
|
||||
}
|
||||
|
||||
.p-tabview-nav-link {
|
||||
transition: color 200ms;
|
||||
|
||||
justify-content: flex-end;
|
||||
padding: 10px;
|
||||
//background-color: var(--surface-card);
|
||||
background-color: initial;
|
||||
border: none;
|
||||
color: var(--gray-400);
|
||||
|
||||
border-radius: initial;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.p-tabview-selected {
|
||||
background-color: var(--surface-50);
|
||||
border-right: 4px solid var(--primary-color);
|
||||
|
||||
.p-tabview-nav-link {
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
//background-color: var(--surface-hover);
|
||||
border-right: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-tabview-panel {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.CheckboxContainer {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
|
||||
& > span:nth-child(1) {
|
||||
color: var(--gray-200);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
& > :nth-child(2){
|
||||
border-bottom: 2px dotted #3f3f3f;
|
||||
height: 2px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Уменьшение размеров InputSwitch с использованием глобальных стилей */
|
||||
.smallInputSwitch {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:global {
|
||||
.p-inputswitch {
|
||||
height: 1rem;
|
||||
width: 2rem;
|
||||
&.p-inputswitch-checked {
|
||||
.p-inputswitch-slider::before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
}
|
||||
|
||||
&.p-highlight .p-inputswitch-slider:before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
|
||||
.p-inputswitch-slider::before {
|
||||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
margin-top: -0.4rem;
|
||||
margin-left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import styles from './MapSettings.module.scss';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import { PrettySwitchbox } from './components';
|
||||
import { InterfaceStoredSettings, InterfaceStoredSettingsProps, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum UserSettingsRemoteProps {
|
||||
link_signature_on_splash = 'link_signature_on_splash',
|
||||
select_on_spash = 'select_on_spash',
|
||||
delete_connection_with_sigs = 'delete_connection_with_sigs',
|
||||
}
|
||||
|
||||
export const DEFAULT_REMOTE_SETTINGS = {
|
||||
[UserSettingsRemoteProps.link_signature_on_splash]: false,
|
||||
[UserSettingsRemoteProps.select_on_spash]: false,
|
||||
[UserSettingsRemoteProps.delete_connection_with_sigs]: false,
|
||||
};
|
||||
|
||||
export const UserSettingsRemoteList = [
|
||||
UserSettingsRemoteProps.link_signature_on_splash,
|
||||
UserSettingsRemoteProps.select_on_spash,
|
||||
UserSettingsRemoteProps.delete_connection_with_sigs,
|
||||
];
|
||||
|
||||
export type UserSettingsRemote = {
|
||||
link_signature_on_splash: boolean;
|
||||
select_on_spash: boolean;
|
||||
delete_connection_with_sigs: boolean;
|
||||
};
|
||||
|
||||
export type UserSettings = UserSettingsRemote & InterfaceStoredSettings;
|
||||
|
||||
export interface MapSettingsProps {
|
||||
show: boolean;
|
||||
onHide: () => void;
|
||||
}
|
||||
|
||||
type CheckboxesList = {
|
||||
prop: keyof UserSettings;
|
||||
label: string;
|
||||
}[];
|
||||
|
||||
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
|
||||
];
|
||||
|
||||
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' },
|
||||
{ prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' },
|
||||
];
|
||||
|
||||
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
|
||||
];
|
||||
|
||||
const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' },
|
||||
];
|
||||
|
||||
const UI_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
|
||||
];
|
||||
|
||||
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({ ...DEFAULT_REMOTE_SETTINGS });
|
||||
|
||||
const mergedSettings = useMemo(() => {
|
||||
return {
|
||||
...interfaceSettings,
|
||||
...userRemoteSettings,
|
||||
};
|
||||
}, [userRemoteSettings, interfaceSettings]);
|
||||
|
||||
const handleShow = async () => {
|
||||
const { user_settings } = await outCommand({
|
||||
type: OutCommand.getUserSettings,
|
||||
data: null,
|
||||
});
|
||||
|
||||
setUserRemoteSettings({
|
||||
...user_settings,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeChecked = useCallback(
|
||||
(prop: keyof UserSettings) => async (checked: boolean) => {
|
||||
// @ts-ignore
|
||||
if (UserSettingsRemoteList.includes(prop)) {
|
||||
const newRemoteSettings = {
|
||||
...userRemoteSettings,
|
||||
[prop]: checked,
|
||||
};
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.updateUserSettings,
|
||||
data: newRemoteSettings,
|
||||
});
|
||||
|
||||
setUserRemoteSettings(newRemoteSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
setInterfaceSettings({
|
||||
...interfaceSettings,
|
||||
[prop]: checked,
|
||||
});
|
||||
},
|
||||
[interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings],
|
||||
);
|
||||
|
||||
const renderCheckboxesList = (list: CheckboxesList) => {
|
||||
return list.map(x => {
|
||||
return (
|
||||
<PrettySwitchbox
|
||||
key={x.prop}
|
||||
label={x.label}
|
||||
checked={mergedSettings[x.prop]}
|
||||
setChecked={handleChangeChecked(x.prop)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="Map settings"
|
||||
visible={show}
|
||||
draggable={false}
|
||||
style={{ width: '550px' }}
|
||||
onShow={handleShow}
|
||||
onHide={() => {
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveIndex(0);
|
||||
onHide();
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className={styles.verticalTabsContainer}>
|
||||
<TabView
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={e => setActiveIndex(e.index)}
|
||||
className={styles.verticalTabView}
|
||||
>
|
||||
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">{renderCheckboxesList(COMMON_CHECKBOXES_PROPS)}</div>
|
||||
</TabPanel>
|
||||
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">
|
||||
{renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)}
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel header="Connections" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(UI_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
.CheckboxContainer {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
|
||||
& > span:nth-child(1) {
|
||||
color: var(--gray-200);
|
||||
font-size: 13px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
& > :nth-child(2){
|
||||
border-bottom: 2px dotted #3f3f3f;
|
||||
height: 1px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Уменьшение размеров InputSwitch с использованием глобальных стилей */
|
||||
.smallInputSwitch {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:global {
|
||||
.p-inputswitch {
|
||||
height: 1rem;
|
||||
width: 2rem;
|
||||
&.p-inputswitch-checked {
|
||||
.p-inputswitch-slider::before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
}
|
||||
|
||||
&.p-highlight .p-inputswitch-slider:before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
|
||||
.p-inputswitch-slider::before {
|
||||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
margin-top: -0.4rem;
|
||||
margin-left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import styles from './MapSettings.module.scss';
|
||||
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
interface PrettySwitchboxProps {
|
||||
checked: boolean;
|
||||
setChecked: (checked: boolean) => void;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const PrettySwitchbox = ({ checked, setChecked, label }: PrettySwitchboxProps) => {
|
||||
return (
|
||||
<label className={styles.CheckboxContainer}>
|
||||
<span>{label}</span>
|
||||
<div />
|
||||
<div className={styles.smallInputSwitch}>
|
||||
<WdCheckbox size="m" label={''} value={checked} onChange={e => setChecked(e.checked ?? false)} />
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './PrettySwitchbox';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './PrettySwitchbox';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './MapSettings';
|
||||
@@ -8,9 +8,10 @@ import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
interface RightBarProps {
|
||||
onShowOnTheMap?: () => void;
|
||||
onShowMapSettings?: () => void;
|
||||
}
|
||||
|
||||
export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
|
||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||
|
||||
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
|
||||
@@ -22,13 +23,6 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
});
|
||||
}, [outCommand]);
|
||||
|
||||
const handleOpenUserSettings = useCallback(() => {
|
||||
outCommand({
|
||||
type: OutCommand.openUserSettings,
|
||||
data: null,
|
||||
});
|
||||
}, [outCommand]);
|
||||
|
||||
const toggleMinimap = useCallback(() => {
|
||||
setInterfaceSettings(x => ({
|
||||
...x,
|
||||
@@ -66,17 +60,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
type="button"
|
||||
onClick={handleAddCharacter}
|
||||
>
|
||||
<i className="pi pi-user-plus text-lg"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
<WdTooltipWrapper content="User settings" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
onClick={handleOpenUserSettings}
|
||||
>
|
||||
<i className="pi pi-cog text-lg"></i>
|
||||
<i className="pi pi-user-plus"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
@@ -86,12 +70,22 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
type="button"
|
||||
onClick={onShowOnTheMap}
|
||||
>
|
||||
<i className="pi pi-hashtag text-lg"></i>
|
||||
<i className="pi pi-hashtag"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center mb-2 gap-1">
|
||||
<WdTooltipWrapper content="User settings" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
onClick={onShowMapSettings}
|
||||
>
|
||||
<i className="pi pi-cog"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
<WdTooltipWrapper
|
||||
content={
|
||||
interfaceSettings.isShowKSpace ? 'Hide highlighting Imperial Space' : 'Show highlighting Imperial Space'
|
||||
@@ -103,11 +97,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
type="button"
|
||||
onClick={toggleKSpace}
|
||||
>
|
||||
{interfaceSettings.isShowKSpace ? (
|
||||
<i className="pi pi-heart-fill text-lg"></i>
|
||||
) : (
|
||||
<i className="pi pi-heart text-lg"></i>
|
||||
)}
|
||||
<i className={interfaceSettings.isShowKSpace ? 'hero-cloud-solid' : 'hero-cloud'}></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
@@ -117,7 +107,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
type="button"
|
||||
onClick={toggleMinimap}
|
||||
>
|
||||
{isShowMinimap ? <i className="pi pi-eye text-lg"></i> : <i className="pi pi-eye-slash text-lg"></i>}
|
||||
<i className={isShowMinimap ? 'pi pi-eye' : 'pi pi-eye-slash'}></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
@@ -127,7 +117,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
|
||||
type="button"
|
||||
onClick={toggleMenu}
|
||||
>
|
||||
<i className="pi pi-window-minimize text-lg"></i>
|
||||
<i className="pi pi-window-minimize"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { createGenericContext } from '@/hooks/Mapper/utils/abstractContextProvider.tsx';
|
||||
|
||||
export interface SystemsSettingsProvider {
|
||||
systemId: string;
|
||||
}
|
||||
|
||||
const { Provider, useContextValue } = createGenericContext<SystemsSettingsProvider>();
|
||||
|
||||
export const SystemsSettingsProvider = Provider;
|
||||
export const useSystemsSettingsProvider = useContextValue;
|
||||
@@ -0,0 +1,81 @@
|
||||
.verticalTabsContainer {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.verticalTabsContainer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
|
||||
:global {
|
||||
.p-tabview {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.p-tabview-panels {
|
||||
padding: 6px 1rem !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.p-tabview-nav-container {
|
||||
border-right: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.p-tabview-nav {
|
||||
flex-direction: column;
|
||||
width: 150px;
|
||||
min-height: 100%;
|
||||
border: none;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
border-right: 4px solid var(--surface-hover);
|
||||
background-color: var(--surface-card);
|
||||
|
||||
transition: background-color 200ms, border-right-color 200ms;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-hover);
|
||||
border-right: 4px solid var(--surface-100);
|
||||
}
|
||||
|
||||
.p-tabview-nav-link {
|
||||
transition: color 200ms;
|
||||
|
||||
justify-content: flex-end;
|
||||
padding: 10px;
|
||||
background-color: initial;
|
||||
border: none;
|
||||
color: var(--gray-400);
|
||||
|
||||
border-radius: initial;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.p-tabview-selected {
|
||||
background-color: var(--surface-50);
|
||||
border-right: 4px solid var(--primary-color);
|
||||
|
||||
.p-tabview-nav-link {
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-right: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-tabview-panel {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
// import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import {
|
||||
SignatureGroupContent,
|
||||
SignatureGroupSelect,
|
||||
} from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { SystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
|
||||
import { Button } from 'primereact/button';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
type SystemSignaturePrepared = Omit<SystemSignature, 'linked_system'> & { linked_system: string };
|
||||
|
||||
export interface MapSettingsProps {
|
||||
systemId: string;
|
||||
show: boolean;
|
||||
onHide: () => void;
|
||||
signatureData: SystemSignature | null;
|
||||
}
|
||||
|
||||
export const SignatureSettings = ({ systemId, show, onHide, signatureData }: MapSettingsProps) => {
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
const handleShow = async () => {};
|
||||
const form = useForm<Partial<SystemSignaturePrepared>>({});
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!signatureData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { group, ...values } = form.getValues();
|
||||
let out = { ...signatureData };
|
||||
|
||||
switch (group) {
|
||||
case SignatureGroup.Wormhole:
|
||||
if (values.linked_system) {
|
||||
await outCommand({
|
||||
type: OutCommand.linkSignatureToSystem,
|
||||
data: {
|
||||
signature_eve_id: signatureData.eve_id,
|
||||
solar_system_source: systemId,
|
||||
solar_system_target: values.linked_system,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (values.type != null) {
|
||||
out = { ...out, type: values.type };
|
||||
}
|
||||
|
||||
if (signatureData.group !== SignatureGroup.Wormhole) {
|
||||
out = { ...out, name: '' };
|
||||
}
|
||||
|
||||
break;
|
||||
case SignatureGroup.CosmicSignature:
|
||||
out = { ...out, type: '', name: '' };
|
||||
break;
|
||||
default:
|
||||
if (values.name != null) {
|
||||
out = { ...out, name: values.name ?? '' };
|
||||
}
|
||||
}
|
||||
|
||||
if (values.description != null) {
|
||||
out = { ...out, description: values.description };
|
||||
}
|
||||
|
||||
// Note: when type of signature changed from WH to other type - we should drop name
|
||||
if (
|
||||
group !== SignatureGroup.Wormhole && // new
|
||||
signatureData.group === SignatureGroup.Wormhole && // prev
|
||||
signatureData.linked_system
|
||||
) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
|
||||
out = { ...out, type: '' };
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
}
|
||||
|
||||
// Note: despite groups have optional type - this will always set
|
||||
out = { ...out, group: group! };
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.updateSignatures,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
added: [],
|
||||
updated: [out],
|
||||
removed: [],
|
||||
},
|
||||
});
|
||||
|
||||
form.reset();
|
||||
onHide();
|
||||
}, [form, onHide, outCommand, signatureData, systemId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!signatureData) {
|
||||
form.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const { linked_system, ...rest } = signatureData;
|
||||
|
||||
form.reset({
|
||||
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
|
||||
...rest,
|
||||
});
|
||||
}, [form, signatureData]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header={`Signature Edit [${signatureData?.eve_id}]`}
|
||||
visible={show}
|
||||
draggable={false}
|
||||
style={{ width: '390px' }}
|
||||
onShow={handleShow}
|
||||
onHide={() => {
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
|
||||
onHide();
|
||||
}}
|
||||
>
|
||||
<SystemsSettingsProvider initialValue={{ systemId }}>
|
||||
<FormProvider {...form}>
|
||||
<div className="flex flex-col gap-2 justify-between">
|
||||
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Group:</span>
|
||||
<SignatureGroupSelect name="group" />
|
||||
</label>
|
||||
|
||||
<SignatureGroupContent />
|
||||
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Description:</span>
|
||||
<Controller
|
||||
name="description"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
||||
</div>
|
||||
</div>
|
||||
</FormProvider>
|
||||
</SystemsSettingsProvider>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
|
||||
import { SignatureGroupContentWormholes } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureGroupContentWormholes.tsx';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
|
||||
export interface SignatureGroupContentProps {}
|
||||
|
||||
export const SignatureGroupContent = ({}: SignatureGroupContentProps) => {
|
||||
const { watch, control } = useFormContext<SystemSignature>();
|
||||
const group = watch('group');
|
||||
|
||||
const {
|
||||
value: { systemId },
|
||||
} = useSystemsSettingsProvider();
|
||||
|
||||
if (!systemId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.Wormhole) {
|
||||
return (
|
||||
<>
|
||||
<SignatureGroupContentWormholes />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.CosmicSignature) {
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Name:</span>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
render={({ field }) => <InputText placeholder="Name" value={field.value} onChange={field.onChange} />}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './SignatureGroupContent';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { SignatureWormholeTypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureWormholeTypeSelect';
|
||||
import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect';
|
||||
|
||||
export const SignatureGroupContentWormholes = () => {
|
||||
return (
|
||||
<>
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Type:</span>
|
||||
<SignatureWormholeTypeSelect name="type" />
|
||||
</label>
|
||||
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Leads To:</span>
|
||||
<SignatureLeadsToSelect name="linked_system" />
|
||||
</label>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import clsx from 'clsx';
|
||||
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { renderIcon } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
const signatureGroupOptions = Object.keys(SignatureGroup).map(x => ({
|
||||
value: SignatureGroup[x as keyof typeof SignatureGroup],
|
||||
label: SignatureGroup[x as keyof typeof SignatureGroup],
|
||||
}));
|
||||
|
||||
// @ts-ignore
|
||||
const renderSignatureTemplate = option => {
|
||||
if (!option) {
|
||||
return 'No group selected';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<span className="w-[20px] mt-[1px] flex justify-center items-center">
|
||||
{renderIcon(
|
||||
{ group: option.label } as SystemSignature,
|
||||
option.label === SignatureGroup.CosmicSignature ? { w: 10, h: 10 } : { w: 16, h: 16 },
|
||||
)}
|
||||
</span>
|
||||
<span>{option.label}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface SignatureGroupSelectProps {
|
||||
name: string;
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const SignatureGroupSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => {
|
||||
const { control } = useFormContext();
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => (
|
||||
<Dropdown
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
options={signatureGroupOptions}
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Select group"
|
||||
className={clsx('w-full')}
|
||||
scrollHeight="240px"
|
||||
itemTemplate={renderSignatureTemplate}
|
||||
valueTemplate={renderSignatureTemplate}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './SignatureGroupSelect';
|
||||
@@ -0,0 +1,3 @@
|
||||
.SystemView {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import clsx from 'clsx';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
|
||||
import { useSystemInfo } from '@/hooks/Mapper/components/hooks';
|
||||
import { useMemo } from 'react';
|
||||
import { SystemView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import classes from './SignatureLeadsToSelect.module.scss';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
// @ts-ignore
|
||||
const renderLinkedSystemItem = (option: { value: string }) => {
|
||||
if (option.value == null) {
|
||||
return <div className="flex gap-2 items-center">No linked system</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<SystemView systemId={option.value} className={classes.SystemView} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const renderLinkedSystemValue = (option: { value: string }) => {
|
||||
if (!option) {
|
||||
return 'Select Leads To system';
|
||||
}
|
||||
|
||||
if (option.value == null) {
|
||||
return 'Select Leads To system';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<SystemView systemId={option.value} className={classes.SystemView} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderLeadsToEmpty = () => <div className="flex items-center text-[14px]">No wormhole to select</div>;
|
||||
|
||||
export interface SignatureLeadsToSelectProps {
|
||||
name: string;
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const SignatureLeadsToSelect = ({ name, defaultValue = '' }: SignatureLeadsToSelectProps) => {
|
||||
const { control, watch } = useFormContext<SystemSignature>();
|
||||
const group = watch('type');
|
||||
|
||||
const {
|
||||
value: { systemId },
|
||||
} = useSystemsSettingsProvider();
|
||||
|
||||
const { leadsTo } = useSystemInfo({ systemId });
|
||||
const { systems: systemStatics } = useLoadSystemStatic({ systems: leadsTo });
|
||||
const {
|
||||
data: { wormholes },
|
||||
} = useMapRootState();
|
||||
|
||||
const leadsToOptions = useMemo(() => {
|
||||
return [
|
||||
{ value: null },
|
||||
...leadsTo
|
||||
.filter(systemId => {
|
||||
const systemStatic = systemStatics.get(parseInt(systemId));
|
||||
const whInfo = wormholes.find(x => x.name === group);
|
||||
|
||||
if (!systemStatic || !whInfo || group === 'K162') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { id: whType } = WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID[systemStatic.system_class];
|
||||
return whInfo.dest === whType;
|
||||
})
|
||||
.map(x => ({ value: x })),
|
||||
];
|
||||
}, [group, leadsTo, systemStatics, wormholes]);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
// @ts-ignore
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Dropdown
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
options={leadsToOptions}
|
||||
optionValue="value"
|
||||
placeholder="Select Leads To wormhole"
|
||||
className={clsx('w-full')}
|
||||
scrollHeight="240px"
|
||||
itemTemplate={renderLinkedSystemItem}
|
||||
valueTemplate={renderLinkedSystemValue}
|
||||
emptyMessage={renderLeadsToEmpty}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './SignatureLeadsToSelect.tsx';
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import clsx from 'clsx';
|
||||
import { Respawn, SolarSystemStaticInfoRaw, WormholeDataRaw } from '@/hooks/Mapper/types';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
|
||||
import { useSystemInfo } from '@/hooks/Mapper/components/hooks';
|
||||
import {
|
||||
SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS,
|
||||
WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID,
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { useMemo } from 'react';
|
||||
import { WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes: WormholeDataRaw[]) => {
|
||||
const { id: whType } = WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID[systemStatic.system_class];
|
||||
|
||||
// @ts-ignore
|
||||
const spawnClassGroup = SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[whType];
|
||||
const possibleWHTypes = wormholes.filter(x => x.src.includes(spawnClassGroup));
|
||||
|
||||
return {
|
||||
statics: possibleWHTypes
|
||||
.filter(x => x.respawn.some(y => y === Respawn.static))
|
||||
.filter(x => systemStatic.statics.includes(x.name)),
|
||||
k162: wormholes.find(x => x.name === 'K162')!,
|
||||
wanderings: possibleWHTypes.filter(x => x.respawn.some(y => y === Respawn.wandering)),
|
||||
};
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const renderWHTypeGroupTemplate = option => {
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>{option.label}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const renderWHTypeTemplateValue = (option: { label: string; data: WormholeDataRaw }) => {
|
||||
if (!option) {
|
||||
return 'Select wormhole type';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<WHClassView whClassName={option.data.name} noOffset useShortTitle />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const renderWHTypeTemplate = (option: { label: string; data: WormholeDataRaw }) => {
|
||||
return (
|
||||
<div className="flex gap-2 items-center ml-[1rem]">
|
||||
<WHClassView whClassName={option.data.name} noOffset useShortTitle />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface SignatureGroupSelectProps {
|
||||
name: string;
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const SignatureWormholeTypeSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const {
|
||||
data: { wormholes },
|
||||
} = useMapRootState();
|
||||
|
||||
const {
|
||||
value: { systemId },
|
||||
} = useSystemsSettingsProvider();
|
||||
|
||||
const system = useSystemInfo({ systemId });
|
||||
|
||||
const possibleWormholesOptions = useMemo(() => {
|
||||
const possibleWormholes = getPossibleWormholes(system.staticInfo, wormholes);
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'Statics',
|
||||
items: [
|
||||
...possibleWormholes.statics.map(x => ({
|
||||
label: x.name,
|
||||
value: x.name,
|
||||
data: x,
|
||||
})),
|
||||
{
|
||||
value: possibleWormholes.k162.name,
|
||||
label: possibleWormholes.k162.name,
|
||||
data: possibleWormholes.k162,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Wanderings',
|
||||
items: possibleWormholes.wanderings.map(x => ({
|
||||
label: x.name,
|
||||
value: x.name,
|
||||
data: x,
|
||||
})),
|
||||
},
|
||||
];
|
||||
}, [system, wormholes]);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => (
|
||||
<Dropdown
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
options={possibleWormholesOptions}
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Select wormhole type"
|
||||
optionGroupLabel="label"
|
||||
optionGroupChildren="items"
|
||||
className={clsx('w-full')}
|
||||
scrollHeight="240px"
|
||||
optionGroupTemplate={renderWHTypeGroupTemplate}
|
||||
itemTemplate={renderWHTypeTemplate}
|
||||
valueTemplate={renderWHTypeTemplateValue}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './SignatureWormholeTypeSelect';
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './SignatureGroupSelect';
|
||||
export * from './SignatureGroupContent';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './SignatureSettings.tsx';
|
||||
@@ -5,6 +5,11 @@
|
||||
.WHClassViewContent {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
||||
&.NoOffset {
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.WHClassName {
|
||||
@@ -13,3 +18,12 @@
|
||||
font-weight: bold;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.NoOffset {
|
||||
*.WHClassName {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
font-weight: initial !important;
|
||||
top: initial !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,26 +16,42 @@ const prepareMass = (mass: number) => {
|
||||
|
||||
export interface WHClassViewProps {
|
||||
whClassName: string;
|
||||
noOffset?: boolean;
|
||||
useShortTitle?: boolean;
|
||||
hideWhClass?: boolean;
|
||||
highlightName?: boolean;
|
||||
className?: string;
|
||||
classNameWh?: string;
|
||||
}
|
||||
|
||||
export const WHClassView = ({ whClassName }: WHClassViewProps) => {
|
||||
export const WHClassView = ({
|
||||
whClassName,
|
||||
noOffset,
|
||||
useShortTitle,
|
||||
hideWhClass,
|
||||
highlightName,
|
||||
className,
|
||||
classNameWh,
|
||||
}: WHClassViewProps) => {
|
||||
const {
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const whData = useMemo(() => wormholesData[whClassName], [whClassName, wormholesData]);
|
||||
const whClass = useMemo(() => WORMHOLES_ADDITIONAL_INFO[whData.dest], [whData.dest]);
|
||||
const whClassStyle = WORMHOLE_CLASS_STYLES[whClass.wormholeClassID];
|
||||
const whClassStyle = WORMHOLE_CLASS_STYLES[whClass?.wormholeClassID] ?? '';
|
||||
|
||||
const uid = useMemo(() => new Date().getTime().toString(), []);
|
||||
|
||||
return (
|
||||
<div className={classes.WHClassViewRoot}>
|
||||
<div className={clsx(classes.WHClassViewRoot, className)}>
|
||||
<Tooltip
|
||||
target={`.wh-name${whClassName}`}
|
||||
target={`.wh-name${whClassName}${uid}`}
|
||||
position="right"
|
||||
mouseTrack
|
||||
mouseTrackLeft={20}
|
||||
mouseTrackTop={30}
|
||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-70 "
|
||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
@@ -49,9 +65,20 @@ export const WHClassView = ({ whClassName }: WHClassViewProps) => {
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<div className={clsx(classes.WHClassViewContent, 'wh-name select-none cursor-help', `wh-name${whClassName}`)}>
|
||||
<span>{whClassName}</span>
|
||||
<span className={clsx(classes.WHClassName, whClassStyle)}>{whClass.shortName}</span>
|
||||
<div
|
||||
className={clsx(
|
||||
classes.WHClassViewContent,
|
||||
{ [classes.NoOffset]: noOffset },
|
||||
'wh-name select-none cursor-help',
|
||||
`wh-name${whClassName}${uid}`,
|
||||
)}
|
||||
>
|
||||
<span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>
|
||||
{!hideWhClass && whClass && (
|
||||
<span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}>
|
||||
{useShortTitle ? whClass.shortTitle : whClass.shortName}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { createEvent } from 'react-event-hook';
|
||||
|
||||
export interface MapEvent {
|
||||
name: string;
|
||||
data: {
|
||||
solar_system_source: number;
|
||||
solar_system_target: number;
|
||||
};
|
||||
import { Command, CommandData } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
|
||||
export interface MapEvent<T extends Command> {
|
||||
name: T;
|
||||
data: CommandData[T];
|
||||
}
|
||||
|
||||
const { useMapEventListener, emitMapEvent } = createEvent('map-event')<MapEvent>();
|
||||
const { useMapEventListener, emitMapEvent } = createEvent('map-event')<MapEvent<Command>>();
|
||||
|
||||
export { useMapEventListener, emitMapEvent };
|
||||
|
||||
@@ -19,6 +19,7 @@ export const parseSignatures = (value: string, availableKeys: string[]): SystemS
|
||||
kind: availableKeys.includes(sigArrInfo[1]) ? sigArrInfo[1] : COSMIC_SIGNATURE,
|
||||
group: sigArrInfo[2],
|
||||
name: sigArrInfo[3],
|
||||
type: '',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import { WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constan
|
||||
import { WormholeDataRaw } from '@/hooks/Mapper/types';
|
||||
|
||||
export const sortWHClasses = (wormholesData: Record<string, WormholeDataRaw>, statics: string[]) => {
|
||||
if (!statics) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return statics
|
||||
.map(x => wormholesData[x])
|
||||
.map(x => ({ name: x.name, ...WORMHOLES_ADDITIONAL_INFO[x.dest] }))
|
||||
|
||||
@@ -12,6 +12,7 @@ export type MapRootData = MapUnionTypes & {
|
||||
|
||||
const INITIAL_DATA: MapRootData = {
|
||||
wormholesData: {},
|
||||
wormholes: [],
|
||||
effects: {},
|
||||
characters: [],
|
||||
userCharacters: [],
|
||||
@@ -26,7 +27,13 @@ const INITIAL_DATA: MapRootData = {
|
||||
selectedConnections: [],
|
||||
};
|
||||
|
||||
type InterfaceStoredSettings = {
|
||||
export enum InterfaceStoredSettingsProps {
|
||||
isShowMenu = 'isShowMenu',
|
||||
isShowMinimap = 'isShowMinimap',
|
||||
isShowKSpace = 'isShowKSpace',
|
||||
}
|
||||
|
||||
export type InterfaceStoredSettings = {
|
||||
isShowMenu: boolean;
|
||||
isShowMinimap: boolean;
|
||||
isShowKSpace: boolean;
|
||||
|
||||
@@ -24,6 +24,7 @@ export const useMapInit = () => {
|
||||
|
||||
if (wormholes) {
|
||||
updateData.wormholesData = wormholes.reduce((acc, x) => ({ ...acc, [x.name]: x }), {});
|
||||
updateData.wormholes = wormholes;
|
||||
}
|
||||
|
||||
if (effects) {
|
||||
|
||||
@@ -49,15 +49,25 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
break;
|
||||
case Commands.addSystems:
|
||||
addSystems(data as CommandAddSystems);
|
||||
setTimeout(() => {
|
||||
emitMapEvent({ name: Commands.addSystems, data });
|
||||
}, 100);
|
||||
break;
|
||||
case Commands.updateSystems:
|
||||
updateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems:
|
||||
removeSystems(data as CommandRemoveSystems);
|
||||
setTimeout(() => {
|
||||
emitMapEvent({ name: Commands.removeSystems, data });
|
||||
}, 100);
|
||||
|
||||
break;
|
||||
case Commands.addConnections:
|
||||
addConnections(data as CommandAddConnections);
|
||||
setTimeout(() => {
|
||||
emitMapEvent({ name: Commands.addConnections, data });
|
||||
}, 100);
|
||||
break;
|
||||
case Commands.removeConnections:
|
||||
removeConnections(data as CommandRemoveConnections);
|
||||
@@ -96,6 +106,8 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem:
|
||||
// TODO command data type lost
|
||||
// @ts-ignore
|
||||
emitMapEvent({ name: Commands.linkSignatureToSystem, data });
|
||||
break;
|
||||
|
||||
@@ -103,6 +115,12 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated:
|
||||
// TODO command data type lost
|
||||
// @ts-ignore
|
||||
emitMapEvent({ name: Commands.signaturesUpdated, data });
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,10 @@ export type Passage = {
|
||||
character: PassageLimitedCharacterType;
|
||||
};
|
||||
|
||||
export type ConnectionInfoOutput = {
|
||||
marl_eol_time: string;
|
||||
};
|
||||
|
||||
export type ConnectionOutput = {
|
||||
passages: Passage[];
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ export enum Commands {
|
||||
centerSystem = 'center_system',
|
||||
selectSystem = 'select_system',
|
||||
linkSignatureToSystem = 'link_signature_to_system',
|
||||
signaturesUpdated = 'signatures_updated',
|
||||
}
|
||||
|
||||
export type Command =
|
||||
@@ -44,7 +45,8 @@ export type Command =
|
||||
| Commands.routes
|
||||
| Commands.selectSystem
|
||||
| Commands.centerSystem
|
||||
| Commands.linkSignatureToSystem;
|
||||
| Commands.linkSignatureToSystem
|
||||
| Commands.signaturesUpdated;
|
||||
|
||||
export type CommandInit = {
|
||||
systems: SolarSystemRawType[];
|
||||
@@ -80,8 +82,8 @@ export type CommandCenterSystem = string | undefined;
|
||||
export type CommandLinkSignatureToSystem = {
|
||||
solar_system_source: number;
|
||||
solar_system_target: number;
|
||||
signatures: any[];
|
||||
};
|
||||
export type CommandLinkSignaturesUpdated = number;
|
||||
|
||||
export interface CommandData {
|
||||
[Commands.init]: CommandInit;
|
||||
@@ -102,6 +104,7 @@ export interface CommandData {
|
||||
[Commands.selectSystem]: CommandSelectSystem;
|
||||
[Commands.centerSystem]: CommandCenterSystem;
|
||||
[Commands.linkSignatureToSystem]: CommandLinkSignatureToSystem;
|
||||
[Commands.signaturesUpdated]: CommandLinkSignaturesUpdated;
|
||||
}
|
||||
|
||||
export interface MapHandlers {
|
||||
@@ -115,10 +118,12 @@ export enum OutCommand {
|
||||
getCharacterJumps = 'get_character_jumps',
|
||||
getSignatures = 'get_signatures',
|
||||
getSystemStaticInfos = 'get_system_static_infos',
|
||||
getConnectionInfo = 'get_connection_info',
|
||||
updateConnectionTimeStatus = 'update_connection_time_status',
|
||||
updateConnectionMassStatus = 'update_connection_mass_status',
|
||||
updateConnectionShipSizeType = 'update_connection_ship_size_type',
|
||||
updateConnectionLocked = 'update_connection_locked',
|
||||
updateConnectionCustomInfo = 'update_connection_custom_info',
|
||||
updateSignatures = 'update_signatures',
|
||||
updateSystemName = 'update_system_name',
|
||||
updateSystemDescription = 'update_system_description',
|
||||
@@ -141,6 +146,10 @@ export enum OutCommand {
|
||||
|
||||
// Only UI commands
|
||||
openSettings = 'open_settings',
|
||||
|
||||
getUserSettings = 'get_user_settings',
|
||||
updateUserSettings = 'update_user_settings',
|
||||
unlinkSignature = 'unlink_signature',
|
||||
}
|
||||
|
||||
export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { SolarSystemConnection } from '@/hooks/Mapper/types/connection.ts';
|
||||
|
||||
export type MapUnionTypes = {
|
||||
wormholesData: Record<string, WormholeDataRaw>;
|
||||
wormholes: WormholeDataRaw[];
|
||||
effects: Record<string, EffectRaw>;
|
||||
characters: CharacterTypeRaw[];
|
||||
userCharacters: string[];
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
|
||||
|
||||
export type SystemSignature = {
|
||||
eve_id: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
group: string;
|
||||
linked_system?: SolarSystemStaticInfoRaw;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export enum SignatureGroup {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
Wormhole = 'Wormhole',
|
||||
GasSite = 'Gas Site',
|
||||
RelicSite = 'Relic Site',
|
||||
DataSite = 'Data Site',
|
||||
OreSite = 'Ore Site',
|
||||
CombatSite = 'Combat Site',
|
||||
Wormhole = 'Wormhole',
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
}
|
||||
|
||||
export type GroupType = {
|
||||
@@ -26,3 +16,14 @@ export type GroupType = {
|
||||
w: number;
|
||||
h: number;
|
||||
};
|
||||
|
||||
export type SystemSignature = {
|
||||
eve_id: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
group: SignatureGroup;
|
||||
type: string;
|
||||
linked_system?: SolarSystemStaticInfoRaw;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
export enum Respawn {
|
||||
static = 'static',
|
||||
wandering = 'wandering',
|
||||
reverse = 'reverse',
|
||||
}
|
||||
|
||||
export type WormholeDataRaw = {
|
||||
dest: string;
|
||||
id: number;
|
||||
@@ -5,7 +11,7 @@ export type WormholeDataRaw = {
|
||||
mass_regen: number;
|
||||
max_mass_per_jump: number;
|
||||
name: string;
|
||||
sibling_groups: any;
|
||||
respawn: Respawn[];
|
||||
src: string[];
|
||||
static: boolean;
|
||||
total_mass: number;
|
||||
|
||||
26
assets/js/hooks/Mapper/utils/abstractContextProvider.tsx
Normal file
26
assets/js/hooks/Mapper/utils/abstractContextProvider.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createContext, ReactNode, useContext, useState } from 'react';
|
||||
|
||||
type ContextType<T> = {
|
||||
value: T;
|
||||
setValue: (newValue: T) => void;
|
||||
};
|
||||
|
||||
export const createGenericContext = <T,>() => {
|
||||
const context = createContext<ContextType<T> | undefined>(undefined);
|
||||
|
||||
const Provider = ({ children, initialValue }: { children: ReactNode; initialValue: T }) => {
|
||||
const [value, setValue] = useState<T>(initialValue);
|
||||
|
||||
return <context.Provider value={{ value, setValue }}>{children}</context.Provider>;
|
||||
};
|
||||
|
||||
const useContextValue = () => {
|
||||
const contextValue = useContext(context);
|
||||
if (!contextValue) {
|
||||
throw new Error('useContextValue must be used within a Provider');
|
||||
}
|
||||
return contextValue;
|
||||
};
|
||||
|
||||
return { Provider, useContextValue };
|
||||
};
|
||||
@@ -65,7 +65,7 @@ export class LabelsManager {
|
||||
}
|
||||
|
||||
hasLabel(label: string) {
|
||||
return this.parsedLabels.labels.includes(label);
|
||||
return this.parsedLabels.labels?.includes(label);
|
||||
}
|
||||
|
||||
toggleLabel(label: string) {
|
||||
|
||||
@@ -8,7 +8,7 @@ export default {
|
||||
const selector = '#' + this.el.id;
|
||||
|
||||
const droppable = new Droppable(containers, {
|
||||
delay: 150,
|
||||
delay: 100,
|
||||
draggable: '.draggable',
|
||||
dropzone: '.dropzone',
|
||||
mirror: {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"react-event-hook": "^3.1.2",
|
||||
"react-flow-renderer": "^10.3.17",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-hook-form": "^7.53.1",
|
||||
"react-usestateref": "^1.0.9",
|
||||
"reactflow": "^11.10.4",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
BIN
assets/static/images/amarr-180.png
Normal file
BIN
assets/static/images/amarr-180.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 187 KiB |
BIN
assets/static/images/caldaria-180.png
Normal file
BIN
assets/static/images/caldaria-180.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/static/images/gallente-180.png
Normal file
BIN
assets/static/images/gallente-180.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
BIN
assets/static/images/mataria-180.png
Normal file
BIN
assets/static/images/mataria-180.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 262 KiB |
@@ -3230,6 +3230,11 @@ react-grid-layout@^1.3.4:
|
||||
react-resizable "^3.0.5"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
react-hook-form@^7.53.1:
|
||||
version "7.53.1"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.1.tgz#3f2cd1ed2b3af99416a4ac674da2d526625add67"
|
||||
integrity sha512-6aiQeBda4zjcuaugWvim9WsGqisoUk+etmFEsSUMm451/Ic8L/UAb7sRtMj3V+Hdzm6mMjU1VhiSzYUZeBm0Vg==
|
||||
|
||||
react-is@^16.13.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
|
||||
@@ -55,11 +55,11 @@ map_subscriptions_enabled =
|
||||
|
||||
map_subscription_characters_limit =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 100)
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 10_000)
|
||||
|
||||
map_subscription_hubs_limit =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 10)
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 100)
|
||||
|
||||
wallet_tracking_enabled =
|
||||
config_dir
|
||||
@@ -77,7 +77,7 @@ config :wanderer_app,
|
||||
web_app_url: web_app_url,
|
||||
git_sha: System.get_env("GIT_SHA", "111"),
|
||||
custom_route_base_url: System.get_env("CUSTOM_ROUTE_BASE_URL"),
|
||||
invites: System.get_env("WANDERER_INVITES", "false") == "true",
|
||||
invites: System.get_env("WANDERER_INVITES", "false") |> String.to_existing_atom(),
|
||||
admin_username: System.get_env("WANDERER_ADMIN_USERNAME", "admin"),
|
||||
admin_password: System.get_env("WANDERER_ADMIN_PASSWORD"),
|
||||
admins: admins,
|
||||
@@ -138,6 +138,7 @@ config :ueberauth, WandererApp.Ueberauth.Strategy.Eve.OAuth,
|
||||
System.get_env("EVE_CLIENT_WITH_CORP_WALLET_SECRET", "<EVE_CLIENT_WITH_CORP_WALLET_SECRET>")
|
||||
|
||||
config :logger,
|
||||
truncate: :infinity,
|
||||
level:
|
||||
String.to_existing_atom(
|
||||
System.get_env(
|
||||
|
||||
3
fly.toml
3
fly.toml
@@ -6,6 +6,7 @@
|
||||
app = 'wanderer'
|
||||
primary_region = 'ams'
|
||||
kill_signal = 'SIGTERM'
|
||||
swap_size_mb = 512
|
||||
|
||||
[build]
|
||||
|
||||
@@ -35,6 +36,6 @@ path = "/metrics"
|
||||
soft_limit = 1000
|
||||
|
||||
[[vm]]
|
||||
memory = '1gb'
|
||||
memory = '256mb'
|
||||
cpu_kind = 'shared'
|
||||
cpus = 1
|
||||
|
||||
@@ -7,6 +7,8 @@ defmodule WandererApp do
|
||||
if it comes from the database, an external API or others.
|
||||
"""
|
||||
|
||||
require Logger
|
||||
|
||||
@doc """
|
||||
When used, dispatch to the appropriate domain service
|
||||
"""
|
||||
@@ -30,4 +32,19 @@ defmodule WandererApp do
|
||||
defmacro __using__(which) when is_atom(which) do
|
||||
apply(__MODULE__, which, [])
|
||||
end
|
||||
|
||||
def log_exception(kind, reason, stacktrace) do
|
||||
reason = Exception.normalize(kind, reason, stacktrace)
|
||||
|
||||
crash_reason =
|
||||
case kind do
|
||||
:throw -> {{:nocatch, reason}, stacktrace}
|
||||
_ -> {reason, stacktrace}
|
||||
end
|
||||
|
||||
Logger.error(
|
||||
Exception.format(kind, reason, stacktrace),
|
||||
crash_reason: crash_reason
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,8 +4,6 @@ defmodule WandererApp.Api.Calculations.CalcMapPermissions do
|
||||
use Ash.Resource.Calculation
|
||||
require Ash.Query
|
||||
|
||||
import Bitwise
|
||||
|
||||
@impl true
|
||||
def load(_query, _opts, _context) do
|
||||
[
|
||||
@@ -17,116 +15,8 @@ defmodule WandererApp.Api.Calculations.CalcMapPermissions do
|
||||
end
|
||||
|
||||
@impl true
|
||||
def calculate([record], _opts, %{actor: actor}) do
|
||||
characters = actor.characters
|
||||
|
||||
character_ids = characters |> Enum.map(& &1.id)
|
||||
character_eve_ids = characters |> Enum.map(& &1.eve_id)
|
||||
|
||||
character_corporation_ids =
|
||||
characters |> Enum.map(& &1.corporation_id) |> Enum.map(&to_string/1)
|
||||
|
||||
character_alliance_ids = characters |> Enum.map(& &1.alliance_id) |> Enum.map(&to_string/1)
|
||||
|
||||
result =
|
||||
record.acls
|
||||
|> Enum.reduce([0, 0], fn acl, acc ->
|
||||
is_owner? = acl.owner_id in character_ids
|
||||
|
||||
is_character_member? =
|
||||
acl.members |> Enum.any?(fn member -> member.eve_character_id in character_eve_ids end)
|
||||
|
||||
is_corporation_member? =
|
||||
acl.members
|
||||
|> Enum.any?(fn member -> member.eve_corporation_id in character_corporation_ids end)
|
||||
|
||||
is_alliance_member? =
|
||||
acl.members
|
||||
|> Enum.any?(fn member -> member.eve_alliance_id in character_alliance_ids end)
|
||||
|
||||
if is_owner? || is_character_member? || is_corporation_member? || is_alliance_member? do
|
||||
case acc do
|
||||
[_, -1] ->
|
||||
[-1, -1]
|
||||
|
||||
[-1, char_acc] ->
|
||||
char_acl_mask =
|
||||
acl.members
|
||||
|> Enum.filter(fn member ->
|
||||
member.eve_character_id in character_eve_ids
|
||||
end)
|
||||
|> Enum.reduce(0, fn member, acc ->
|
||||
case acc do
|
||||
-1 -> -1
|
||||
_ -> WandererApp.Permissions.calc_role_mask(member.role, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
char_acc =
|
||||
case char_acl_mask do
|
||||
-1 -> -1
|
||||
_ -> char_acc ||| char_acl_mask
|
||||
end
|
||||
|
||||
[-1, char_acc]
|
||||
|
||||
[any_acc, char_acc] ->
|
||||
any_acl_mask =
|
||||
acl.members
|
||||
|> Enum.filter(fn member ->
|
||||
member.eve_character_id in character_eve_ids ||
|
||||
member.eve_corporation_id in character_corporation_ids ||
|
||||
member.eve_alliance_id in character_alliance_ids
|
||||
end)
|
||||
|> Enum.reduce(0, fn member, acc ->
|
||||
case acc do
|
||||
-1 -> -1
|
||||
_ -> WandererApp.Permissions.calc_role_mask(member.role, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
char_acl_mask =
|
||||
acl.members
|
||||
|> Enum.filter(fn member ->
|
||||
member.eve_character_id in character_eve_ids
|
||||
end)
|
||||
|> Enum.reduce(0, fn member, acc ->
|
||||
case acc do
|
||||
-1 -> -1
|
||||
_ -> WandererApp.Permissions.calc_role_mask(member.role, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
any_acc =
|
||||
case any_acl_mask do
|
||||
-1 -> -1
|
||||
_ -> any_acc ||| any_acl_mask
|
||||
end
|
||||
|
||||
char_acc =
|
||||
case char_acl_mask do
|
||||
-1 -> -1
|
||||
_ -> char_acc ||| char_acl_mask
|
||||
end
|
||||
|
||||
[any_acc, char_acc]
|
||||
end
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
|
||||
case result do
|
||||
[_, -1] ->
|
||||
[-1]
|
||||
|
||||
[-1, char_acc] ->
|
||||
[char_acc]
|
||||
|
||||
[any_acc, _char_acc] ->
|
||||
[any_acc]
|
||||
end
|
||||
end
|
||||
def calculate([record], _opts, %{actor: actor}),
|
||||
do: WandererApp.Permissions.check_characters_access(actor.characters, record.acls)
|
||||
|
||||
@impl true
|
||||
def calculate(_records, _opts, _context) do
|
||||
|
||||
@@ -28,6 +28,7 @@ defmodule WandererApp.Api.MapConnection do
|
||||
define(:update_time_status, action: :update_time_status)
|
||||
define(:update_ship_size_type, action: :update_ship_size_type)
|
||||
define(:update_locked, action: :update_locked)
|
||||
define(:update_custom_info, action: :update_custom_info)
|
||||
end
|
||||
|
||||
actions do
|
||||
@@ -87,6 +88,10 @@ defmodule WandererApp.Api.MapConnection do
|
||||
update :update_locked do
|
||||
accept [:locked]
|
||||
end
|
||||
|
||||
update :update_custom_info do
|
||||
accept [:custom_info]
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -131,6 +136,10 @@ defmodule WandererApp.Api.MapConnection do
|
||||
|
||||
attribute :locked, :boolean
|
||||
|
||||
attribute :custom_info, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -15,6 +15,7 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
define(:create, action: :create)
|
||||
define(:update, action: :update)
|
||||
define(:update_linked_system, action: :update_linked_system)
|
||||
define(:update_type, action: :update_type)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
@@ -32,7 +33,8 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
:name,
|
||||
:description,
|
||||
:kind,
|
||||
:group
|
||||
:group,
|
||||
:type
|
||||
]
|
||||
|
||||
defaults [:read, :destroy]
|
||||
@@ -51,7 +53,8 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
:name,
|
||||
:description,
|
||||
:kind,
|
||||
:group
|
||||
:group,
|
||||
:type
|
||||
]
|
||||
|
||||
argument :system_id, :uuid, allow_nil?: false
|
||||
@@ -68,7 +71,7 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
:description,
|
||||
:kind,
|
||||
:group,
|
||||
:linked_system_id
|
||||
:type
|
||||
]
|
||||
|
||||
primary? true
|
||||
@@ -79,6 +82,10 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
accept [:linked_system_id]
|
||||
end
|
||||
|
||||
update :update_type do
|
||||
accept [:type]
|
||||
end
|
||||
|
||||
read :by_system_id do
|
||||
argument(:system_id, :string, allow_nil?: false)
|
||||
|
||||
@@ -105,6 +112,10 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :type, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :linked_system_id, :integer do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
@@ -8,6 +8,10 @@ defmodule WandererApp.Api.UserActivity do
|
||||
postgres do
|
||||
repo(WandererApp.Repo)
|
||||
table("user_activity_v1")
|
||||
|
||||
custom_indexes do
|
||||
index [:entity_id, :event_type, :inserted_at], unique: true
|
||||
end
|
||||
end
|
||||
|
||||
code_interface do
|
||||
@@ -104,6 +108,8 @@ defmodule WandererApp.Api.UserActivity do
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
|
||||
|
||||
relationships do
|
||||
belongs_to :character, WandererApp.Api.Character do
|
||||
allow_nil? true
|
||||
|
||||
@@ -3,6 +3,8 @@ defmodule WandererApp.Application do
|
||||
|
||||
use Application
|
||||
|
||||
require Logger
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
children =
|
||||
@@ -45,7 +47,16 @@ defmodule WandererApp.Application do
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
opts = [strategy: :one_for_one, name: WandererApp.Supervisor]
|
||||
|
||||
Supervisor.start_link(children, opts)
|
||||
|> case do
|
||||
{:ok, _pid} = ok ->
|
||||
ok
|
||||
|
||||
{:error, info} = e ->
|
||||
Logger.error("Failed to start application: #{inspect(info)}")
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
# Tell Phoenix to update the endpoint configuration
|
||||
|
||||
@@ -73,7 +73,8 @@ defmodule WandererApp.Character.Tracker do
|
||||
case WandererApp.Esi.get_character_info(eve_id) do
|
||||
{:ok, info} ->
|
||||
{:ok, character_state} = WandererApp.Character.get_character_state(character_id)
|
||||
update = maybe_update_corporation(character_state, info)
|
||||
|
||||
update = maybe_update_corporation(character_state, eve_id |> String.to_integer())
|
||||
WandererApp.Character.update_character_state(character_id, update)
|
||||
|
||||
:ok
|
||||
@@ -103,7 +104,9 @@ defmodule WandererApp.Character.Tracker do
|
||||
end
|
||||
|
||||
def update_ship(%{character_id: character_id, track_ship: true} = character_state) do
|
||||
case WandererApp.Character.get_character(character_id) do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character()
|
||||
|> case do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) ->
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:ship_forbidden")
|
||||
|> case do
|
||||
@@ -541,12 +544,17 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
defp maybe_update_corporation(
|
||||
state,
|
||||
%{
|
||||
"corporation_id" => corporation_id
|
||||
} = _info
|
||||
character_eve_id
|
||||
)
|
||||
when not is_nil(corporation_id),
|
||||
do: update_corporation(state, corporation_id)
|
||||
when not is_nil(character_eve_id) and is_integer(character_eve_id) do
|
||||
case WandererApp.Esi.post_characters_affiliation([character_eve_id]) do
|
||||
{:ok, [character_aff_info]} when not is_nil(character_aff_info) ->
|
||||
update_corporation(state, character_aff_info |> Map.get("corporation_id"))
|
||||
|
||||
error ->
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_corporation(
|
||||
state,
|
||||
|
||||
@@ -5,6 +5,10 @@ defmodule WandererApp.Esi do
|
||||
defdelegate get_alliance_info(eve_id, opts \\ []), to: WandererApp.Esi.ApiClient
|
||||
defdelegate get_corporation_info(eve_id, opts \\ []), to: WandererApp.Esi.ApiClient
|
||||
defdelegate get_character_info(eve_id, opts \\ []), to: WandererApp.Esi.ApiClient
|
||||
|
||||
defdelegate post_characters_affiliation(character_eve_ids, opts \\ []),
|
||||
to: WandererApp.Esi.ApiClient
|
||||
|
||||
defdelegate get_character_wallet(character_eve_id, opts \\ []), to: WandererApp.Esi.ApiClient
|
||||
defdelegate get_corporation_wallets(corporation_id, opts \\ []), to: WandererApp.Esi.ApiClient
|
||||
|
||||
|
||||
@@ -37,21 +37,32 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
|
||||
@logger Application.compile_env(:wanderer_app, :logger)
|
||||
|
||||
def get_server_status, do: _get("/status")
|
||||
def get_server_status, do: get("/status")
|
||||
|
||||
def set_autopilot_waypoint(add_to_beginning, clear_other_waypoints, destination_id, opts \\ []) do
|
||||
_post_esi(
|
||||
"/ui/autopilot/waypoint",
|
||||
opts
|
||||
|> Keyword.merge(
|
||||
params: %{
|
||||
add_to_beginning: add_to_beginning,
|
||||
clear_other_waypoints: clear_other_waypoints,
|
||||
destination_id: destination_id
|
||||
}
|
||||
def set_autopilot_waypoint(add_to_beginning, clear_other_waypoints, destination_id, opts \\ []),
|
||||
do:
|
||||
post_esi(
|
||||
"/ui/autopilot/waypoint",
|
||||
opts
|
||||
|> Keyword.merge(
|
||||
params: %{
|
||||
add_to_beginning: add_to_beginning,
|
||||
clear_other_waypoints: clear_other_waypoints,
|
||||
destination_id: destination_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def post_characters_affiliation(character_eve_ids, _opts)
|
||||
when is_list(character_eve_ids),
|
||||
do:
|
||||
post(
|
||||
"#{@base_url}/characters/affiliation/",
|
||||
json: character_eve_ids,
|
||||
params: %{
|
||||
datasource: "tranquility"
|
||||
}
|
||||
)
|
||||
|
||||
def find_routes(map_id, origin, hubs, routes_settings) do
|
||||
origin = origin |> String.to_integer()
|
||||
@@ -173,7 +184,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
routes =
|
||||
all_routes
|
||||
|> Enum.map(fn route_info ->
|
||||
_map_route_info(route_info)
|
||||
map_route_info(route_info)
|
||||
end)
|
||||
|> Enum.filter(fn route_info -> not is_nil(route_info) end)
|
||||
|
||||
@@ -189,7 +200,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:ok, result}
|
||||
|
||||
_ ->
|
||||
case _get_all_routes_custom(hubs, origin, params) do
|
||||
case get_all_routes_custom(hubs, origin, params) do
|
||||
{:ok, result} ->
|
||||
WandererApp.Cache.insert(
|
||||
cache_key,
|
||||
@@ -199,22 +210,21 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
|
||||
{:ok, result}
|
||||
|
||||
{:error, error} ->
|
||||
@logger.error("Error getting custom routes for #{inspect(origin)}: #{inspect(error)}")
|
||||
{:error, _error} ->
|
||||
@logger.error("Error getting custom routes for #{inspect(origin)}: #{inspect(hubs)}")
|
||||
|
||||
@logger.error(
|
||||
"Error getting custom routes for #{inspect(origin)}: #{inspect(params)}"
|
||||
)
|
||||
|
||||
_get_all_routes_eve(hubs, origin, params, opts)
|
||||
get_all_routes_eve(hubs, origin, params, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp _get_all_routes_custom(hubs, origin, params),
|
||||
defp get_all_routes_custom(hubs, origin, params),
|
||||
do:
|
||||
_post(
|
||||
post(
|
||||
"#{get_custom_route_base_url()}/route/multiple",
|
||||
[
|
||||
json: %{
|
||||
@@ -228,7 +238,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
|> Keyword.merge(@timeout_opts)
|
||||
)
|
||||
|
||||
def _get_all_routes_eve(hubs, origin, params, opts),
|
||||
def get_all_routes_eve(hubs, origin, params, opts),
|
||||
do:
|
||||
{:ok,
|
||||
hubs
|
||||
@@ -297,7 +307,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
opts: [ttl: @ttl]
|
||||
)
|
||||
def get_character_info(eve_id, opts \\ []) do
|
||||
case _get(
|
||||
case get(
|
||||
"/characters/#{eve_id}/",
|
||||
opts |> _with_cache_opts()
|
||||
) do
|
||||
@@ -374,7 +384,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
|
||||
defp _get_routes_eve(origin, destination, params, opts),
|
||||
do:
|
||||
_get(
|
||||
get(
|
||||
"/route/#{origin}/#{destination}/?#{params |> Plug.Conn.Query.encode()}",
|
||||
opts |> _with_cache_opts()
|
||||
)
|
||||
@@ -383,14 +393,14 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
|
||||
defp _get_alliance_info(alliance_eve_id, info_path, opts),
|
||||
do:
|
||||
_get(
|
||||
get(
|
||||
"/alliances/#{alliance_eve_id}/#{info_path}",
|
||||
opts |> _with_cache_opts()
|
||||
)
|
||||
|
||||
defp _get_corporation_info(corporation_eve_id, info_path, opts),
|
||||
do:
|
||||
_get(
|
||||
get(
|
||||
"/corporations/#{corporation_eve_id}/#{info_path}",
|
||||
opts |> _with_cache_opts()
|
||||
)
|
||||
@@ -405,7 +415,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
character_id = opts |> Keyword.get(:character_id, nil)
|
||||
|
||||
if not _is_access_token_expired?(character_id) do
|
||||
_get(
|
||||
get(
|
||||
path,
|
||||
auth_opts,
|
||||
opts
|
||||
@@ -426,7 +436,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
|
||||
defp _get_corporation_auth_data(corporation_eve_id, info_path, opts),
|
||||
do:
|
||||
_get(
|
||||
get(
|
||||
"/corporations/#{corporation_eve_id}/#{info_path}",
|
||||
[params: opts[:params] || []] ++
|
||||
(opts |> _get_auth_opts() |> _with_cache_opts()),
|
||||
@@ -437,14 +447,14 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
opts |> Keyword.merge(@cache_opts) |> Keyword.merge(cache_dir: System.tmp_dir!())
|
||||
end
|
||||
|
||||
defp _post_esi(path, opts),
|
||||
defp post_esi(path, opts),
|
||||
do:
|
||||
_post(
|
||||
post(
|
||||
"#{@base_url}#{path}",
|
||||
[params: opts[:params] || []] ++ (opts |> _get_auth_opts())
|
||||
)
|
||||
|
||||
defp _get(path, api_opts \\ [], opts \\ []) do
|
||||
defp get(path, api_opts \\ [], opts \\ []) do
|
||||
try do
|
||||
case Req.get("#{@base_url}#{path}", api_opts |> Keyword.merge(@retry_opts)) do
|
||||
{:ok, %{status: 200, body: body}} ->
|
||||
@@ -476,7 +486,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
end
|
||||
end
|
||||
|
||||
defp _post(url, opts) do
|
||||
defp post(url, opts) do
|
||||
try do
|
||||
case Req.post("#{url}", opts) do
|
||||
{:ok, %{status: status, body: body}} when status in [200, 201] ->
|
||||
@@ -514,7 +524,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:ok, token} ->
|
||||
auth_opts = [access_token: token.access_token] |> _get_auth_opts()
|
||||
|
||||
_get(
|
||||
get(
|
||||
path,
|
||||
api_opts |> Keyword.merge(auth_opts),
|
||||
opts |> Keyword.merge(retry_count: retry_count + 1)
|
||||
@@ -589,7 +599,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
end
|
||||
end
|
||||
|
||||
defp _map_route_info(
|
||||
defp map_route_info(
|
||||
%{
|
||||
"origin" => origin,
|
||||
"destination" => destination,
|
||||
@@ -598,14 +608,14 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
} = _route_info
|
||||
),
|
||||
do:
|
||||
_map_route_info(%{
|
||||
map_route_info(%{
|
||||
origin: origin,
|
||||
destination: destination,
|
||||
systems: result_systems,
|
||||
success: success
|
||||
})
|
||||
|
||||
defp _map_route_info(
|
||||
defp map_route_info(
|
||||
%{origin: origin, destination: destination, systems: result_systems, success: success} =
|
||||
_route_info
|
||||
) do
|
||||
@@ -627,5 +637,5 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
}
|
||||
end
|
||||
|
||||
defp _map_route_info(_), do: nil
|
||||
defp map_route_info(_), do: nil
|
||||
end
|
||||
|
||||
@@ -79,7 +79,8 @@ defmodule WandererApp.EveDataService do
|
||||
max_mass_per_jump: row["max_mass_per_jump"],
|
||||
static: row["static"],
|
||||
mass_regen: row["mass_regen"],
|
||||
sibling_groups: row["sibling_groups"]
|
||||
sibling_groups: row["sibling_groups"],
|
||||
respawn: row["respawn"]
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -8,6 +8,7 @@ defmodule WandererApp.Map do
|
||||
defstruct map_id: nil,
|
||||
name: nil,
|
||||
scope: :none,
|
||||
owner_id: nil,
|
||||
characters: [],
|
||||
systems: Map.new(),
|
||||
hubs: [],
|
||||
@@ -16,11 +17,12 @@ defmodule WandererApp.Map do
|
||||
characters_limit: nil,
|
||||
hubs_limit: nil
|
||||
|
||||
def new(%{id: map_id, name: name, scope: scope, acls: acls, hubs: hubs}) do
|
||||
def new(%{id: map_id, name: name, scope: scope, owner_id: owner_id, acls: acls, hubs: hubs}) do
|
||||
map =
|
||||
struct!(__MODULE__,
|
||||
map_id: map_id,
|
||||
scope: scope,
|
||||
owner_id: owner_id,
|
||||
name: name,
|
||||
acls: acls,
|
||||
hubs: hubs
|
||||
@@ -52,15 +54,6 @@ defmodule WandererApp.Map do
|
||||
end
|
||||
end
|
||||
|
||||
def get_map_options!(map) do
|
||||
map
|
||||
|> Map.get(:options)
|
||||
|> case do
|
||||
nil -> %{"layout" => "left_to_right"}
|
||||
options -> Jason.decode!(options)
|
||||
end
|
||||
end
|
||||
|
||||
def update_map(map_id, map_update) do
|
||||
Cachex.get_and_update(:map_cache, map_id, fn map ->
|
||||
case map do
|
||||
@@ -223,7 +216,7 @@ defmodule WandererApp.Map do
|
||||
%{visible: true} = system ->
|
||||
system
|
||||
|
||||
_ ->
|
||||
_system ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
@@ -271,7 +264,7 @@ defmodule WandererApp.Map do
|
||||
case not Map.has_key?(systems, solar_system_id) do
|
||||
true ->
|
||||
map_id
|
||||
|> update_map(%{systems: Map.put_new(systems, solar_system_id, system)})
|
||||
|> update_map(%{systems: Map.put(systems, solar_system_id, system)})
|
||||
|
||||
:ok
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ defmodule WandererApp.Map.PositionCalculator do
|
||||
def get_available_positions(level, x, y, opts),
|
||||
do: adjusted_coordinates(1 + level * 2, x, y, opts)
|
||||
|
||||
defp edge_coordinates(n, opts) when n > 1 do
|
||||
defp edge_coordinates(n, _opts) when n > 1 do
|
||||
min = -div(n, 2)
|
||||
max = div(n, 2)
|
||||
# Top edge
|
||||
|
||||
@@ -183,6 +183,12 @@ defmodule WandererApp.Map.Server do
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.delete_connection/2, [connection_info]})
|
||||
|
||||
def get_connection_info(map_id, connection_info) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.call({&Impl.get_connection_info/2, [connection_info]}, :timer.minutes(1))
|
||||
|
||||
def update_connection_time_status(map_id, connection_info) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
@@ -207,6 +213,12 @@ defmodule WandererApp.Map.Server do
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_connection_locked/2, [connection_info]})
|
||||
|
||||
def update_connection_custom_info(map_id, connection_info) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_connection_custom_info/2, [connection_info]})
|
||||
|
||||
@impl true
|
||||
def handle_continue(:load_state, state),
|
||||
do: {:noreply, state |> Impl.load_state(), {:continue, :start_map}}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -87,4 +87,113 @@ defmodule WandererApp.Permissions do
|
||||
delete_map: check_permission(user_permissions, @delete_map)
|
||||
}
|
||||
end
|
||||
|
||||
def check_characters_access(characters, acls) do
|
||||
character_ids = characters |> Enum.map(& &1.id)
|
||||
character_eve_ids = characters |> Enum.map(& &1.eve_id)
|
||||
|
||||
character_corporation_ids =
|
||||
characters |> Enum.map(& &1.corporation_id) |> Enum.map(&to_string/1)
|
||||
|
||||
character_alliance_ids = characters |> Enum.map(& &1.alliance_id) |> Enum.map(&to_string/1)
|
||||
|
||||
result =
|
||||
acls
|
||||
|> Enum.reduce([0, 0], fn acl, acc ->
|
||||
is_owner? = acl.owner_id in character_ids
|
||||
|
||||
is_character_member? =
|
||||
acl.members |> Enum.any?(fn member -> member.eve_character_id in character_eve_ids end)
|
||||
|
||||
is_corporation_member? =
|
||||
acl.members
|
||||
|> Enum.any?(fn member -> member.eve_corporation_id in character_corporation_ids end)
|
||||
|
||||
is_alliance_member? =
|
||||
acl.members
|
||||
|> Enum.any?(fn member -> member.eve_alliance_id in character_alliance_ids end)
|
||||
|
||||
if is_owner? || is_character_member? || is_corporation_member? || is_alliance_member? do
|
||||
case acc do
|
||||
[_, -1] ->
|
||||
[-1, -1]
|
||||
|
||||
[-1, char_acc] ->
|
||||
char_acl_mask =
|
||||
acl.members
|
||||
|> Enum.filter(fn member ->
|
||||
member.eve_character_id in character_eve_ids
|
||||
end)
|
||||
|> Enum.reduce(0, fn member, acc ->
|
||||
case acc do
|
||||
-1 -> -1
|
||||
_ -> calc_role_mask(member.role, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
char_acc =
|
||||
case char_acl_mask do
|
||||
-1 -> -1
|
||||
_ -> char_acc ||| char_acl_mask
|
||||
end
|
||||
|
||||
[-1, char_acc]
|
||||
|
||||
[any_acc, char_acc] ->
|
||||
any_acl_mask =
|
||||
acl.members
|
||||
|> Enum.filter(fn member ->
|
||||
member.eve_character_id in character_eve_ids ||
|
||||
member.eve_corporation_id in character_corporation_ids ||
|
||||
member.eve_alliance_id in character_alliance_ids
|
||||
end)
|
||||
|> Enum.reduce(0, fn member, acc ->
|
||||
case acc do
|
||||
-1 -> -1
|
||||
_ -> calc_role_mask(member.role, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
char_acl_mask =
|
||||
acl.members
|
||||
|> Enum.filter(fn member ->
|
||||
member.eve_character_id in character_eve_ids
|
||||
end)
|
||||
|> Enum.reduce(0, fn member, acc ->
|
||||
case acc do
|
||||
-1 -> -1
|
||||
_ -> calc_role_mask(member.role, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
any_acc =
|
||||
case any_acl_mask do
|
||||
-1 -> -1
|
||||
_ -> any_acc ||| any_acl_mask
|
||||
end
|
||||
|
||||
char_acc =
|
||||
case char_acl_mask do
|
||||
-1 -> -1
|
||||
_ -> char_acc ||| char_acl_mask
|
||||
end
|
||||
|
||||
[any_acc, char_acc]
|
||||
end
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
|
||||
case result do
|
||||
[_, -1] ->
|
||||
[-1]
|
||||
|
||||
[-1, char_acc] ->
|
||||
[char_acc]
|
||||
|
||||
[any_acc, _char_acc] ->
|
||||
[any_acc]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user