Compare commits

...

295 Commits

Author SHA1 Message Date
CI
638a4e2535 chore: release version v1.52.6
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-23 09:16:28 +00:00
Dmitry Popov
489fde16d1 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-02-23 10:04:09 +01:00
Dmitry Popov
35e1c363e5 fix(Map): Fixed delete systems on map changes 2025-02-23 10:04:05 +01:00
CI
6b97d36bf1 chore: release version v1.52.5
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-22 08:21:41 +00:00
Dmitry Popov
82f6a7f701 fix(Map): Fixed delete system on signature deletion 2025-02-22 09:10:12 +01:00
Dmitry Popov
2d92dfbafa fix(Map): Fixed delete system on signature deletion 2025-02-22 08:39:50 +01:00
CI
f81f41f555 chore: release version v1.52.4
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-21 11:33:25 +00:00
Dmitry Popov
54c7b44d69 fix: signature paste for russian lang 2025-02-21 12:25:35 +01:00
CI
9da6605ccb chore: release version v1.52.3
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-21 07:44:16 +00:00
guarzo
a90bf9762a fix: remove signature expiration (#196) 2025-02-21 11:15:06 +04:00
CI
c87cfb3c43 chore: release version v1.52.2 2025-02-21 07:10:35 +00:00
guarzo
85cb9ccfa8 fix: prevent constant full signature widget rerender (#195) 2025-02-21 10:54:20 +04:00
CI
da2639786d chore: release version v1.52.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-20 07:57:18 +00:00
guarzo
3cf77da293 fix: proper virtual scroller usage (#192) 2025-02-20 11:20:43 +04:00
guarzo
3dd7633194 fix: restore delete key functionality for nodes (#191) 2025-02-20 11:19:20 +04:00
CI
ae7f4edf4a chore: release version v1.52.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-19 20:24:25 +00:00
Dmitry Popov
52eab28f27 feat(Map): Added map characters view 2025-02-19 21:12:51 +01:00
Dmitry Popov
6098d32bce chore: release version v1.51.3 2025-02-19 17:48:13 +01:00
CI
1839834771 chore: release version v1.51.3
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-19 09:11:12 +00:00
guarzo
7cdfb87853 fix issue with deselection and linked sig splash filters (#187) 2025-02-19 12:23:22 +04:00
guarzo
3d54783a3e fix: pending deletion working again (#185)
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-19 02:24:15 +04:00
CI
f965461820 chore: release version v1.51.2 2025-02-18 17:36:51 +00:00
Dmitry Popov
6d67f87d4b Sigs character name (#182)
* feat(Signatures): Added character names
2025-02-18 21:12:46 +04:00
CI
60697a50c2 chore: release version v1.51.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-18 07:56:02 +00:00
guarzo
778d23da06 updates to acl api (#179) 2025-02-18 11:24:14 +04:00
CI
0ee9a15d5d chore: release version v1.51.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-17 22:44:07 +00:00
guarzo
24bb902bb9 feat: add undo deletion for signatures (#155)
* feat: add undo for signature deletion and addition
2025-02-18 02:29:40 +04:00
CI
32fe6395a1 chore: release version v1.50.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-17 00:09:15 +00:00
guarzo
5f506bf4b2 feat: allow addition of characters to acl without preregistration (#176) 2025-02-17 03:52:47 +04:00
CI
0127ebfe46 chore: release version v1.49.0
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / Manual Approval (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-02-15 08:37:43 +00:00
guarzo
8c5366fd9b feat: add api for acl management (#171) 2025-02-15 12:16:42 +04:00
CI
dbcad892a9 chore: release version v1.48.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / Manual Approval (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-02-13 17:40:15 +00:00
Dmitry Popov
6da3096db1 chore: release version v1.48.0 2025-02-13 18:04:28 +01:00
CI
cd8efcd6e3 chore: release version v1.48.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-12 22:46:57 +00:00
Dmitry Popov
b52471ae5e chore: release version v1.47.6 2025-02-12 23:37:54 +01:00
Dmitry Popov
438fecb61f chore: release version v1.47.6 2025-02-12 23:07:45 +01:00
guarzo
70b589a359 System Kills cleanup (#166)
* fix: styling and count of kills tooltip
2025-02-13 01:23:36 +04:00
guarzo
cf7069b3b2 feat: autosize local character tooltip and increase hover target (#165) 2025-02-13 01:11:40 +04:00
CI
b2198e469e chore: release version v1.47.6
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-12 09:47:56 +00:00
Dmitry Popov
8ab337e8e7 chore: release version v1.47.5 2025-02-12 10:17:35 +01:00
CI
51878ab503 chore: release version v1.47.5 2025-02-12 07:55:37 +00:00
guarzo
401dfad298 fix: sync kills count bookmark and the kills widget (#160)
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
* fix: lazy load kills widget
2025-02-12 03:15:16 +04:00
CI
18cff7d312 chore: release version v1.47.4
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-11 11:10:34 +00:00
Dmitry Popov
7896de00d6 chore: release version v1.47.3 2025-02-11 12:02:42 +01:00
CI
3b079505c3 chore: release version v1.47.3 2025-02-11 10:20:28 +00:00
Dmitry Popov
5b972b03e5 Revert "fix: lazy load kills widget (#157)" (#158)
This reverts commit b29e57b3a4.
2025-02-11 14:20:01 +04:00
CI
79b284c46d chore: release version v1.47.2 2025-02-11 08:31:23 +00:00
guarzo
b29e57b3a4 fix: lazy load kills widget (#157)
* fix: lazy load kills widget

* fix: updates for eslint and pr feedback
2025-02-11 12:28:24 +04:00
CI
c6f4baeee3 chore: release version v1.47.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-02-09 16:27:54 +00:00
Dmitry Popov
6d341be072 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-02-09 17:27:27 +01:00
Dmitry Popov
2437ec9c84 fix(Connections): Fixed connections auto-refresh after update 2025-02-09 17:27:22 +01:00
CI
7e692b5805 chore: release version v1.47.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-09 10:13:17 +00:00
Dmitry Popov
01b7370ecd feat(Map): Added check for active map subscription to using Map APIs 2025-02-09 11:12:43 +01:00
CI
20ad8b07d7 chore: release version v1.46.1 2025-02-09 09:36:55 +00:00
Aleksei Chichenkov
cab1880fb0 fix(Map): Fixed a lot of design and architect issues after last milli… (#154)
* fix(Map): Fixed a lot of design and architect issues after last million PRs

* fix(Map): removed unnecessary hooks styles

---------

Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
2025-02-09 13:36:25 +04:00
CI
78eefcd6a7 chore: release version v1.46.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-08 11:03:09 +00:00
Dmitry Popov
eec78d38a8 feat: Added WANDERER_RESTRICT_MAPS_CREATION env support
Restrict maps creation for any registered users, allow for server admins
only.
2025-02-08 12:02:38 +01:00
CI
73f8b1f06b chore: release version v1.45.5
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-07 19:16:01 +00:00
guarzo
f96cb01860 fix: restore styling for local characters list (#152) 2025-02-07 23:15:20 +04:00
CI
6800be1bb6 chore: release version v1.45.4 2025-02-07 19:15:01 +00:00
guarzo
143f0a5b3a fix: remove snap to grid customization (#153) 2025-02-07 23:14:29 +04:00
CI
b6495504f8 chore: release version v1.45.3
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-02-05 17:22:36 +00:00
guarzo
2f07ec1b74 fix: color and formatting fixes for local character (#150) 2025-02-05 21:22:10 +04:00
CI
7073a0e8e6 chore: release version v1.45.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-05 15:55:30 +00:00
guarzo
bb0d91a3c7 fix: fix route list hover and on the map character list (#149)
* fix: correct formatting for on the map character list

* fix: fix hover for route list
2025-02-05 19:55:05 +04:00
CI
1cb12b97ba chore: release version v1.45.1 2025-02-05 14:59:57 +00:00
guarzo
860d20dc66 fix: kill count subscript position on firefox, and remove kill filter for single system (#148) 2025-02-05 18:59:30 +04:00
CI
a850071965 chore: release version v1.45.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-05 07:02:17 +00:00
guarzo
fc41573e70 feat: allow filtering of k-space kills (#147) 2025-02-05 07:01:46 +00:00
CI
97f1808fb5 chore: release version v1.44.9
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64/v8) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-04 21:00:56 +00:00
guarzo
d31046eebb fix: improve local character header shrink behavior (#146) 2025-02-04 21:00:30 +00:00
CI
a70fa50eab chore: release version v1.44.8 2025-02-04 19:32:13 +00:00
Dmitry Popov
9a082c26f5 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-02-04 20:31:26 +01:00
Dmitry Popov
6af2dc1ed5 fix(Core): include external libraries in build 2025-02-04 20:31:21 +01:00
CI
5fd1509d44 chore: release version v1.44.7 2025-02-04 19:21:18 +00:00
Dmitry Popov
2448c0531b fix(Core): include external libraries in build 2025-02-04 20:20:34 +01:00
CI
b685ea1013 chore: release version v1.44.6 2025-02-04 17:19:49 +00:00
guarzo
55465688c8 Add hover tooltips for local counter and kills bookmark (#130)
* feat: add local pilots and kills display on hover
2025-02-04 21:19:13 +04:00
CI
ac3c7e0c44 chore: release version v1.44.5 2025-02-04 17:12:10 +00:00
guarzo
2d6ab5646c fix: include category param in search cache key (#144) 2025-02-04 21:11:41 +04:00
CI
67b373ac29 chore: release version v1.44.4
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-02-02 21:45:53 +00:00
Dmitry Popov
678169e6fa chore: release version v1.44.0 2025-02-02 22:44:55 +01:00
Dmitry Popov
7ee3c8db82 Merge branch 'main' into zkill_subs 2025-02-02 22:25:00 +01:00
Dmitry Popov
304f4b01ab chore: release version v1.44.0 2025-02-02 22:24:47 +01:00
CI
4af12c21b2 chore: release version v1.44.3 2025-02-02 21:18:47 +00:00
guarzo
497da1e5f7 fix: restored kills lightning bolt functionality (#143) 2025-02-03 01:18:21 +04:00
CI
5bd968acae chore: release version v1.44.2 2025-02-02 19:54:11 +00:00
guarzo
f74c20142c Add api for visible system kill information (#133)
* feat: api for zkill information
2025-02-02 23:53:40 +04:00
CI
d4c40d7542 chore: release version v1.44.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-02-01 20:34:37 +00:00
Aleksei Chichenkov
04f3fec0c0 fix(Map): Fixed problem with windows. (#140)
* fix(Map): Fixed problem with windows.

* fix(Core): Added min heigth for body

---------

Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
Co-authored-by: Dmitry Popov <dmitriypopovsamara@gmail.com>
2025-02-02 00:34:08 +04:00
CI
cd0b4b0fc9 chore: release version v1.44.0 2025-02-01 15:22:11 +00:00
Aleksei Chichenkov
e7b115e6e6 Merge pull request #124 from guarzo/guarzo/zkill
feat: add zkill widget
2025-02-01 18:21:40 +03:00
Gustav
dff8fc6396 refactor: additional design feedback improvements 2025-01-31 15:00:46 -07:00
Gustav
afdaeb3d34 fix: design feedback patch 2025-01-31 15:00:46 -07:00
Gustav
ac6053361e fix: removed unneeded event handler 2025-01-31 15:00:46 -07:00
Gustav
eb3e1ba3aa feat: add news post for zkill widget 2025-01-31 15:00:46 -07:00
Gustav
8468a9b5de feat: add zkill widget 2025-01-31 15:00:46 -07:00
CI
5eafe59dcb chore: release version v1.43.9
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-30 21:55:35 +00:00
Dmitry Popov
b38bcaa8cf fix(Core): Add discord link to 'Like' icon on main interface 2025-01-30 22:55:04 +01:00
CI
8a238a447d chore: release version v1.43.8
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-26 16:21:18 +00:00
Dmitry Popov
3731219216 fix(Core): Update shuttered constellations (required EVE DB data update on server). 2025-01-26 17:20:28 +01:00
CI
73d5fd5f67 chore: release version v1.43.7 2025-01-26 16:12:32 +00:00
Dmitry Popov
e8e4aed6d5 Signature EOL status support (#136)
* feat(Map): Added an ability to mark signature as EOL

* chore: release version v1.39.1

* fix(Map): Add correct styles for switch

* fix(Map): Refactor signatures code. Add ability to set EOL for signature marked as EOL

* feat(Map): Added EOL status for unsplashed signatures. Show precise time for connection passages on hover.

---------

Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
2025-01-26 20:12:07 +04:00
CI
63571a462f chore: release version v1.43.6
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-22 17:41:42 +00:00
Dmitry Popov
606add4142 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-22 18:40:47 +01:00
Dmitry Popov
dac480b059 fix(Widgets): Fix widgets not visible on map 2025-01-22 18:40:37 +01:00
CI
5f67cb1dd7 chore: release version v1.43.5 2025-01-22 17:07:37 +00:00
Dmitry Popov
5886fff753 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-22 18:06:22 +01:00
Dmitry Popov
da2e12bdd1 fix(Audit): Fix signature added/removed system name 2025-01-22 18:06:03 +01:00
CI
05c3d20e56 chore: release version v1.43.4
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-21 08:48:20 +00:00
guarzo
4633d26517 fix: improve structure widget styling (#127) 2025-01-21 12:47:40 +04:00
CI
30b0556d47 chore: release version v1.43.3 2025-01-21 08:46:40 +00:00
Dmitry Popov
e094378dc5 chore: release version v1.43.2 2025-01-21 09:46:09 +01:00
CI
0c48189503 chore: release version v1.43.2 2025-01-21 07:50:24 +00:00
guarzo
a5c346627a fix: prevent constraint error for follow/toggle (#132) 2025-01-21 11:49:58 +04:00
CI
4e526040bf chore: release version v1.43.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-20 23:28:45 +00:00
Dmitry Popov
869c25cd60 chore: release version v1.42.4 2025-01-21 00:27:49 +01:00
CI
6aac698cd8 chore: release version v1.43.0 2025-01-20 23:04:11 +00:00
guarzo
230016b90f feat: add news post for structures widget (#131) 2025-01-21 03:03:31 +04:00
CI
4b1aef8dd9 chore: release version v1.42.5 2025-01-20 23:01:42 +00:00
Dmitry Popov
d34509d7a0 fix(Map): Fix link signatures on splash. Fix deleting connection on locked system remove. 2025-01-20 23:59:58 +01:00
CI
fca98ec232 chore: release version v1.42.4
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-20 10:43:19 +00:00
Dmitry Popov
e2814e95bd fix: Fix system statics list (required EVE DB data update). Add system name to signature added/removed audit log 2025-01-20 11:42:38 +01:00
CI
68a3f84704 chore: release version v1.42.3
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-17 08:18:30 +00:00
guarzo
4bc76feefc fix: change structure tooltip to avoid paste confusion (#125)
* fix: change structure tooltip to avoid paste confusion

* fix: clarify use of evetime and use primereact calendar
2025-01-17 12:18:04 +04:00
CI
da39a55fd0 chore: release version v1.42.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-16 23:30:13 +00:00
Dmitry Popov
ee3cf04cd4 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-17 00:29:40 +01:00
Dmitry Popov
d79e7fe2ff chore: release version v1.42.0 2025-01-17 00:29:36 +01:00
CI
8de9fdef32 chore: release version v1.42.1 2025-01-16 22:29:33 +00:00
Dmitry Popov
f51deeec2d Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-16 23:29:07 +01:00
Dmitry Popov
a971c69a96 fix(Map): Remove linked sig ID if system containing signature removed from map 2025-01-16 23:25:59 +01:00
CI
b7995f50de chore: release version v1.42.0 2025-01-16 21:53:41 +00:00
Dmitry Popov
14997a2959 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-16 22:50:48 +01:00
Dmitry Popov
8fef6bcf82 feat(Audit): Add 'Signatures added/removed' map audit events 2025-01-16 22:50:44 +01:00
CI
1f82d23963 chore: release version v1.41.0 2025-01-16 19:50:07 +00:00
Dmitry Popov
28317a2431 feat(Audit): Add 'ACL added/removed' map audit events 2025-01-16 20:49:34 +01:00
CI
6aac496a57 chore: release version v1.40.7
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-15 14:09:50 +00:00
Aleksei Chichenkov
ac9306b713 Merge pull request #123 from guarzo/guarzo/themesimple
refactor: simplify theme scss and add typing for hook
2025-01-15 17:09:23 +03:00
Gustav
d55e804efa refactor: simplify theme scss and add typing for hook 2025-01-14 21:58:23 -05:00
CI
08407a5679 chore: release version v1.40.6 2025-01-15 02:48:14 +00:00
CI
c37d175bec chore: release version v1.40.5
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-14 22:41:08 +00:00
Dmitry Popov
69c5326e72 fix(Map): Fix follow mode 2025-01-14 23:40:40 +01:00
CI
305f63e11d chore: release version v1.40.4
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-14 21:35:49 +00:00
guarzo
698fd5e083 fix: center system is not selected text for structures (#122) 2025-01-15 01:35:24 +04:00
CI
1af8342d30 chore: release version v1.40.3 2025-01-14 20:47:50 +00:00
Dmitry Popov
68b59da78e Merge remote-tracking branch 'origin/main' 2025-01-14 21:47:22 +01:00
Dmitry Popov
e784a3f850 fix(Map): Fix system revert issues 2025-01-14 21:47:05 +01:00
CI
a45e2f3fc2 chore: release version v1.40.2 2025-01-14 20:07:10 +00:00
Dmitry Popov
8a3d920c31 fix(Map): Fix issues with splashing signatures select & sig ID in temp names 2025-01-14 21:06:41 +01:00
CI
996d7c47bd chore: release version v1.40.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-14 14:16:42 +00:00
Aleksei Chichenkov
8d2b9db430 Merge pull request #117 from guarzo/guarzo/import
Clean-up theme swap logic for nodes
2025-01-14 17:15:53 +03:00
CI
423ce343c7 chore: release version v1.40.0 2025-01-14 14:13:09 +00:00
Aleksei Chichenkov
1c17912d9f Merge pull request #113 from guarzo/guarzo/structure
feat(widget): add structure widget #46
2025-01-14 17:12:36 +03:00
Gustav
6714eb5d9b feat: add structure widget with timer and associated api 2025-01-14 08:49:25 -05:00
CI
1620e1fd21 chore: release version v1.39.3 2025-01-14 11:47:37 +00:00
Aleksei Chichenkov
859014874f Merge pull request #121 from wanderer-industries/windows-part-2
fix(Map): Add style of corners for windows. Add ability to reset widg…
2025-01-14 14:47:12 +03:00
achichenkov
ef44881f06 fix(Map): Add style of corners for windows. Add ability to reset widgets. A lot of refactoring 2025-01-14 14:40:06 +03:00
Gustav
b0532325fa refactor: encapsulate theme behavior toggles and fix eslint issues 2025-01-13 07:08:39 -05:00
CI
2c00bd426e chore: release version v1.39.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-13 11:12:24 +00:00
Dmitry Popov
6eccf2ac67 chore: release version v1.39.1 2025-01-13 12:11:55 +01:00
CI
973a1e54b3 chore: release version v1.39.1 2025-01-13 09:11:50 +00:00
Aleksei Chichenkov
2b42b637df Merge pull request #120 from wanderer-industries/feat-windows-new
Feat windows new
2025-01-13 12:11:25 +03:00
achichenkov
b950572818 Merge branch 'refs/heads/main' into feat-windows-new
# Conflicts:
#	assets/js/hooks/Mapper/mapRootProvider/MapRootProvider.tsx
2025-01-13 11:53:20 +03:00
CI
e470a210f1 chore: release version v1.39.0 2025-01-13 07:59:50 +00:00
Dmitry Popov
71ec2d413c Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-13 08:59:17 +01:00
Dmitry Popov
9122412558 feat(Map): Added option to show signature ID as system temporary name part 2025-01-13 08:59:13 +01:00
CI
0ba5c963b4 chore: release version v1.38.7
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-12 14:36:09 +00:00
Dmitry Popov
39a0ce284f Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-12 15:35:32 +01:00
Dmitry Popov
f9d580dbc0 chore: release version v1.38.4 2025-01-12 15:35:28 +01:00
CI
5c41574328 chore: release version v1.38.6 2025-01-12 14:19:26 +00:00
Dmitry Popov
f17d74c8b7 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-12 15:18:43 +01:00
Dmitry Popov
c88854c54c chore: release version v1.38.4 2025-01-12 15:18:39 +01:00
CI
f3779961d6 chore: release version v1.38.5 2025-01-12 13:58:52 +00:00
Dmitry Popov
d93fc29734 chore: release version v1.38.4 2025-01-12 14:58:12 +01:00
CI
c67918aca5 chore: release version v1.38.4
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-12 13:01:44 +00:00
Dmitry Popov
a9f276c95a Show sig ID (#119)
* feat(Map): Add option to show sig ID on system

* feat(Map): Add option to show sig ID in custom label
2025-01-12 17:01:08 +04:00
CI
7cee4894a5 chore: release version v1.38.3 2025-01-12 10:43:58 +00:00
Tsuro Tsero
edf8bef813 hotfix: ARM compatibility (#118)
* hotfix: add ARM64 compatible Docker image
2025-01-12 14:43:32 +04:00
achichenkov
2081218398 Merge branch 'refs/heads/main' into feat-windows-new 2025-01-11 13:37:33 +03:00
achichenkov
b100052453 fix(Map): New windows systems 2025-01-11 13:36:21 +03:00
CI
71636e895e chore: release version v1.38.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-11 08:40:26 +00:00
Dmitry Popov
7ff9689b76 fix: Fix connections remove timeouts 2025-01-11 09:39:55 +01:00
CI
5a4d819622 chore: release version v1.38.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-10 23:29:25 +00:00
guarzo
3117d85648 fix: restored default theme colors (#115) 2025-01-11 03:28:59 +04:00
CI
114133ecd2 chore: release version v1.38.0 2025-01-10 22:18:54 +00:00
Dmitry Popov
bf8a1197e4 feat(Map): Ability to store/view audit logs up to 3 months 2025-01-10 23:18:09 +01:00
Dmitry Popov
54c06a1fc0 feat(Map): Inroduced Env settings for connection auto EOL/remove timeouts
WANDERER_MAP_CONNECTION_AUTO_EXPIRE_HOURS (24)
WANDERER_MAP_CONNECTION_AUTO_EOL_HOURS (21)
WANDERER_MAP_CONNECTION_EOL_EXPIRE_TIMEOUT_MINS (30)
2025-01-10 23:17:05 +01:00
achichenkov
e77a42dfda Merge branch 'refs/heads/main' into feat-windows-new 2025-01-10 12:27:44 +03:00
CI
f83b4a2ba7 chore: release version v1.37.9
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-10 09:15:58 +00:00
guarzo
d34e7b8d8a fix: restore system status colors (#112)
* fix: restore system status colors
2025-01-10 13:15:31 +04:00
CI
fa0c7f3c66 chore: release version v1.37.8 2025-01-10 09:04:36 +00:00
guarzo
5f58645b41 fix: fix issue with newly added systems not adding a connection (#114)
* fix: resolve issue with newly added systems not connecting
2025-01-10 13:04:05 +04:00
achichenkov
7ae0ec7573 Merge branch 'refs/heads/main' into feat-windows-new 2025-01-10 11:46:21 +03:00
CI
b1149cecaf chore: release version v1.37.6
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-09 21:57:08 +00:00
Aleksei Chichenkov
8f28d2be65 Merge pull request #111 from guarzo/guarzo/themefixes
fix: support additional theme names
2025-01-10 00:56:39 +03:00
Gustav
d758b54ef8 fix: support additional theme names 2025-01-09 15:06:32 -05:00
Gustav
58293b4dc4 refactor: providing some additional variables for theme support 2025-01-09 14:24:03 -05:00
CI
f2083f4256 chore: release version v1.37.5
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-09 19:09:14 +00:00
Aleksei Chichenkov
6c7bd5804e Merge pull request #109 from guarzo/guarzo/themefixes
fix: restore node styling, simplify framework for new themes
2025-01-09 22:08:47 +03:00
Gustav
483ae21e89 fix: restore node styling, simplify framework for new themes 2025-01-09 13:38:11 -05:00
achichenkov
fc36d51e24 fix(Map): Add new windows system and removed old 2025-01-09 17:40:48 +03:00
CI
f734565844 chore: release version v1.37.4 2025-01-09 13:21:32 +00:00
CI
8c718ba181 chore: release version v1.37.3 2025-01-09 12:52:30 +00:00
achichenkov
8aaa2e7add Merge branch 'refs/heads/main' into feat-windows-new 2025-01-09 15:52:06 +03:00
Aleksei Chichenkov
c8d8734601 Merge pull request #108 from wanderer-industries/fix-dbclick
fix(Map): Fixed dbclick behaviour
2025-01-09 15:51:16 +03:00
achichenkov
5c757e8255 fix(Map): Fixed dbclick behaviour 2025-01-09 15:50:03 +03:00
achichenkov
22f608f302 Merge branch 'refs/heads/main' into feat-windows-new 2025-01-09 15:37:01 +03:00
CI
82f90ef759 chore: release version v1.37.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-09 07:26:08 +00:00
Aleksei Chichenkov
167c8eea6b Merge pull request #107 from guarzo/guarzo/theme-pathfinder
fix: fix route color
2025-01-09 10:25:37 +03:00
Gustav
d76079d4c7 theme cleanup 2025-01-08 23:45:27 -05:00
Gustav
bf9c4cda02 route color fix 2025-01-08 23:44:12 -05:00
CI
af00402546 chore: release version v1.37.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-08 21:53:07 +00:00
Aleksei Chichenkov
a245842ca4 Merge pull request #106 from guarzo/guarzo/themes
refactor: cleaned up scss classes, and updated theme dropdown to use declarative style
2025-01-09 00:52:36 +03:00
Gustav
8ddd672f13 fix: add back pathfinder theme font 2025-01-08 16:44:23 -05:00
Gustav
92f471c0b0 refactor: declarative dropdown for theme choice 2025-01-08 16:20:34 -05:00
Gustav
9e2a2c5b44 chore: clean up theme scss class usage 2025-01-08 16:20:25 -05:00
CI
5f5d3df003 chore: release version v1.37.0 2025-01-08 20:01:55 +00:00
Aleksei Chichenkov
c66cc8868e Merge pull request #100 from guarzo/guarzo/themes
feat: add theme selection, and pathfinder theme
2025-01-08 23:01:26 +03:00
guarzo
0d6528ce4f Merge branch 'main' into guarzo/themes 2025-01-08 14:05:59 -05:00
CI
34c385ac5f chore: release version v1.36.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-08 17:55:41 +00:00
Aleksei Chichenkov
b6d12e73a9 Merge pull request #105 from wanderer-industries/feat-wnd-104-fix-pasting
fix(Map): Fixed pasting into Name, Custom Label and Description
2025-01-08 20:55:16 +03:00
achichenkov
1118858120 fix(Map): Fixed pasting into Name, Custom Label and Description 2025-01-08 20:35:57 +03:00
CI
ae3a34d5bf chore: release version v1.36.1 2025-01-08 17:08:14 +00:00
Aleksei Chichenkov
43df42e49b Merge pull request #102 from wanderer-industries/feat-signatures-ru
fix(Map): Add support RU signatures and fix filtering
2025-01-08 20:07:43 +03:00
achichenkov
e670f3bf03 fix(Map): Removed unnecessary comment 2025-01-08 19:39:33 +03:00
achichenkov
c26a9404c5 fix(Map): Add support RU signatures and fix filtering 2025-01-08 12:46:56 +03:00
Gustav
c0fad4ca92 unused import 2025-01-07 18:44:49 -05:00
Gustav
16dbf9378b feat: add theme selection and pathfinder theme 2025-01-07 18:44:46 -05:00
CI
4001fe5eac chore: release version v1.36.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-07 23:28:11 +00:00
guarzo
2992dd8f8b feat: added static system info to api (#101)
* feat: added static system info to api
2025-01-08 03:27:44 +04:00
CI
98a03d1e59 chore: release version v1.35.0 2025-01-07 23:05:04 +00:00
guarzo
2088393c79 feat(Map): add "temporary system names" toggle (#86)
If enabled and set, a temporary name is displayed instead of the system name. The original system name appears on a secondary row if no custom name exists. Temporary names are removed when the system is removed from the map.
2025-01-08 03:04:36 +04:00
CI
093042b88a chore: release version v1.33.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-07 12:47:07 +00:00
Dmitry Popov
e5ef35c186 hotfix: Fix map behaviour for 'Allow only tracked characters' map option 2025-01-07 13:46:34 +01:00
CI
1cd23d5efd chore: release version v1.33.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-07 09:17:09 +00:00
guarzo
ead5818a3f feat(Map): api to allow systematic access to visible systems and tracked characters (#89)
* feat: add limited api for system and tracked characters
* env variable to disable public api key
2025-01-07 13:16:39 +04:00
CI
a5f66ada68 chore: release version v1.32.7
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-06 22:24:26 +00:00
Dmitry Popov
0919742853 Revert "hotfix: add ARM64 compatible Docker image (#94)" (#99)
This reverts commit f85317983c.
2025-01-07 02:23:59 +04:00
CI
f3efffd259 chore: release version v1.32.6 2025-01-06 21:56:57 +00:00
Tsuro Tsero
f85317983c hotfix: add ARM64 compatible Docker image (#94) 2025-01-07 01:56:22 +04:00
CI
76f709b768 chore: release version v1.32.5
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-04 20:23:09 +00:00
guarzo
e3b2356302 fix(map): prevent deselect on click to map (#96)
Fixes #80

- Prevents single node deselection on background / same node click
- Allows deseletion of all nodes if multiple are currently selected
2025-01-05 00:22:41 +04:00
CI
3d810211ee chore: release version v1.32.4
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-01-02 19:56:48 +00:00
Dmitry Popov
7453795dc5 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-01-02 20:56:18 +01:00
Dmitry Popov
9de7cd99ee fix(Map): Fix 'Character Activity' modal 2025-01-02 20:56:14 +01:00
CI
51489c1aa5 chore: release version v1.32.3 2025-01-02 17:23:05 +00:00
Dmitry Popov
25dd6de770 fix(Map): Fix 'Allow only tracked characters' saving 2025-01-02 18:22:32 +01:00
achichenkov
9727405194 fix(Map): First prototype of windows 2025-01-02 19:40:58 +03:00
CI
2a825f5a02 chore: release version v1.32.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-02 10:12:04 +00:00
guarzo
908d249eb9 Allow user to follow a specific tracked character (#87)
* add follow character functionality
2025-01-02 14:09:10 +04:00
CI
6cd119e8f4 chore: release version v1.32.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-25 13:34:54 +00:00
Dmitry Popov
9a59c8eb75 Merge pull request #81 from LordMrcS/main
Update Wormhole Path Colors for Reduced and VOC
2024-12-25 17:34:30 +04:00
Marcos Silva
452c022d41 Merge branch 'main' into main 2024-12-25 10:03:04 -03:00
CI
27e9bab82a chore: release version v1.32.0
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-24 09:02:07 +00:00
Aleksei Chichenkov
edef860530 Merge pull request #85 from wanderer-industries/search-systems
Improve Search systems for Routes and Map (Manually add system)
2024-12-24 12:01:40 +03:00
achichenkov
032cb63411 Merge branch 'refs/heads/main' into search-systems 2024-12-24 11:43:02 +03:00
achichenkov
a1791ba578 fix(Map): Added ability to add new system to routes via routes widget 2024-12-24 11:42:46 +03:00
Dmitry Popov
3a69fd7786 chore(Map): Get rid of old add system modal 2024-12-23 11:13:49 +01:00
achichenkov
8a90723c2e fix(Map): Reworked add system to map 2024-12-23 13:06:15 +03:00
Dmitry Popov
af2fc342c7 chore(Map): Add search systems 2024-12-23 10:44:46 +01:00
Dmitry Popov
05ea2fcdbe chore(Map): Add default filtering to search systems 2024-12-22 22:59:28 +01:00
Dmitry Popov
6d4321fead chore(Map): Add static info to search systems 2024-12-22 17:16:42 +01:00
CI
3f6364c9ea chore: release version v1.31.0
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-20 15:05:20 +00:00
Dmitry Popov
0d11b12282 feat(Core): Show tracking for new users by default. Auto link characters to account fix. Add character loading indicators. 2024-12-20 16:04:31 +01:00
Dmitry Popov
0796bcf7d0 feat(Map): Add search & update manual adding systems API 2024-12-18 11:13:20 +01:00
Dmitry Popov
0b5bec142a feat(Map): Add search & update manual adding systems API 2024-12-18 11:09:52 +01:00
CI
a5020b58f2 chore: release version v1.30.2
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-17 16:31:33 +00:00
Aleksei Chichenkov
f039a74a8f Merge pull request #84 from wanderer-industries/fix-ship-size
fix(Map): Fixed problem with ship size change.
2024-12-17 19:30:56 +03:00
achichenkov
0e6bb7390b fix(Map): Fixed problem with ship size change. 2024-12-17 19:10:26 +03:00
CI
b52b4eecca chore: release version v1.30.1 2024-12-17 12:26:49 +00:00
Aleksei Chichenkov
8186977d1d Merge pull request #83 from wanderer-industries/feat-set-wormhole-size
Feature (Map) - Improved connection size settings and some UI issues
2024-12-17 15:26:20 +03:00
achichenkov
86adcfe4d7 fix(Map): Little rework Signatures header: change System Signatures to Signatures, and show selected system name instead. 2024-12-17 14:32:35 +03:00
Dmitry Popov
ce2dd872c4 fix(Map): update default size of connections 2024-12-17 11:47:05 +01:00
achichenkov
aadc53c90e fix(Map): add ability set the size of wormhole and mark connection with label 2024-12-17 13:33:37 +03:00
CI
cbc1b6b5c8 chore: release version v1.30.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-16 18:49:37 +00:00
Aleksei Chichenkov
1aed7a9232 Merge pull request #82 from wanderer-industries/ui-issues-part-3
UI issues part 3
2024-12-16 21:49:11 +03:00
achichenkov
b549189644 fix(Map): fixed U210, K346 for C4 shattered systems 2024-12-16 17:13:33 +03:00
achichenkov
35279d17b4 fix(Map): fixed U210, K346 for shattered systems. Fixed mass of mediums chains. Fixed size of some capital chains from 3M to 3.3M. Based on https://whtype.info/ data. 2024-12-16 16:48:53 +03:00
achichenkov
bb403aa0c5 fix(Map): removed unnecessary log 2024-12-16 12:45:51 +03:00
achichenkov
04327c288b fix(Map): Uncomment what should not be commented 2024-12-16 12:43:26 +03:00
achichenkov
94d60e40d0 feat(Map): Fixed incorrect wrapping labels of checkboxes in System Signatures, Local and Routes. Also changed dotlan links for k-spacem now it leads to region map before, for wh all stay as it was. Added ability to chane to softer background and remove dots on background of map. Also some small design issues. #2 2024-12-16 12:42:24 +03:00
achichenkov
8505fcb6b7 feat(Map): Fixed incorrect wrapping labels of checkboxes in System Signatures, Local and Routes. Also changed dotlan links for k-spacem now it leads to region map before, for wh all stay as it was. Added ability to chane to softer background and remove dots on background of map. Also some small design issues. 2024-12-16 12:42:03 +03:00
Marcos Silva
e0a37f7635 Update Wormhole Path Colors for Reduced and VOC
Replaced the reduced and verge of collapse path colors to make it clearer.
2024-12-14 20:03:30 -03:00
CI
9aec57166d chore: release version v1.29.5
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-14 22:32:16 +00:00
Dmitry Popov
a3739f2950 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-14 23:31:47 +01:00
Dmitry Popov
3d3b152758 fix(Core): Fix character trackers cleanup 2024-12-14 23:31:42 +01:00
CI
0e03730543 chore: release version v1.29.4
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-10 11:01:09 +00:00
Dmitry Popov
97e07a6511 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-10 12:00:38 +01:00
Dmitry Popov
a77a51ba15 fix(Core): Small fixes 2024-12-10 12:00:34 +01:00
CI
42e706e1c2 chore: release version v1.29.3
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-07 18:02:05 +00:00
Dmitry Popov
025dd06053 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-07 19:01:36 +01:00
Dmitry Popov
bcb421d879 fix(Core): Increased eve DB data download timeout 2024-12-07 19:01:32 +01:00
CI
66056ab54b chore: release version v1.29.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-07 08:32:58 +00:00
Dmitry Popov
bb92f76ceb Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-07 09:32:28 +01:00
Dmitry Popov
84076b340b fix(Core): Fix unpkg CDN issues, fix Abyssals sites adding as systems on map 2024-12-07 09:32:24 +01:00
337 changed files with 18967 additions and 4370 deletions

View File

@@ -1,7 +1,12 @@
{
"name": "wanderer-dev",
"dockerComposeFile": ["./docker-compose.yml"],
"extensions": ["jakebecker.elixir-ls"],
"extensions": [
"jakebecker.elixir-ls",
"JakeBecker.elixir-ls",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"service": "wanderer",
"workspaceFolder": "/app",
"shutdownAction": "stopCompose",

View File

@@ -6,3 +6,6 @@ export EVE_CLIENT_WITH_WALLET_ID="<EVE_CLIENT_WITH_WALLET_ID>"
export EVE_CLIENT_WITH_WALLET_SECRET="<EVE_CLIENT_WITH_WALLET_SECRET>"
export GIT_SHA="1111"
export WANDERER_INVITES="false"
export WANDERER_PUBLIC_API_DISABLED="false"
export WANDERER_CHARACTER_API_DISABLED="false"
export WANDERER_ZKILL_PRELOAD_DISABLED="false"

View File

@@ -1,8 +1,6 @@
name: Build
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
push:
branches:
- main
@@ -41,8 +39,28 @@ jobs:
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
manual-approval:
name: Manual Approval
runs-on: ubuntu-latest
needs: deploy-test
if: success()
permissions:
issues: write
steps:
- name: Await Manual Approval
uses: trstringer/manual-approval@v1
with:
secret: ${{ github.TOKEN }}
approvers: DmitryPopov
minimum-approvals: 1
issue-title: "Manual Approval Required for Release"
issue-body: "Please approve or deny the deployment."
build:
name: 🛠 Build
needs: manual-approval
runs-on: ubuntu-22.04
if: ${{ (github.ref == 'refs/heads/main') && github.event_name == 'push' }}
permissions:
@@ -78,22 +96,23 @@ jobs:
fetch-depth: 0
- name: 😅 Cache deps
id: cache-deps
uses: actions/cache@v3
uses: actions/cache@v4
env:
cache-name: cache-elixir-deps
with:
path: deps
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
path: |
deps
key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.cache-name }}-
${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-
- name: 😅 Cache compiled build
id: cache-build
uses: actions/cache@v3
uses: actions/cache@v4
env:
cache-name: cache-compiled-build
with:
path: |
**/_build
_build
key: ${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}-${{ hashFiles( '**/lib/**/*.{ex,eex}', '**/config/*.exs', '**/mix.exs' ) }}
restore-keys: |
${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}-
@@ -122,6 +141,9 @@ jobs:
name: 🛠 Build Docker Images
needs: build
runs-on: ubuntu-22.04
outputs:
release-tag: ${{ steps.get-latest-tag.outputs.tag }}
release-notes: ${{ steps.get-content.outputs.string }}
permissions:
checks: write
contents: write
@@ -135,6 +157,7 @@ jobs:
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Prepare
run: |
@@ -183,15 +206,28 @@ jobs:
push: true
context: .
file: ./Dockerfile
tags: ${{ env.REGISTRY_IMAGE }}:latest,${{ env.REGISTRY_IMAGE }}:${{ steps.get-latest-tag.outputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
labels: ${{ steps.meta.outputs.labels }}
platforms: ${{ matrix.platform }}
outputs: type=image,"name=${{ env.REGISTRY_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
build-args: |
MIX_ENV=prod
BUILD_METADATA=${{ steps.meta.outputs.json }}
- name: Image digest
run: echo ${{ steps.build.outputs.digest }}
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
- uses: markpatterson27/markdown-to-output@v1
id: extract-changelog
@@ -211,16 +247,54 @@ jobs:
maxLength: 500
truncationSymbol: "…"
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
merge:
runs-on: ubuntu-latest
needs:
- docker
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ steps.get-content.outputs.string }}
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.WANDERER_DOCKER_USER }}
password: ${{ secrets.WANDERER_DOCKER_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}},value=${{ needs.docker.outputs.release-tag }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
create-release:
name: 🏷 Create Release
runs-on: ubuntu-22.04
needs: docker
needs: [docker, merge]
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
steps:
- name: ⬇️ Checkout repo
@@ -228,17 +302,11 @@ jobs:
with:
fetch-depth: 0
- name: Get Release Tag
id: get-latest-tag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
with:
fallback: 1.0.0
- name: 🏷 Create Draft Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.get-latest-tag.outputs.tag }}
name: Release ${{ steps.get-latest-tag.outputs.tag }}
tag_name: ${{ needs.docker.outputs.release-tag }}
name: Release ${{ needs.docker.outputs.release-tag }}
body: |
## Info
Commit ${{ github.sha }} was deployed to `staging`. [See code diff](${{ github.event.compare }}).
@@ -248,3 +316,9 @@ jobs:
## How to Promote?
In order to promote this to prod, edit the draft and press **"Publish release"**.
draft: true
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ needs.docker.outputs.release-notes }}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,9 @@ WORKDIR /app
# set build ENV
ENV MIX_ENV="prod"
# Set ERL_FLAGS for ARM compatibility
ENV ERL_FLAGS="+JPperf true"
# install mix dependencies
COPY mix.exs mix.lock ./
RUN rm -Rf _build deps && mix deps.get --only $MIX_ENV

View File

@@ -58,6 +58,7 @@ Now you can visit [`localhost:8000`](http://localhost:8000) from your browser.
- `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# npm install -g yarn`
- `root@0d0a785313b6:/app# mix setup`
- See how to run server in #Run section

View File

@@ -1,6 +1,3 @@
// import '@fontsource-variable/inter'
// import '@fontsource-variable/jetbrains-mono'
// import './lib/tailwind/index.css';
import './css/app.css';
import './lib/phoenix';

View File

@@ -3,6 +3,8 @@
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
/*@import 'primereact/resources/themes/bootstrap4-dark-blue/theme.css' layer(primereact);*/
@import '../js/hooks/Mapper/components/map/styles/index.scss';
@layer tailwind-base {
@tailwind base;
}
@@ -23,6 +25,10 @@ body {
width: 400px; /* As IE6 ignores !important it will set width as 400px; */
}
body > div:first-of-type {
min-height: 500px !important;
}
.lending-normal {
font-family: 'Shentox', 'Rogan', sans-serif !important;
font-weight: 500;
@@ -930,3 +936,66 @@ body {
width: 16px;
height: 16px;
}
.verticalTabsContainer {
display: flex;
width: 100%;
min-height: 300px;
}
.verticalTabsContainer .p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.verticalTabsContainer .p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
height: 100%;
}
.verticalTabsContainer .p-tabview-nav-container {
border-right: none;
height: 100%;
}
.verticalTabsContainer .p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
}
.verticalTabsContainer .p-tabview-nav li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition:
background-color 200ms,
border-right-color 200ms;
}
.verticalTabsContainer .p-tabview-nav li:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.verticalTabsContainer .p-tabview-nav li .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;
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected .p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected:hover {
border-right: 4px solid var(--primary-color);
}
.verticalTabsContainer .p-tabview-panel {
flex-grow: 1;
}

View File

@@ -108,3 +108,32 @@
.p-dropdown-empty-message {
padding: 0.25rem 0.5rem;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
margin-right: 0 !important;
}
/* Fixed sizes of Input switch */
.p-inputswitch {
width: 2.0rem;
height: 1.15rem;
.p-inputswitch-slider:before {
width: 0.8rem;
height: 0.8rem;
left: 0.14rem;
margin-top: -0.385rem;
}
&.p-highlight .p-inputswitch-slider:before {
transform: translateX(0.8rem);
}
&:not(.p-disabled):has(.p-inputswitch-input:hover) .p-inputswitch-slider {
background: rgb(255 255 255 / 21%);
}
&.p-highlight .p-inputswitch-slider {
background: #966d3d;
}
}

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -88,6 +88,23 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
setSystem(undefined);
}, []);
const onSystemTemporaryName = useCallback((temporaryName?: string) => {
const { system, outCommand } = ref.current;
if (!system) {
return;
}
outCommand({
type: OutCommand.updateSystemTemporaryName,
data: {
system_id: system,
value: temporaryName ?? '',
},
});
setSystem(undefined);
}, []);
const onSystemStatus = useCallback((status: number) => {
const { system, outCommand } = ref.current;
if (!system) {
@@ -161,6 +178,7 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
onLockToggle,
onHubToggle,
onSystemTag,
onSystemTemporaryName,
onSystemStatus,
onSystemLabels,
onOpenSettings,

View File

@@ -8,6 +8,7 @@ import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export const useContextMenuSystemItems = ({
onDeleteSystem,
@@ -44,6 +45,8 @@ export const useContextMenuSystemItems = ({
<FastSystemActions
systemId={systemId}
systemName={system.system_static_info.solar_system_name}
regionName={system.system_static_info.region_name}
isWH={isWormholeSpace(system.system_static_info.system_class)}
showEdit
onOpenSettings={onOpenSettings}
/>

View File

@@ -10,6 +10,7 @@ import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/ty
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { Route } from '@/hooks/Mapper/types/routes.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export interface ContextMenuSystemInfoProps {
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
@@ -48,7 +49,6 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
if (!systemId || !system) {
return [];
}
return [
{
className: classes.FastActions,
@@ -57,6 +57,8 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
<FastSystemActions
systemId={systemId}
systemName={system.solar_system_name}
regionName={system.region_name}
isWH={isWormholeSpace(system.system_class)}
onOpenSettings={onOpenSettings}
/>
);

View File

@@ -9,13 +9,22 @@ import { PrimeIcons } from 'primereact/api';
export interface FastSystemActionsProps {
systemId: string;
systemName: string;
regionName: string;
isWH: boolean;
showEdit?: boolean;
onOpenSettings(): void;
}
export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEdit }: FastSystemActionsProps) => {
const ref = useRef({ systemId, systemName });
ref.current = { systemId, systemName };
export const FastSystemActions = ({
systemId,
systemName,
regionName,
isWH,
onOpenSettings,
showEdit,
}: FastSystemActionsProps) => {
const ref = useRef({ systemId, systemName, regionName, isWH });
ref.current = { systemId, systemName, regionName, isWH };
const handleOpenZKB = useCallback(
() => window.open(`https://zkillboard.com/system/${ref.current.systemId}`, '_blank'),
@@ -27,10 +36,17 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
[],
);
const handleOpenDotlan = useCallback(
() => window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank'),
[],
);
const handleOpenDotlan = useCallback(() => {
if (ref.current.isWH) {
window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank');
return;
}
return window.open(
`https://evemaps.dotlan.net/map/${ref.current.regionName.replace(/ /gim, '_')}/${ref.current.systemName}#jumps`,
'_blank',
);
}, []);
const copySystemNameToClipboard = useCallback(async () => {
try {
@@ -43,9 +59,9 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
return (
<LayoutEventBlocker className={clsx('flex px-2 gap-2 justify-between items-center h-full')}>
<div className={clsx('flex gap-2 items-center h-full', classes.Links)}>
<WdImgButton source={ZKB_ICON} onClick={handleOpenZKB} />
<WdImgButton source={ANOIK_ICON} onClick={handleOpenAnoikis} />
<WdImgButton source={DOTLAN_ICON} onClick={handleOpenDotlan} />
<WdImgButton tooltip={{ content: 'Open zkillboard' }} source={ZKB_ICON} onClick={handleOpenZKB} />
<WdImgButton tooltip={{ content: 'Open Anoikis' }} source={ANOIK_ICON} onClick={handleOpenAnoikis} />
<WdImgButton tooltip={{ content: 'Open Dotlan' }} source={DOTLAN_ICON} onClick={handleOpenDotlan} />
</div>
<div className="flex gap-2 items-center pl-1">

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,2 +1,3 @@
export * from './useSystemInfo';
export * from './useGetOwnOnlineCharacters';
export * from './useElementWidth';

View File

@@ -0,0 +1,43 @@
import { useState, useLayoutEffect, RefObject } from 'react';
/**
* useElementWidth
*
* A custom hook that accepts a ref to an HTML element and returns its current width.
* It uses a ResizeObserver and window resize listener to update the width when necessary.
*
* @param ref - A RefObject pointing to an HTML element.
* @returns The current width of the element.
*/
export function useElementWidth<T extends HTMLElement>(ref: RefObject<T>): number {
const [width, setWidth] = useState<number>(0);
useLayoutEffect(() => {
const updateWidth = () => {
if (ref.current) {
const newWidth = ref.current.getBoundingClientRect().width;
if (newWidth > 0) {
setWidth(newWidth);
}
}
};
updateWidth(); // Initial measurement
const observer = new ResizeObserver(() => {
const id = setTimeout(updateWidth, 100);
return () => clearTimeout(id);
});
if (ref.current) {
observer.observe(ref.current);
}
window.addEventListener("resize", updateWidth);
return () => {
observer.disconnect();
window.removeEventListener("resize", updateWidth);
};
}, [ref]);
return width;
}

View File

@@ -1,4 +1,11 @@
.MapRoot {
width: 100%;
height: 100%;
background-color: var(--rf-bg-color, #0C0A09);
&.BackgroundAlternateColor {
background-color: var(--rf-soft-bg-color, #171717);
--rf-node-bg-color: var(--rf-node-soft-bg-color, #202020);
}
}

View File

@@ -1,8 +1,8 @@
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect } from 'react';
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } from 'react';
import ReactFlow, {
Background,
ConnectionMode,
Edge,
EdgeChange,
MiniMap,
Node,
NodeChange,
@@ -16,24 +16,24 @@ import ReactFlow, {
} 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 { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
import { useEdgesState, useMapHandlers, useNodesState, useUpdateNodes } from './hooks';
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import {
ContextMenuConnection,
ContextMenuRoot,
SolarSystemEdge,
SolarSystemNode,
useContextMenuConnectionHandlers,
useContextMenuRootHandlers,
} from './components';
import { OnMapSelectionChange } from './map.types';
import { getBehaviorForTheme } from './helpers/getThemeBehavior';
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
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 clsx from 'clsx';
import { useBackgroundVars } from './hooks/useBackgroundVars';
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
@@ -75,12 +75,6 @@ const initialEdges = [
},
];
const nodeTypes = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
custom: SolarSystemNode,
} as never;
const edgeTypes = {
floating: SolarSystemEdge,
};
@@ -91,12 +85,16 @@ interface MapCompProps {
onSelectionChange: OnMapSelectionChange;
onManualDelete(systems: string[]): void;
onConnectionInfoClick?(e: SolarSystemConnection): void;
onAddSystem?: OnMapAddSystemCallback;
onSelectionContextMenu?: NodeSelectionMouseHandler;
minimapClasses?: string;
isShowMinimap?: boolean;
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
showKSpaceBG?: boolean;
isThickConnections?: boolean;
isShowBackgroundPattern?: boolean;
isSoftBackground?: boolean;
theme?: string;
}
const MapComp = ({
@@ -111,16 +109,28 @@ const MapComp = ({
isShowMinimap,
showKSpaceBG,
isThickConnections,
isShowBackgroundPattern,
isSoftBackground,
theme,
onAddSystem,
}: MapCompProps) => {
const { getNode } = useReactFlow();
const { getNode, getNodes } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
useMapHandlers(refn, onSelectionChange);
useUpdateNodes(nodes);
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
const { update } = useMapState();
const { variant, gap, size, color } = useBackgroundVars(theme);
const { isPanAndDrag, nodeComponent, connectionMode } = getBehaviorForTheme(theme || 'default');
const nodeTypes = useMemo(() => {
return {
custom: nodeComponent,
};
}, [nodeComponent]);
const onConnect: OnConnect = useCallback(
params => {
@@ -177,33 +187,40 @@ const MapComp = ({
const handleNodesChange = useCallback(
(changes: NodeChange[]) => {
const systemsIdsToRemove: string[] = [];
// const systemsIdsToRemove: string[] = [];
// prevents single node deselection on background / same node click
// allows deseletion of all nodes if multiple are currently selected
if (changes.length === 1 && changes[0].type == 'select' && changes[0].selected === false) {
changes[0].selected = getNodes().filter(node => node.selected).length === 1;
}
const nextChanges = changes.reduce((acc, change) => {
if (change.type !== 'remove') {
return [...acc, change];
}
const node = getNode(change.id);
if (!node) {
return [...acc, change];
}
if (node.data.locked) {
return acc;
}
systemsIdsToRemove.push(node.data.id);
return [...acc, change];
// if (change.type !== 'remove') {
// }
// const node = getNode(change.id);
// if (!node) {
// return [...acc, change];
// }
// if (node.data.locked) {
// return acc;
// }
// systemsIdsToRemove.push(node.data.id);
// return [...acc, change];
}, [] as NodeChange[]);
if (systemsIdsToRemove.length > 0) {
onManualDelete(systemsIdsToRemove);
}
// if (systemsIdsToRemove.length > 0) {
// onManualDelete(systemsIdsToRemove);
// }
onNodesChange(nextChanges);
},
[getNode, onManualDelete, onNodesChange],
[getNode, getNodes, onManualDelete, onNodesChange],
);
useEffect(() => {
@@ -216,7 +233,7 @@ const MapComp = ({
return (
<>
<div className={classes.MapRoot}>
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
<ReactFlow
nodes={nodes}
edges={edges}
@@ -228,7 +245,7 @@ const MapComp = ({
defaultViewport={getViewPortFromStore()}
edgeTypes={edgeTypes}
nodeTypes={nodeTypes}
connectionMode={ConnectionMode.Loose}
connectionMode={connectionMode}
snapToGrid
nodeDragThreshold={10}
onNodeDragStop={handleDragStop}
@@ -236,6 +253,10 @@ const MapComp = ({
onConnectStart={() => update({ isConnecting: true })}
onConnectEnd={() => update({ isConnecting: false })}
onNodeMouseEnter={(_, node) => update({ hoverNodeId: node.id })}
onPaneClick={event => {
event.preventDefault();
event.stopPropagation();
}}
// onKeyUp=
onNodeMouseLeave={() => update({ hoverNodeId: null })}
onEdgeClick={(_, t) => {
@@ -256,14 +277,20 @@ const MapComp = ({
minZoom={0.2}
maxZoom={1.5}
elevateNodesOnSelect
deleteKeyCode={['Delete']}
deleteKeyCode={['']}
{...(isPanAndDrag
? {
selectionOnDrag: true,
panOnDrag: [2],
}
: {})}
// 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
selectionMode={SelectionMode.Partial}
>
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
<Background />
{isShowBackgroundPattern && <Background variant={variant} gap={gap} size={size} color={color} />}
</ReactFlow>
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
Test // DON NOT REMOVE

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext } from 'react';
import { OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { MapUnionTypes } from '@/hooks/Mapper/types';
import { MapUnionTypes, SystemSignature } from '@/hooks/Mapper/types';
import { ContextStoreDataUpdate, useContextStore } from '@/hooks/Mapper/utils';
export type MapData = MapUnionTypes & {
@@ -9,6 +9,7 @@ export type MapData = MapUnionTypes & {
visibleNodes: Set<string>;
showKSpaceBG: boolean;
isThickConnections: boolean;
linkedSigEveId: string;
};
interface MapProviderProps {
@@ -29,10 +30,14 @@ const INITIAL_DATA: MapData = {
isConnecting: false,
connections: [],
hoverNodeId: null,
linkedSigEveId: '',
visibleNodes: new Set(),
showKSpaceBG: false,
isThickConnections: false,
userPermissions: {},
systemSignatures: {} as Record<string, SystemSignature[]>,
options: {} as Record<string, string | boolean>,
isSubscriptionActive: false,
};
export interface MapContextProps {

View File

@@ -1,15 +1,17 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
.ConnectionTimeEOL {
background-image: linear-gradient(207deg, transparent, #7452c3e3);
background-image: linear-gradient(207deg, transparent, var(--conn-time-eol));
}
.ConnectionFrigate {
background-image: linear-gradient(207deg, transparent, #325d88);
background-image: linear-gradient(207deg, transparent, var(--conn-frigate));
}
.ConnectionSave {
background-image: linear-gradient(207deg, transparent, rgba(155, 102, 45, 0.85));
background-image: linear-gradient(207deg, transparent, var(--conn-save));
}
.SelectedItem {
background-color: rgba(98, 98, 98, 0.33);
background-color: var(--selected-item-bg);
}

View File

@@ -6,7 +6,14 @@ import { Edge } from '@reactflow/core/dist/esm/types/edges';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import clsx from 'clsx';
import classes from './ContextMenuConnection.module.scss';
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
import {
MASS_STATE_NAMES,
MASS_STATE_NAMES_ORDER,
SHIP_SIZES_NAMES,
SHIP_SIZES_NAMES_ORDER,
SHIP_SIZES_NAMES_SHORT,
SHIP_SIZES_SIZE,
} from '@/hooks/Mapper/components/map/constants.ts';
export interface ContextMenuConnectionProps {
contextMenuRef: RefObject<ContextMenu>;
@@ -48,10 +55,6 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
icon: PrimeIcons.CLOCK,
command: onChangeTimeState,
},
]
: []),
...(isWormhole
? [
{
label: `Frigate`,
className: clsx({
@@ -60,13 +63,9 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
icon: PrimeIcons.CLOUD,
command: () =>
onChangeShipSizeStatus(
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.large : ShipSizeStatus.small,
),
},
]
: []),
...(isWormhole
? [
{
label: `Save mass`,
className: clsx({
@@ -75,19 +74,40 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
icon: PrimeIcons.LOCK,
command: () => onToggleMassSave(!edge.data?.locked),
},
]
: []),
...(isWormhole && !isFrigateSize
? [
...(!isFrigateSize
? [
{
label: `Mass status`,
icon: PrimeIcons.CHART_PIE,
items: MASS_STATE_NAMES_ORDER.map(x => ({
label: MASS_STATE_NAMES[x],
className: clsx({
[classes.SelectedItem]: edge.data?.mass_status === x,
}),
command: () => onChangeMassState(x),
})),
},
]
: []),
{
label: `Mass status`,
icon: PrimeIcons.CHART_PIE,
items: MASS_STATE_NAMES_ORDER.map(x => ({
label: MASS_STATE_NAMES[x],
label: `Ship Size`,
icon: PrimeIcons.CLOUD,
items: SHIP_SIZES_NAMES_ORDER.map(x => ({
label: (
<div className="grid grid-cols-[20px_120px_1fr_40px] gap-2 items-center">
<div className="text-[12px] font-bold text-stone-400">{SHIP_SIZES_NAMES_SHORT[x]}</div>
<div>{SHIP_SIZES_NAMES[x]}</div>
<div></div>
<div className="flex justify-end whitespace-nowrap text-[12px] font-bold text-stone-500">
{SHIP_SIZES_SIZE[x]} t.
</div>
</div>
) as unknown as string, // TODO my lovely kostyl
className: clsx({
[classes.SelectedItem]: edge.data?.mass_status === x,
[classes.SelectedItem]: edge.data?.ship_size_type === x,
}),
command: () => onChangeMassState(x),
command: () => onChangeShipSizeStatus(x),
})),
},
]
@@ -98,7 +118,7 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
command: onDeleteConnection,
},
];
}, [edge, onChangeTimeState, onDeleteConnection, onChangeMassState, onChangeShipSizeStatus]);
}, [edge, onChangeTimeState, onDeleteConnection, onChangeShipSizeStatus, onToggleMassSave, onChangeMassState]);
return (
<>

View File

@@ -97,14 +97,16 @@ export const useContextMenuConnectionHandlers = () => {
},
});
outCommand({
type: OutCommand.updateConnectionMassStatus,
data: {
source: edge.source,
target: edge.target,
value: MassState.normal,
},
});
if (status === ShipSizeStatus.small) {
outCommand({
type: OutCommand.updateConnectionMassStatus,
data: {
source: edge.source,
target: edge.target,
value: MassState.normal,
},
});
}
}, []);
const onToggleMassSave = useCallback((locked: boolean) => {

View File

@@ -1,14 +1,16 @@
import { useReactFlow, XYPosition } from 'reactflow';
import React, { useRef, useState } from 'react';
import React, { useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { useMapState } from '../../MapProvider.tsx';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { OnMapAddSystemCallback } from '@/hooks/Mapper/components/map/map.types.ts';
export const useContextMenuRootHandlers = () => {
type UseContextMenuRootHandlers = {
onAddSystem?: OnMapAddSystemCallback;
};
export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHandlers = {}) => {
const rf = useReactFlow();
const contextMenuRef = useRef<ContextMenu | null>(null);
const { outCommand } = useMapState();
const [position, setPosition] = useState<XYPosition | null>(null);
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
@@ -18,14 +20,17 @@ export const useContextMenuRootHandlers = () => {
contextMenuRef.current?.show(e);
};
const onAddSystem = () => {
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
};
const ref = useRef({ onAddSystem, position });
ref.current = { onAddSystem, position };
const onAddSystemCallback = useCallback(() => {
ref.current.onAddSystem?.({ coordinates: position });
}, [position]);
return {
handleRootContext,
contextMenuRef,
onAddSystem,
onAddSystem: onAddSystemCallback,
};
};

View File

@@ -1,23 +1,47 @@
@import "@/hooks/Mapper/components/map/styles/neon-variables";
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
.react-flow__edge.selected {
.EdgePathBack {
stroke: $pastel-yellow;
.EdgePathBack {
fill: none;
stroke: #80a5c5;
stroke-width: 3px;
&.TimeCrit {
stroke: #f11ab2;
stroke-width: 4px;
}
&.Hovered {
stroke: #b5c8d9;
&.TimeCrit {
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
}
}
.EdgePathFront {
fill: none;
stroke: #2c3844;
stroke-width: 2px;
&.MassVerge:not(&.Frigate) {
stroke: #af2900;
stroke: #af0000;
}
&.MassHalf:not(&.Frigate) {
stroke: #a85f00;
stroke: #ffd700;
}
&.Frigate {
@@ -54,46 +78,29 @@
}
}
.EdgePathBack {
fill: none;
stroke: #80a5c5;
stroke-width: 3px;
&.TimeCrit {
stroke: #f11ab2;
stroke-width: 4px;
}
&.Hovered {
stroke: #b5c8d9;
&.TimeCrit {
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
}
}
.ClickPath {
fill: none;
stroke: none;
stroke-width: 8px;
}
.LinkLabel{
.Handle {
border: 1px solid var(--pastel-blue);
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
}
&.Right {
margin-left: 0px;
}
}
.LinkLabel {
font-size: 9px;
line-height: 10px;
padding: 2px 4px;
@@ -109,22 +116,3 @@
height: 8px;
font-size: 8px;
}
.Handle {
min-width: initial;
min-height: initial;
border: 1px solid #5a7d9a;
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
&.Right {
margin-left: 0px;
}
}
}

View File

@@ -8,6 +8,7 @@ import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeS
import { PrimeIcons } from 'primereact/api';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { SHIP_SIZES_DESCRIPTION, SHIP_SIZES_NAMES_SHORT } from '@/hooks/Mapper/components/map/constants.ts';
const MAP_TRANSLATES: Record<string, string> = {
[Position.Top]: 'translate(-48%, 0%)',
@@ -30,6 +31,14 @@ const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
[Position.Right]: { x: 0, y: 0 },
};
export const SHIP_SIZES_COLORS = {
[ShipSizeStatus.small]: 'bg-indigo-400',
[ShipSizeStatus.medium]: 'bg-cyan-500',
[ShipSizeStatus.large]: '',
[ShipSizeStatus.freight]: 'bg-lime-400',
[ShipSizeStatus.capital]: 'bg-red-400',
};
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
@@ -121,7 +130,7 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
/>
<div
className="absolute flex items-center gap-1"
className="absolute flex items-center gap-1 pointer-events-none"
style={{
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
@@ -137,6 +146,19 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
</WdTooltipWrapper>
)}
{isWormhole && data.ship_size_type !== ShipSizeStatus.large && (
<WdTooltipWrapper
content={SHIP_SIZES_DESCRIPTION[data.ship_size_type]}
className={clsx(
classes.LinkLabel,
'pointer-events-auto rounded opacity-100 cursor-auto text-neutral-900 font-bold',
SHIP_SIZES_COLORS[data.ship_size_type],
)}
>
{SHIP_SIZES_NAMES_SHORT[data.ship_size_type]}
</WdTooltipWrapper>
)}
</div>
</EdgeLabelRenderer>
</>

View File

@@ -0,0 +1,48 @@
.KillsBookmark {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 8px;
font-weight: 700;
border: 0;
border-radius: 5px 5px 0 0;
padding: 4px 3px;
}
.KillsBookmarkWithIcon {
display: flex;
align-items: center;
justify-content: center;
margin-top: -2px;
text-shadow: 0 0 3px #000;
padding-right: 2px;
height: 8px;
font-size: 8px;
line-height: 12px;
font-weight: 700;
text-size-adjust: 100%;
.pi {
font-size: 9px;
}
.text {
font-size: 9px;
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
}
.TooltipContainer {
background-color: #1a1a1a;
color: #fff;
padding: 3px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
border-radius: 2px;
pointer-events: auto;
max-width: 500px;
max-height: 300px;
overflow-y: auto;
overflow-x: hidden;
}

View File

@@ -0,0 +1,39 @@
import { SystemKillsContent } from '../../../mapInterface/widgets/SystemKills/SystemKillsContent/SystemKillsContent';
import { useKillsCounter } from '../../hooks/useKillsCounter';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { WithChildren, WithClassName } from '@/hooks/Mapper/types/common';
type TooltipSize = 'xs' | 'sm' | 'md' | 'lg';
type KillsBookmarkTooltipProps = {
killsCount: number;
killsActivityType: string | null;
systemId: string;
className?: string;
size?: TooltipSize;
} & WithChildren &
WithClassName;
export const KillsCounter = ({ killsCount, systemId, className, children, size = 'xs' }: KillsBookmarkTooltipProps) => {
const { isLoading, kills: detailedKills, systemNameMap } = useKillsCounter({ realSystemId: systemId });
if (!killsCount || detailedKills.length === 0 || !systemId || isLoading) return null;
const tooltipContent = (
<div style={{ width: '100%', minWidth: '300px', overflow: 'hidden' }}>
<SystemKillsContent
kills={detailedKills}
systemNameMap={systemNameMap}
onlyOneSystem={true}
autoSize={true}
limit={killsCount}
/>
</div>
);
return (
<WdTooltipWrapper content={tooltipContent} className={className} size={size} interactive={true}>
{children}
</WdTooltipWrapper>
);
};

View File

@@ -0,0 +1,54 @@
.TooltipActive {
pointer-events: auto !important;
position: relative;
z-index: 3;
}
.hoverTarget {
padding: 0.5rem;
margin: -0.5rem;
display: inline-block;
}
.localCounter {
mix-blend-mode: screen;
display: flex;
align-items: center;
gap: 1px;
position: relative;
top: 1px;
color: var(--rf-node-local-counter);
&.hasUserCharacters {
color: var(--rf-has-user-characters);
}
& > i {
font-size: 9px;
position: relative;
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: var(--rf-local-counter-font-weight, 500);
@-moz-document url-prefix() {
position: relative;
top: -1px;
}
}
}
.Pathfinder {
.localCounter {
@-moz-document url-prefix() {
top: 0;
}
& > span {
position: relative;
top: -1px;
}
}
}

View File

@@ -0,0 +1,61 @@
import { useMemo } from 'react';
import clsx from 'clsx';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit/WdTooltip';
import { CharItemProps, LocalCharactersList } from '../../../mapInterface/widgets/LocalCharacters/components';
import { useLocalCharactersItemTemplate } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalWidgetSettings';
import classes from './SolarSystemLocalCounter.module.scss';
import { AvailableThemes } from '@/hooks/Mapper/mapRootProvider';
import { useTheme } from '@/hooks/Mapper/hooks/useTheme.ts';
interface LocalCounterProps {
localCounterCharacters: Array<CharItemProps>;
hasUserCharacters: boolean;
showIcon?: boolean;
}
export const LocalCounter = ({ localCounterCharacters, hasUserCharacters, showIcon = true }: LocalCounterProps) => {
const [settings] = useLocalCharacterWidgetSettings();
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
const theme = useTheme();
const pilotTooltipContent = useMemo(() => {
return (
<div
style={{
width: '100%',
minWidth: '300px',
overflow: 'hidden',
}}
>
<LocalCharactersList items={localCounterCharacters} itemTemplate={itemTemplate} itemSize={26} autoSize={true} />
</div>
);
}, [localCounterCharacters, itemTemplate]);
if (localCounterCharacters.length === 0) {
return null;
}
return (
<div
className={clsx(classes.TooltipActive, {
[classes.Pathfinder]: theme === AvailableThemes.pathfinder,
})}
>
<WdTooltipWrapper content={pilotTooltipContent} position={TooltipPosition.right} offset={0} interactive={true}>
<div className={clsx(classes.hoverTarget)}>
<div
className={clsx(classes.localCounter, {
[classes.hasUserCharacters]: hasUserCharacters,
})}
>
{showIcon && <i className="pi pi-users" />}
<span>{localCounterCharacters.length}</span>
</div>
</div>
</WdTooltipWrapper>
</div>
);
};

View File

@@ -1,324 +0,0 @@
import { memo, useMemo } from 'react';
import { Handle, Position, WrapNodeProps } from 'reactflow';
import { MapSolarSystemType } from '../../map.types';
import classes from './SolarSystemNode.module.scss';
import clsx from 'clsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import {
EFFECT_BACKGROUND_STYLES,
LABELS_INFO,
LABELS_ORDER,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
} from '@/hooks/Mapper/components/map/constants.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { PrimeIcons } from 'primereact/api';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { OutCommand } from '@/hooks/Mapper/types';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: classes.Caldaria,
[Spaces.Matar]: classes.Mataria,
[Spaces.Amarr]: classes.Amarria,
[Spaces.Gallente]: classes.Gallente,
};
const sortedLabels = (labels: string[]) => {
if (!labels) {
return [];
}
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
};
export const getActivityType = (count: number) => {
if (count <= 5) {
return 'activityNormal';
}
if (count <= 30) {
return 'activityWarn';
}
return 'activityDanger';
};
// eslint-disable-next-line react/display-name
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
const { interfaceSettings } = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const {
system_class,
security,
class_title,
solar_system_id,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = data.system_static_info;
const signatures = data.system_signatures;
const { locked, name, tag, status, labels, id } = data || {};
const customName = solar_system_name !== name ? name : undefined;
const {
data: {
characters,
presentCharacters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
// eslint-disable-next-line
}, [characters, presentCharacters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
() => getSystemClassStyles({ systemClass: system_class, security }),
[security, system_class],
);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
const lebM = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(lebM.list), [lebM]);
const labelCustom = useMemo(() => lebM.customLabel, [lebM]);
const killsCount = useMemo(() => {
const systemKills = kills[solar_system_id];
if (!systemKills) {
return null;
}
return systemKills;
}, [kills, solar_system_id]);
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: {
system_id: solar_system_id.toString(),
},
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
signatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
})),
);
}, [isShowUnsplashedSignatures, signatures]);
return (
<>
{visible && (
<div className={classes.Bookmarks}>
{labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
</div>
)}
{is_shattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{killsCount && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[getActivityType(killsCount)])}>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{killsCount}</span>
</div>
</div>
)}
{labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
[classes.selected]: selected,
})}
>
{visible && (
<>
<div className={classes.HeadRow}>
<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,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{solar_system_name}
</div>
{isWormhole && (
<div className={classes.statics}>
{sortedStatics.map(x => (
<WormholeClassComp key={x} id={x} />
))}
</div>
)}
{effect_name !== null && isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{customName && (
<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-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{region_name}
</div>
)}
{isWormhole && !customName && <div />}
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{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', fontWeight: 'bold' }}></i>
)}
{charactersInSystem.length > 0 && (
<div className={clsx(classes.localCounter, { ['text-amber-300']: hasUserCharacters })}>
<i className="pi pi-users" style={{ fontSize: '0.50rem' }}></i>
<span className="font-sans">{charactersInSystem.length}</span>
</div>
)}
</div>
</div>
</div>
</>
)}
</div>
{visible && isShowUnsplashedSignatures && (
<div className={classes.Unsplashed}>
{unsplashedLeft.map(x => (
<UnsplashedSignature key={x.sig_id} signature={x} />
))}
</div>
)}
{visible && isShowUnsplashedSignatures && (
<div className={clsx([classes.Unsplashed, classes['Unsplashed--right']])}>
{unsplashedRight.map(x => (
<UnsplashedSignature key={x.sig_id} signature={x} />
))}
</div>
)}
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});

View File

@@ -2,27 +2,30 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020; // Dark background for tooltips
$tooltip-bg: #202020;
.RootCustomNode {
display: flex;
width: 130px;
height: 34px;
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
flex-direction: column;
padding: 2px 6px;
font-size: 10px;
background-color: $tooltip-bg;
background-color: var(--rf-node-bg-color, #202020) !important;
color: var(--rf-text-color, #ffffff);
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
border: 1px solid darken($pastel-blue, 10%);
border-radius: 5px;
position: relative;
z-index: 1;
z-index: 3;
overflow: hidden;
&.Mataria,
@@ -85,51 +88,40 @@ $tooltip-bg: #202020; // Dark background for tooltips
box-shadow: 0 0 10px #9a1af1c2;
}
.tooltip {
background-color: $tooltip-bg;
color: $text-color;
padding: 5px 10px;
border-radius: 3px;
border: 1px solid $pastel-pink;
}
&.eve-system-status-home {
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(45deg, var(--eve-solar-system-status-color-background), transparent);
&.selected {
border-color: $eve-solar-system-status-color-home;
border-color: var(--eve-solar-system-status-color-home);
}
}
&.eve-system-status-friendly {
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly-dark30), transparent);
&.selected {
border-color: darken($eve-solar-system-status-color-friendly, 5%);
border-color: var(--eve-solar-system-status-color-friendly-dark5);
}
}
&.eve-system-status-lookingFor {
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
&.selected {
border-color: $pastel-pink;
}
}
&.eve-system-status-warning {
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-warning), transparent);
}
&.eve-system-status-dangerous {
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-dangerous), transparent);
}
&.eve-system-status-target {
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-target), transparent);
}
}
@@ -154,8 +146,6 @@ $tooltip-bg: #202020; // Dark background for tooltips
padding-left: 3px;
padding-right: 3px;
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
@@ -242,26 +232,17 @@ $tooltip-bg: #202020; // Dark background for tooltips
.TagTitle {
font-size: 11px;
font-weight: bold;
font-weight: 500;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
color: #ffb01d;
color: var(--rf-tag-color, #38bdf8);
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
font-weight: bold;
}
}
.classSystemName {
//font-weight: bold;
}
.solarSystemName {
}
}
.BottomRow {
@@ -270,22 +251,23 @@ $tooltip-bg: #202020; // Dark background for tooltips
align-items: center;
height: 19px;
.localCounter {
display: flex;
//align-items: center;
gap: 2px;
& > i {
position: relative;
top: 1px;
.hasLocalCounter {
margin-right: 2px;
&.countAbove9 {
margin-right: 1.5rem;
}
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: 500;
//margin-top: 1px;
}
.lockIcon {
font-size: 0.45rem;
font-weight: bold;
position: relative;
}
.mapMarker {
font-size: 0.45rem;
font-weight: bold;
position: relative;
}
}
@@ -316,7 +298,8 @@ $tooltip-bg: #202020; // Dark background for tooltips
.Handlers {
position: absolute;
z-index: 2;
z-index: 4;
pointer-events: none;
top: 0;
left: 0;
width: 100%;
@@ -329,6 +312,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
border: 1px solid $pastel-blue;
width: 5px;
height: 5px;
pointer-events: auto;
&.selected {
border-color: $pastel-pink;

View File

@@ -0,0 +1,209 @@
import { memo } from 'react';
import { MapSolarSystemType } from '../../map.types';
import { Handle, NodeProps, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeDefault.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useSolarSystemNode, useNodeKillsCount } from '../../hooks/useSolarSystemLogic';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
return (
<>
{nodeVars.visible && (
<div className={classes.Bookmarks}>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size="lg"
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</KillsCounter>
)}
{nodeVars.labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
nodeVars.classTitleColor,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
)}
>
{nodeVars.classTitle ?? '-'}
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{nodeVars.tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{nodeVars.systemName}
</div>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={String(whClass)} id={String(whClass)} />
))}
</div>
)}
{nodeVars.effectName !== null && nodeVars.isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[nodeVars.effectName])} />
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{nodeVars.customName && (
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{nodeVars.customName}
</div>
)}
{!nodeVars.isWormhole && !nodeVars.customName && (
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{nodeVars.regionName}
</div>
)}
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
)}
</div>
{nodeVars.visible && (
<>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
</>
)}
<div className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});
SolarSystemNodeDefault.displayName = 'SolarSystemNodeDefault';

View File

@@ -0,0 +1,20 @@
@import './SolarSystemNodeDefault.module.scss';
/* ---------------------------------------------
Only override what's different from the base
Currently none required
---------------------------------------------- */
.RootCustomNode {
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-home),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
}
}
}

View File

@@ -0,0 +1,219 @@
import { memo } from 'react';
import { MapSolarSystemType } from '../../map.types';
import { Handle, NodeProps, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeTheme.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks/useSolarSystemLogic';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
return (
<>
{nodeVars.visible && (
<div className={classes.Bookmarks}>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size="lg"
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</KillsCounter>
)}
{nodeVars.labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
nodeVars.classTitleColor,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
)}
>
{nodeVars.classTitle ?? '-'}
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle)}>{nodeVars.tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap',
)}
>
{nodeVars.systemName}
</div>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={String(whClass)} id={String(whClass)} />
))}
</div>
)}
{nodeVars.effectName !== null && nodeVars.isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[nodeVars.effectName])} />
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{nodeVars.customName && (
<div
className={clsx(
classes.CustomName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{nodeVars.customName}
</div>
)}
{!nodeVars.isWormhole && !nodeVars.customName && (
<div
className={clsx(
classes.RegionName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{nodeVars.regionName}
</div>
)}
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
)}
</div>
{nodeVars.visible && (
<>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
</>
)}
<div className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});
SolarSystemNodeTheme.displayName = 'SolarSystemNodeTheme';

View File

@@ -1 +1,2 @@
export * from './SolarSystemNode';
export * from './SolarSystemNodeDefault';
export * from './SolarSystemNodeTheme';

View File

@@ -9,10 +9,14 @@
width: 13px;
height: 4px;
border-radius: 4px;
color: #ffffff;
color: var(--text-color);
font-size: 8px;
text-align: center;
font-weight: bolder;
display: block;
}
& > .Eol {
display: block;
}
}

View File

@@ -8,8 +8,8 @@ import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper
import { useMemo } from 'react';
import clsx from 'clsx';
import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
import { k162Types } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
import { K162_TYPES_MAP } from '@/hooks/Mapper/constants.ts';
import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureCustomInfo.ts';
interface UnsplashedSignatureProps {
signature: SystemSignature;
@@ -22,17 +22,22 @@ export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) =>
const whData = useMemo(() => wormholesData[signature.type], [signature.type, wormholesData]);
const whClass = useMemo(() => (whData ? WORMHOLES_ADDITIONAL_INFO[whData.dest] : null), [whData]);
const k162TypeOption = useMemo(() => {
if (!signature.custom_info) {
return null;
}
const customInfo = JSON.parse(signature.custom_info);
if (!customInfo.k162Type) {
return null;
}
return k162Types.find(x => x.value === customInfo.k162Type);
const customInfo = useMemo(() => {
return parseSignatureCustomInfo(signature.custom_info);
}, [signature]);
const k162TypeOption = useMemo(() => {
if (!customInfo?.k162Type) {
return null;
}
return K162_TYPES_MAP[customInfo.k162Type];
}, [customInfo]);
const isEOL = useMemo(() => {
return customInfo?.isEOL;
}, [customInfo]);
const whClassStyle = useMemo(() => {
if (signature.type === 'K162' && k162TypeOption) {
const k162Data = wormholesData[k162TypeOption.whClassName];
@@ -45,19 +50,19 @@ export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) =>
return (
<WdTooltipWrapper
className={clsx(classes.Signature)}
// @ts-ignore
content={
(
<div className="flex flex-col gap-1">
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
{renderInfoColumn(signature)}
</InfoDrawer>
</div>
) as React.ReactNode
<div className="flex flex-col gap-1">
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
{renderInfoColumn(signature)}
</InfoDrawer>
</div>
}
>
<div className={clsx(classes.Box, whClassStyle)}>
<svg width="13" height="4" viewBox="0 0 13 4" xmlns="http://www.w3.org/2000/svg">
<rect width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
<svg width="13" height="8" viewBox="0 0 13 8" xmlns="http://www.w3.org/2000/svg">
<rect y="1" width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
{isEOL && <rect x="4" width="5" height="6" rx="1" className={clsx(classes.Eol)} fill="#a153ac" />}
</svg>
</div>
</WdTooltipWrapper>

View File

@@ -1,4 +1,4 @@
import { ConnectionType, MassState } from '@/hooks/Mapper/types';
import { ConnectionType, MassState, ShipSizeStatus } from '@/hooks/Mapper/types';
export enum SOLAR_SYSTEM_CLASS_IDS {
ccp1 = -1,
@@ -727,16 +727,41 @@ export const MASS_STATE_NAMES = {
[MassState.verge]: 'Verge of collapse',
};
// export const SHIP_SIZES_NAMES_ORDER = [
// ShipSizeStatus.small,
// ShipSizeStatus.normal,
// // ShipSizeStatus.large,
// // ShipSizeStatus.capital,
// ];
//
// export const SHIP_SIZES_NAMES = {
// [ShipSizeStatus.small]: 'Frigate',
// [ShipSizeStatus.normal]: 'Normal',
// // [ShipSizeStatus.large]: 'Normal',
// // [ShipSizeStatus.capital]: 'Normal',
// };
export const SHIP_SIZES_NAMES_ORDER = [
ShipSizeStatus.small,
ShipSizeStatus.medium,
ShipSizeStatus.large,
ShipSizeStatus.freight,
ShipSizeStatus.capital,
];
export const SHIP_SIZES_NAMES = {
[ShipSizeStatus.small]: 'Frigate',
[ShipSizeStatus.medium]: 'Medium',
[ShipSizeStatus.large]: 'Normal',
[ShipSizeStatus.freight]: 'Huge',
[ShipSizeStatus.capital]: 'Capital',
};
export const SHIP_SIZES_SIZE = {
[ShipSizeStatus.small]: '5K',
[ShipSizeStatus.medium]: '62K',
[ShipSizeStatus.large]: '375K',
[ShipSizeStatus.freight]: '1M',
[ShipSizeStatus.capital]: '2M',
};
export const SHIP_SIZES_DESCRIPTION = {
[ShipSizeStatus.small]: 'Frigate wormhole - up to Destroyer | 5K t.',
[ShipSizeStatus.medium]: 'Cruise wormhole - up to Battlecruiser | 62K t.',
[ShipSizeStatus.large]: 'Large wormhole - up to Battleship | 375K t.',
[ShipSizeStatus.freight]: 'Huge wormhole - up to Freighter | 1M t.',
[ShipSizeStatus.capital]: 'Capital wormhole - up to Capital | 2M t.',
};
export const SHIP_SIZES_NAMES_SHORT = {
[ShipSizeStatus.small]: 'S',
[ShipSizeStatus.medium]: 'M',
[ShipSizeStatus.large]: 'L',
[ShipSizeStatus.freight]: 'H',
[ShipSizeStatus.capital]: 'XL',
};

View File

@@ -10,5 +10,6 @@ export const convertSystem2Node = (sys: SolarSystemRawType): Node => {
position: sys.position,
data: sys,
draggable: !sys.locked,
deletable: !sys.locked,
};
};

View File

@@ -0,0 +1,32 @@
import { SolarSystemNodeDefault, SolarSystemNodeTheme } from '../components/SolarSystemNode';
import type { NodeProps } from 'reactflow';
import type { ComponentType } from 'react';
import { MapSolarSystemType } from '../map.types';
import { ConnectionMode } from 'reactflow';
export type SolarSystemNodeComponent = ComponentType<NodeProps<MapSolarSystemType>>;
interface ThemeBehavior {
isPanAndDrag: boolean;
nodeComponent: SolarSystemNodeComponent;
connectionMode: ConnectionMode;
}
const THEME_BEHAVIORS: {
[key: string]: ThemeBehavior;
} = {
default: {
isPanAndDrag: false,
nodeComponent: SolarSystemNodeDefault,
connectionMode: ConnectionMode.Loose,
},
pathfinder: {
isPanAndDrag: true,
nodeComponent: SolarSystemNodeTheme,
connectionMode: ConnectionMode.Loose,
},
};
export function getBehaviorForTheme(themeName: string) {
return THEME_BEHAVIORS[themeName] ?? THEME_BEHAVIORS.default;
}

View File

@@ -42,7 +42,7 @@ export const useMapUpdateSystems = () => {
return newSystem;
});
update({ systems: out });
update({ systems: out }, true);
},
[rf, update],
);

View File

@@ -0,0 +1,48 @@
import { useEffect, useState } from 'react';
import { BackgroundVariant } from 'reactflow';
export function useBackgroundVars(themeName?: string) {
const [variant, setVariant] = useState<BackgroundVariant>(BackgroundVariant.Dots);
const [gap, setGap] = useState<number>(16);
const [size, setSize] = useState<number>(1);
const [color, setColor] = useState('#81818b');
const [snapSize, setSnapSize] = useState<number>(25);
useEffect(() => {
// match any element whose entire `class` attribute ends with "-theme"
let themeEl = document.querySelector('[class$="-theme"]');
// If none is found, fall back to the <html> element
if (!themeEl) {
themeEl = document.documentElement;
}
const style = getComputedStyle(themeEl as HTMLElement);
const rawVariant = style.getPropertyValue('--rf-bg-variant').replace(/['"]/g, '').trim().toLowerCase();
let finalVariant: BackgroundVariant = BackgroundVariant.Dots;
if (rawVariant === 'lines') {
finalVariant = BackgroundVariant.Lines;
} else if (rawVariant === 'cross') {
finalVariant = BackgroundVariant.Cross;
}
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
const cssVarSize = style.getPropertyValue('--rf-bg-size');
const cssVarSnapSize = style.getPropertyValue('--rf-snap-size');
const cssColor = style.getPropertyValue('--rf-bg-pattern-color');
const gapNum = parseInt(cssVarGap, 10) || 16;
const sizeNum = parseInt(cssVarSize, 10) || 1;
const snapSize = parseInt(cssVarSnapSize, 10) || 25; //react-flow default
setVariant(finalVariant);
setGap(gapNum);
setSize(sizeNum);
setColor(cssColor);
setSnapSize(snapSize);
}, [themeName]);
return { variant, gap, size, color, snapSize };
}

View File

@@ -0,0 +1,44 @@
import { useMemo } from 'react';
import { useSystemKills } from '../../mapInterface/widgets/SystemKills/hooks/useSystemKills';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
interface UseKillsCounterProps {
realSystemId: string;
}
export function useKillsCounter({ realSystemId }: UseKillsCounterProps) {
const { data: mapData, outCommand } = useMapRootState();
const { systems } = mapData;
const systemNameMap = useMemo(() => {
const m: Record<string, string> = {};
systems.forEach(sys => {
m[sys.id] = sys.temporary_name || sys.name || '???';
});
return m;
}, [systems]);
const { kills: allKills, isLoading } = useSystemKills({
systemId: realSystemId,
outCommand,
showAllVisible: false,
});
const filteredKills = useMemo(() => {
if (!allKills || allKills.length === 0) return [];
return [...allKills]
.sort((a, b) => {
const aTime = a.kill_time ? new Date(a.kill_time).getTime() : 0;
const bTime = b.kill_time ? new Date(b.kill_time).getTime() : 0;
return bTime - aTime;
})
.slice(0, 10);
}, [allKills]);
return {
isLoading,
kills: filteredKills,
systemNameMap,
};
}

View File

@@ -70,7 +70,7 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
setTimeout(() => addConnections(data as CommandAddConnections), 100);
break;
case Commands.removeConnections:
removeConnections(data as CommandRemoveConnections);
setTimeout(() => removeConnections(data as CommandRemoveConnections), 100);
break;
case Commands.charactersUpdated:
charactersUpdated(data as CommandCharactersUpdated);
@@ -127,6 +127,10 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
// do nothing here
break;
case Commands.detailedKillsUpdated:
// do nothing here
break;
default:
console.warn(`Map handlers: Unknown command: ${type}`, data);
break;

View File

@@ -0,0 +1,305 @@
import { useEffect, useMemo, useState } from 'react';
import { MapSolarSystemType } from '../map.types';
import { NodeProps } from 'reactflow';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { CharacterTypeRaw, Commands, OutCommand, SystemSignature } from '@/hooks/Mapper/types';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
import { useMapEventListener } from '@/hooks/Mapper/events';
export type LabelInfo = {
id: string;
shortName: string;
};
export type UnsplashedSignatureType = SystemSignature & { sig_id: string };
function getActivityType(count: number): string {
if (count <= 5) return 'activityNormal';
if (count <= 30) return 'activityWarn';
return 'activityDanger';
}
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: 'Caldaria',
[Spaces.Matar]: 'Mataria',
[Spaces.Amarr]: 'Amarria',
[Spaces.Gallente]: 'Gallente',
};
function sortedLabels(labels: string[]): LabelInfo[] {
if (!labels) return [];
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x] as LabelInfo);
}
export function useLocalCounter(nodeVars: SolarSystemNodeVars) {
const localCounterCharacters = useMemo(() => {
return nodeVars.charactersInSystem
.map(char => ({
...char,
compact: true,
isOwn: nodeVars.userCharacters.includes(char.eve_id),
}))
.sort((a, b) => a.name.localeCompare(b.name));
}, [nodeVars.charactersInSystem, nodeVars.userCharacters]);
return { localCounterCharacters };
}
export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarSystemNodeVars {
const { id, data, selected } = props;
const {
system_static_info,
system_signatures,
locked,
name,
tag,
status,
labels,
temporary_name,
linked_sig_eve_id: linkedSigEveId = '',
} = data;
const {
system_class,
security,
class_title,
solar_system_id,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = system_static_info;
const {
interfaceSettings,
data: { systemSignatures: mapSystemSignatures },
} = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const isShowLinkedSigId = useMapGetOption('show_linked_signature_id') === 'true';
const isShowLinkedSigIdTempName = useMapGetOption('show_linked_signature_id_temp_name') === 'true';
const {
data: {
characters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const systemSigs = useMemo(
() => mapSystemSignatures[solar_system_id] || system_signatures,
[system_signatures, solar_system_id, mapSystemSignatures],
);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === solar_system_id && c.online);
}, [characters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
() => getSystemClassStyles({ systemClass: system_class, security }),
[security, system_class],
);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
const linkedSigPrefix = useMemo(() => (linkedSigEveId ? linkedSigEveId.split('-')[0] : null), [linkedSigEveId]);
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
const labelCustom = useMemo(() => {
if (isShowLinkedSigId && linkedSigPrefix) {
return labelsManager.customLabel ? `${linkedSigPrefix}${labelsManager.customLabel}` : linkedSigPrefix;
}
return labelsManager.customLabel;
}, [linkedSigPrefix, isShowLinkedSigId, labelsManager]);
const killsCount = useMemo(() => kills[solar_system_id] ?? null, [kills, solar_system_id]);
const killsActivityType = killsCount ? getActivityType(killsCount) : null;
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: { system_id: solar_system_id.toString() },
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const computedTemporaryName = useMemo(() => {
if (!isTempSystemNameEnabled) {
return '';
}
if (isShowLinkedSigIdTempName && linkedSigPrefix) {
return temporary_name ? `${linkedSigPrefix}${temporary_name}` : `${linkedSigPrefix}${solar_system_name}`;
}
return temporary_name;
}, [isShowLinkedSigIdTempName, isTempSystemNameEnabled, linkedSigPrefix, solar_system_name, temporary_name]);
const systemName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName) {
return computedTemporaryName;
}
return solar_system_name;
}, [isTempSystemNameEnabled, solar_system_name, computedTemporaryName]);
const customName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName && name) {
return name;
}
if (solar_system_name !== name && name) {
return name;
}
return null;
}, [isTempSystemNameEnabled, computedTemporaryName, name, solar_system_name]);
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
systemSigs
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
kind: s.kind,
name: s.name,
group: s.group,
})) as UnsplashedSignatureType[],
);
}, [isShowUnsplashedSignatures, systemSigs]);
// Ensure hubs are always strings.
const hubsAsStrings = useMemo(() => hubs.map(item => item.toString()), [hubs]);
const nodeVars: SolarSystemNodeVars = {
id,
selected,
visible,
isWormhole,
classTitleColor,
killsCount,
killsActivityType,
hasUserCharacters,
userCharacters,
showHandlers,
regionClass,
systemName,
customName,
labelCustom,
isShattered: is_shattered,
tag,
status,
labelsInfo,
dbClick,
sortedStatics,
effectName: effect_name,
regionName: region_name,
solarSystemId: solar_system_id.toString(),
solarSystemName: solar_system_name,
locked,
hubs: hubsAsStrings,
name: name,
isConnecting,
hoverNodeId,
charactersInSystem,
unsplashedLeft,
unsplashedRight,
isThickConnections,
classTitle: class_title,
temporaryName: computedTemporaryName,
};
return nodeVars;
}
export interface SolarSystemNodeVars {
id: string;
selected: boolean;
visible: boolean;
isWormhole: boolean;
classTitleColor: string | null;
killsCount: number | null;
killsActivityType: string | null;
hasUserCharacters: boolean;
showHandlers: boolean;
regionClass: string | null;
systemName: string;
customName?: string | null;
labelCustom: string | null;
isShattered: boolean;
tag?: string | null;
status?: number;
labelsInfo: LabelInfo[];
dbClick: (event: React.MouseEvent<HTMLDivElement>) => void;
sortedStatics: Array<string | number>;
effectName: string | null;
regionName: string | null;
solarSystemId: string;
solarSystemName: string | null;
locked: boolean;
hubs: string[];
name: string | null;
isConnecting: boolean;
hoverNodeId: string | null;
charactersInSystem: Array<CharacterTypeRaw>;
userCharacters: string[];
unsplashedLeft: Array<SystemSignature>;
unsplashedRight: Array<SystemSignature>;
isThickConnections: boolean;
classTitle: string | null;
temporaryName?: string | null;
}
export function useNodeKillsCount(systemId: number | string, initialKillsCount: number | null): number | null {
const [killsCount, setKillsCount] = useState<number | null>(initialKillsCount);
useEffect(() => {
setKillsCount(initialKillsCount);
}, [initialKillsCount]);
useMapEventListener(event => {
if (event.name === Commands.killsUpdated && event.data?.toString() === systemId.toString()) {
//@ts-ignore
if (event.payload && typeof event.payload.kills === 'number') {
// @ts-ignore
setKillsCount(event.payload.kills);
}
return true;
}
return false;
});
return killsCount;
}

View File

@@ -1,5 +1,6 @@
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
import { SolarSystemConnection } from '@/hooks/Mapper/types';
import { XYPosition } from 'reactflow';
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
@@ -7,3 +8,5 @@ export type OnMapSelectionChange = (event: {
systems: string[];
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
}) => void;
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;

View File

@@ -0,0 +1,36 @@
@import './eve-common-variables';
@import './eve-common';
.default-theme {
--rf-bg-color: #0C0A09;
--rf-soft-bg-color: #171717;
--rf-node-bg-color: #202020;
--rf-node-soft-bg-color: #202020;
--rf-text-color: #ffffff;
--rf-tag-color: #38BDF8;
--rf-region-name: #D6D3D1;
--rf-custom-name: #93C5FD;
--rf-node-font-family: 'Shentox', 'Rogan', sans-serif !important;
--rf-node-font-weight: 500;
--rf-bg-variant: "dots";
--rf-bg-gap: 15;
--rf-bg-size: 1;
--rf-bg-pattern-color: #81818a;
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
--pastel-green: #88b04b;
--pastel-yellow: #ffdd59;
--dark-bg: #2d2d2d;
--text-color: #ffffff;
--tooltip-bg: #202020;
--window-corner: #72716f;
--rf-local-counter-font-weight: 500;
--rf-node-local-counter: inherit;
--rf-has-user-characters: #ffc75d;
}

View File

@@ -1,78 +1,122 @@
$eve-link-color-default: #333;
$eve-link-color-top-mass-0: #333;
$eve-link-color-top-mass-1: #5a4520;
$eve-link-color-top-mass-2: #672c2c;
$eve-link-color-middle-mass-0: #333;
$eve-link-color-middle-mass-1: #333;
$eve-link-color-middle-mass-2: #333;
$eve-link-color-middle-time-0: #5c5c5c;
$eve-link-color-middle-time-1: #ff00cd;
$eve-link-color-middle-time-1-border: #99f3ff;
$eve-link-color-top-mass-1-time-1: #796300;
$eve-link-color-top-mass-2-time-1: #8c1717;
$eve-link-color-temp: orange;
$friendlyBase: #3bbd39;
$friendlyAlpha: #3bbd3952;
$friendlyDark20: darken($friendlyBase, 20%);
$friendlyDark30: darken($friendlyBase, 30%);
$friendlyDark5: darken($friendlyBase, 5%);
$eve-effect-pulsar: #40aef5;
$eve-effect-magnetar: #f058f8;
$eve-effect-wolfRayet: #ef7843;
$eve-effect-blackHole: #1b1b1b;
$eve-effect-cataclysmicVariable: #ffea90;
$eve-effect-redGiant: #fd3c3c;
$eve-effect-dazhLiminalityLocus: #ff6464;
$eve-effect-imperialStellarObservatory: #6991ce;
$eve-effect-stateStellarObservatory: #6991ce;
$eve-effect-republicStellarObservatory: #6991ce;
$eve-effect-federalStellarObservatory: #6991ce;
$lookingForBase: #43c2fd;
$lookingForAlpha: rgba(67, 176, 253, 0.48);
$lookingForDark15: darken($lookingForBase, 15%);
$eve-wh-type-color-high: #5dffd2;
$eve-wh-type-color-low: #f79400;
$eve-wh-type-color-null: #fc3c3c;
$eve-wh-type-color-c1: #69bfce;
$eve-wh-type-color-c2: #6991ce;
$eve-wh-type-color-c3: #a8cb70;
$eve-wh-type-color-c4: #e39c68;
$eve-wh-type-color-c5: #de8686;
$eve-wh-type-color-c6: #e76363;
$eve-wh-type-color-c13: #988cb5;
$eve-wh-type-color-drifter: #ff44f6;
$eve-wh-type-color-thera: #ffffff;
$eve-wh-type-color-zarzakh: #212121;
$homeBase: rgb(179, 253, 67);
$homeAlpha: rgba(186, 248, 48, 0.32);
$homeBackground: #a0fa5636;
$homeDark30: darken($homeBase, 30%);
$eve-security-color-10: #2c74df;
$eve-security-color-09: #3998e8;
$eve-security-color-08: #4dcbf5;
$eve-security-color-07: #60d8a2;
$eve-security-color-06: #71e454;
$eve-security-color-05: #f2fc81;
$eve-security-color-04: #d96c07;
$eve-security-color-03: #cb440f;
$eve-security-color-02: #b91117;
$eve-security-color-01: #732020;
$eve-security-color-00: #8b3263;
$eve-security-color-m-01: #8b3263;
$eve-security-color-m-02: #8b3263;
$eve-security-color-m-03: #8b3263;
$eve-security-color-m-04: #8b3263;
$eve-security-color-m-05: #8b3263;
$eve-security-color-m-06: #8b3263;
$eve-security-color-m-07: #8b3263;
$eve-security-color-m-08: #8b3263;
$eve-security-color-m-09: #8b3263;
$eve-security-color-m-10: #8b3263;
:root {
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
--pastel-green: #88b04b;
--pastel-yellow: #ffdd59;
--dark-bg: #2d2d2d;
--text-color: #ffffff;
--tooltip-bg: #202020;
$eve-solar-system-status-unknown: transparent;
$eve-solar-system-status-friendly: #3bbd3952;
$eve-solar-system-status-warning: #906518a6;
$eve-solar-system-status-target: #b439ff6b;
$eve-solar-system-status-dangerous: #d54040;
$eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
$eve-solar-system-status-home: rgb(197, 253, 67);
--pastel-blue-darken10: #4f6b86;
--pastel-blue-lighten10: #6da3af;
--pastel-pink-darken10: #bb7ca9;
--pastel-pink-lighten10: #e0a6cb;
--pastel-green-darken10: #79a244;
--pastel-green-lighten10: #99cf52;
--pastel-yellow-darken10: #e6c44f;
--pastel-yellow-lighten10: #ffe874;
$eve-solar-system-status-color-unknown: transparent;
$eve-solar-system-status-color-friendly: #3bbd39;
$eve-solar-system-status-color-warning: #ffb93b;
$eve-solar-system-status-color-target: #b439ff;
$eve-solar-system-status-color-dangerous: #d54040;
$eve-solar-system-status-color-lookingFor: #43c2fd;
$eve-solar-system-status-color-home: rgb(197, 253, 67);
--eve-link-color-default: #333;
--eve-link-color-top-mass-0: #333;
--eve-link-color-top-mass-1: #5a4520;
--eve-link-color-top-mass-2: #672c2c;
--eve-link-color-middle-mass-0: #333;
--eve-link-color-middle-mass-1: #333;
--eve-link-color-middle-mass-2: #333;
--eve-link-color-middle-time-0: #5c5c5c;
--eve-link-color-middle-time-1: #ff00cd;
--eve-link-color-middle-time-1-border: #99f3ff;
--eve-link-color-top-mass-1-time-1: #796300;
--eve-link-color-top-mass-2-time-1: #8c1717;
--eve-link-color-temp: orange;
--eve-effect-pulsar: #40aef5;
--eve-effect-magnetar: #f058f8;
--eve-effect-wolfRayet: #ef7843;
--eve-effect-blackHole: #1b1b1b;
--eve-effect-cataclysmicVariable: #ffea90;
--eve-effect-redGiant: #fd3c3c;
--eve-effect-dazhLiminalityLocus: #ff6464;
--eve-effect-imperialStellarObservatory: #6991ce;
--eve-effect-stateStellarObservatory: #6991ce;
--eve-effect-republicStellarObservatory: #6991ce;
--eve-effect-federalStellarObservatory: #6991ce;
--eve-wh-type-color-high: #5dffd2;
--eve-wh-type-color-low: #f79400;
--eve-wh-type-color-null: #fc3c3c;
--eve-wh-type-color-c1: #69bfce;
--eve-wh-type-color-c2: #6991ce;
--eve-wh-type-color-c3: #a8cb70;
--eve-wh-type-color-c4: #e39c68;
--eve-wh-type-color-c5: #de8686;
--eve-wh-type-color-c6: #e76363;
--eve-wh-type-color-c13: #988cb5;
--eve-wh-type-color-drifter: #ff44f6;
--eve-wh-type-color-thera: #ffffff;
--eve-wh-type-color-zarzakh: #212121;
--eve-security-color-10: #2c74df;
--eve-security-color-09: #3998e8;
--eve-security-color-08: #4dcbf5;
--eve-security-color-07: #60d8a2;
--eve-security-color-06: #71e454;
--eve-security-color-05: #f2fc81;
--eve-security-color-04: #d96c07;
--eve-security-color-03: #cb440f;
--eve-security-color-02: #b91117;
--eve-security-color-01: #732020;
--eve-security-color-00: #8b3263;
--eve-security-color-m-01: #8b3263;
--eve-security-color-m-02: #8b3263;
--eve-security-color-m-03: #8b3263;
--eve-security-color-m-04: #8b3263;
--eve-security-color-m-05: #8b3263;
--eve-security-color-m-06: #8b3263;
--eve-security-color-m-07: #8b3263;
--eve-security-color-m-08: #8b3263;
--eve-security-color-m-09: #8b3263;
--eve-security-color-m-10: #8b3263;
--eve-solar-system-status-unknown: transparent;
--eve-solar-system-status-color-unknown: transparent;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
--eve-solar-system-status-color-background: #{$homeBackground};
--eve-solar-system-status-color-home-dark30: #{$homeDark30};
--eve-solar-system-status-friendly: #{$friendlyAlpha};
--eve-solar-system-status-color-friendly: #{$friendlyBase};
--eve-solar-system-status-friendly-dark30: #{$friendlyDark30};
--eve-solar-system-status-color-friendly-dark20: #{$friendlyDark20};
--eve-solar-system-status-color-friendly-dark5: #{$friendlyDark5};
--eve-solar-system-status-lookingFor: #{$lookingForAlpha};
--eve-solar-system-status-color-lookingFor: #{$lookingForBase};
--eve-solar-system-status-color-lookingFor-dark15: #{$lookingForDark15};
--eve-solar-system-status-warning: #906518a6;
--eve-solar-system-status-color-warning: #ffb93b;
--eve-solar-system-status-target: #b439ff6b;
--eve-solar-system-status-color-target: #b439ff;
--eve-solar-system-status-dangerous: #d54040;
--eve-solar-system-status-color-dangerous: #d54040;
--conn-time-eol: #7452c3e3;
--conn-frigate: #325d88;
--conn-save: rgba(155, 102, 45, 0.85);
--selected-item-bg: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,535 +1,504 @@
@import "eve-common-variables";
@import './eve-common-variables';
.eve-wh-effect-color-pulsar {
fill: $eve-effect-pulsar;
background-color: $eve-effect-pulsar;
fill: var(--eve-effect-pulsar);
background-color: var(--eve-effect-pulsar);
}
.eve-wh-effect-color-magnetar {
fill: $eve-effect-magnetar;
background-color: $eve-effect-magnetar;
fill: var(--eve-effect-magnetar);
background-color: var(--eve-effect-magnetar);
}
.eve-wh-effect-color-wolfRayet {
fill: $eve-effect-wolfRayet;
background-color: $eve-effect-wolfRayet;
fill: var(--eve-effect-wolfRayet);
background-color: var(--eve-effect-wolfRayet);
}
.eve-wh-effect-color-blackHole {
fill: $eve-effect-blackHole;
background-color: $eve-effect-blackHole;
box-shadow: 0 0 8px rgba(255 255 255 / 33);
fill: var(--eve-effect-blackHole);
background-color: var(--eve-effect-blackHole);
box-shadow: 0 0 8px rgba(255, 255, 255, 0.33);
}
.eve-wh-effect-color-cataclysmicVariable {
fill: $eve-effect-cataclysmicVariable;
background-color: $eve-effect-cataclysmicVariable;
fill: var(--eve-effect-cataclysmicVariable);
background-color: var(--eve-effect-cataclysmicVariable);
}
.eve-wh-effect-color-redGiant {
fill: $eve-effect-redGiant;
background-color: $eve-effect-redGiant;
fill: var(--eve-effect-redGiant);
background-color: var(--eve-effect-redGiant);
}
.text-eve-wh-effect-color-pulsar {
color: $eve-effect-pulsar;
color: var(--eve-effect-pulsar);
}
.text-eve-wh-effect-color-magnetar {
color: $eve-effect-magnetar;
color: var(--eve-effect-magnetar);
}
.text-eve-wh-effect-color-wolfRayet {
color: $eve-effect-wolfRayet;
color: var(--eve-effect-wolfRayet);
}
.text-eve-wh-effect-color-blackHole {
color: #fff;
}
.text-eve-wh-effect-color-cataclysmicVariable {
color: $eve-effect-cataclysmicVariable;
color: var(--eve-effect-cataclysmicVariable);
}
.text-eve-wh-effect-color-redGiant {
color: $eve-effect-redGiant;
color: var(--eve-effect-redGiant);
}
.text-eve-wh-effect-color-dazhLiminalityLocus {
color: $eve-effect-dazhLiminalityLocus;
color: var(--eve-effect-dazhLiminalityLocus);
}
.text-eve-wh-effect-color-imperialStellarObservatory {
color: $eve-effect-imperialStellarObservatory;
color: var(--eve-effect-imperialStellarObservatory);
}
.text-eve-wh-effect-color-stateStellarObservatory {
color: $eve-effect-stateStellarObservatory;
color: var(--eve-effect-stateStellarObservatory);
}
.text-eve-wh-effect-color-republicStellarObservatory {
color: $eve-effect-republicStellarObservatory;
color: var(--eve-effect-republicStellarObservatory);
}
.text-eve-wh-effect-color-federalStellarObservatory {
color: $eve-effect-federalStellarObservatory;
color: var(--eve-effect-federalStellarObservatory);
}
/* Security color classes */
.eve-security-color-10 {
color: $eve-security-color-10 !important;
fill: $eve-security-color-10;
color: var(--eve-security-color-10) !important;
fill: var(--eve-security-color-10);
}
.eve-security-color-09 {
color: $eve-security-color-09 !important;
fill: $eve-security-color-09;
color: var(--eve-security-color-09) !important;
fill: var(--eve-security-color-09);
}
.eve-security-color-08 {
color: $eve-security-color-08 !important;
fill: $eve-security-color-08;
color: var(--eve-security-color-08) !important;
fill: var(--eve-security-color-08);
}
.eve-security-color-07 {
color: $eve-security-color-07 !important;
fill: $eve-security-color-07;
color: var(--eve-security-color-07) !important;
fill: var(--eve-security-color-07);
}
.eve-security-color-06 {
color: $eve-security-color-06 !important;
fill: $eve-security-color-06;
color: var(--eve-security-color-06) !important;
fill: var(--eve-security-color-06);
}
.eve-security-color-05 {
color: $eve-security-color-05 !important;
fill: $eve-security-color-05;
color: var(--eve-security-color-05) !important;
fill: var(--eve-security-color-05);
}
.eve-security-color-04 {
color: $eve-security-color-04 !important;
fill: $eve-security-color-04;
color: var(--eve-security-color-04) !important;
fill: var(--eve-security-color-04);
}
.eve-security-color-03 {
color: $eve-security-color-03 !important;
fill: $eve-security-color-03;
color: var(--eve-security-color-03) !important;
fill: var(--eve-security-color-03);
}
.eve-security-color-02 {
color: $eve-security-color-02 !important;
fill: $eve-security-color-02;
color: var(--eve-security-color-02) !important;
fill: var(--eve-security-color-02);
}
.eve-security-color-01 {
color: $eve-security-color-01 !important;
fill: $eve-security-color-01;
color: var(--eve-security-color-01) !important;
fill: var(--eve-security-color-01);
}
.eve-security-color-00 {
color: $eve-security-color-00 !important;
fill: $eve-security-color-00;
color: var(--eve-security-color-00) !important;
fill: var(--eve-security-color-00);
}
.eve-security-color-m-01 {
color: $eve-security-color-m-01 !important;
fill: $eve-security-color-m-01;
color: var(--eve-security-color-m-01) !important;
fill: var(--eve-security-color-m-01);
}
.eve-security-color-m-02 {
color: $eve-security-color-m-02 !important;
fill: $eve-security-color-m-02;
color: var(--eve-security-color-m-02) !important;
fill: var(--eve-security-color-m-02);
}
.eve-security-color-m-03 {
color: $eve-security-color-m-03 !important;
fill: $eve-security-color-m-03;
color: var(--eve-security-color-m-03) !important;
fill: var(--eve-security-color-m-03);
}
.eve-security-color-m-04 {
color: $eve-security-color-m-04 !important;
fill: $eve-security-color-m-04;
color: var(--eve-security-color-m-04) !important;
fill: var(--eve-security-color-m-04);
}
.eve-security-color-m-05 {
color: $eve-security-color-m-05 !important;
fill: $eve-security-color-m-05;
color: var(--eve-security-color-m-05) !important;
fill: var(--eve-security-color-m-05);
}
.eve-security-color-m-06 {
color: $eve-security-color-m-06 !important;
fill: $eve-security-color-m-06;
color: var(--eve-security-color-m-06) !important;
fill: var(--eve-security-color-m-06);
}
.eve-security-color-m-07 {
color: $eve-security-color-m-07 !important;
fill: $eve-security-color-m-07;
color: var(--eve-security-color-m-07) !important;
fill: var(--eve-security-color-m-07);
}
.eve-security-color-m-08 {
color: $eve-security-color-m-08 !important;
fill: $eve-security-color-m-08;
color: var(--eve-security-color-m-08) !important;
fill: var(--eve-security-color-m-08);
}
.eve-security-color-m-09 {
color: $eve-security-color-m-09 !important;
fill: $eve-security-color-m-09;
color: var(--eve-security-color-m-09) !important;
fill: var(--eve-security-color-m-09);
}
.eve-security-color-m-10 {
color: $eve-security-color-m-10 !important;
fill: $eve-security-color-m-10;
color: var(--eve-security-color-m-10) !important;
fill: var(--eve-security-color-m-10);
}
/* Security backgrounds */
.eve-security-background-10 {
background-color: $eve-security-color-10;
fill: $eve-security-color-10;
background-color: var(--eve-security-color-10);
fill: var(--eve-security-color-10);
}
.eve-security-background-09 {
background-color: $eve-security-color-09;
fill: $eve-security-color-09;
background-color: var(--eve-security-color-09);
fill: var(--eve-security-color-09);
}
.eve-security-background-08 {
background-color: $eve-security-color-08;
fill: $eve-security-color-08;
background-color: var(--eve-security-color-08);
fill: var(--eve-security-color-08);
}
.eve-security-background-07 {
background-color: $eve-security-color-07;
fill: $eve-security-color-07;
background-color: var(--eve-security-color-07);
fill: var(--eve-security-color-07);
}
.eve-security-background-06 {
background-color: $eve-security-color-06;
fill: $eve-security-color-06;
background-color: var(--eve-security-color-06);
fill: var(--eve-security-color-06);
}
.eve-security-background-05 {
background-color: $eve-security-color-05;
fill: $eve-security-color-05;
background-color: var(--eve-security-color-05);
fill: var(--eve-security-color-05);
}
.eve-security-background-04 {
background-color: $eve-security-color-04;
fill: $eve-security-color-04;
background-color: var(--eve-security-color-04);
fill: var(--eve-security-color-04);
}
.eve-security-background-03 {
background-color: $eve-security-color-03;
fill: $eve-security-color-03;
background-color: var(--eve-security-color-03);
fill: var(--eve-security-color-03);
}
.eve-security-background-02 {
background-color: $eve-security-color-02;
fill: $eve-security-color-02;
background-color: var(--eve-security-color-02);
fill: var(--eve-security-color-02);
}
.eve-security-background-01 {
background-color: $eve-security-color-01;
fill: $eve-security-color-01;
background-color: var(--eve-security-color-01);
fill: var(--eve-security-color-01);
}
.eve-security-background-00 {
background-color: $eve-security-color-00;
fill: $eve-security-color-00;
background-color: var(--eve-security-color-00);
fill: var(--eve-security-color-00);
}
.eve-security-background-m-01 {
background-color: $eve-security-color-m-01;
fill: $eve-security-color-m-01;
background-color: var(--eve-security-color-m-01);
fill: var(--eve-security-color-m-01);
}
.eve-security-background-m-02 {
background-color: $eve-security-color-m-02;
fill: $eve-security-color-m-02;
background-color: var(--eve-security-color-m-02);
fill: var(--eve-security-color-m-02);
}
.eve-security-background-m-03 {
background-color: $eve-security-color-m-03;
fill: $eve-security-color-m-03;
background-color: var(--eve-security-color-m-03);
fill: var(--eve-security-color-m-03);
}
.eve-security-background-m-04 {
background-color: $eve-security-color-m-04;
fill: $eve-security-color-m-04;
background-color: var(--eve-security-color-m-04);
fill: var(--eve-security-color-m-04);
}
.eve-security-background-m-05 {
background-color: $eve-security-color-m-05;
fill: $eve-security-color-m-05;
background-color: var(--eve-security-color-m-05);
fill: var(--eve-security-color-m-05);
}
.eve-security-background-m-06 {
background-color: $eve-security-color-m-06;
fill: $eve-security-color-m-06;
background-color: var(--eve-security-color-m-06);
fill: var(--eve-security-color-m-06);
}
.eve-security-background-m-07 {
background-color: $eve-security-color-m-07;
fill: $eve-security-color-m-07;
background-color: var(--eve-security-color-m-07);
fill: var(--eve-security-color-m-07);
}
.eve-security-background-m-08 {
background-color: $eve-security-color-m-08;
fill: $eve-security-color-m-08;
background-color: var(--eve-security-color-m-08);
fill: var(--eve-security-color-m-08);
}
.eve-security-background-m-09 {
background-color: $eve-security-color-m-09;
fill: $eve-security-color-m-09;
background-color: var(--eve-security-color-m-09);
fill: var(--eve-security-color-m-09);
}
.eve-security-background-m-10 {
background-color: $eve-security-color-m-10;
fill: $eve-security-color-m-10;
background-color: var(--eve-security-color-m-10);
fill: var(--eve-security-color-m-10);
}
/* WH Type color classes */
.eve-wh-type-color-high {
color: $eve-wh-type-color-high;
fill: $eve-wh-type-color-high;
color: var(--eve-wh-type-color-high) !important;
fill: var(--eve-wh-type-color-high);
font-weight: bold !important;
}
.eve-wh-type-color-low {
color: $eve-wh-type-color-low;
fill: $eve-wh-type-color-low;
color: var(--eve-wh-type-color-low) !important;
fill: var(--eve-wh-type-color-low);
font-weight: bold !important;
}
.eve-wh-type-color-null {
color: $eve-wh-type-color-null;
fill: $eve-wh-type-color-null;
color: var(--eve-wh-type-color-null) !important;
fill: var(--eve-wh-type-color-null);
font-weight: bold !important;
}
.eve-wh-type-color-c1 {
color: $eve-wh-type-color-c1 !important;
fill: $eve-wh-type-color-c1;
color: var(--eve-wh-type-color-c1) !important;
fill: var(--eve-wh-type-color-c1);
font-weight: bold !important;
}
.eve-wh-type-color-c2 {
color: $eve-wh-type-color-c2 !important;
fill: $eve-wh-type-color-c2;
color: var(--eve-wh-type-color-c2) !important;
fill: var(--eve-wh-type-color-c2);
font-weight: bold !important;
}
.eve-wh-type-color-c3 {
color: $eve-wh-type-color-c3 !important;
fill: $eve-wh-type-color-c3;
color: var(--eve-wh-type-color-c3) !important;
fill: var(--eve-wh-type-color-c3);
font-weight: bold !important;
}
.eve-wh-type-color-c4 {
color: $eve-wh-type-color-c4 !important;
fill: $eve-wh-type-color-c4;
color: var(--eve-wh-type-color-c4) !important;
fill: var(--eve-wh-type-color-c4);
font-weight: bold !important;
}
.eve-wh-type-color-c5 {
color: $eve-wh-type-color-c5 !important;
fill: $eve-wh-type-color-c5;
color: var(--eve-wh-type-color-c5) !important;
fill: var(--eve-wh-type-color-c5);
font-weight: bold !important;
}
.eve-wh-type-color-c6 {
color: $eve-wh-type-color-c6 !important;
fill: $eve-wh-type-color-c6;
color: var(--eve-wh-type-color-c6) !important;
fill: var(--eve-wh-type-color-c6);
font-weight: bold !important;
}
.eve-wh-type-color-c13 {
color: $eve-wh-type-color-c13 !important;
fill: $eve-wh-type-color-c13;
color: var(--eve-wh-type-color-c13) !important;
fill: var(--eve-wh-type-color-c13);
}
.eve-wh-type-color-drifter {
color: $eve-wh-type-color-drifter !important;
fill: $eve-wh-type-color-drifter;
color: var(--eve-wh-type-color-drifter) !important;
fill: var(--eve-wh-type-color-drifter);
}
.eve-wh-type-color-thera {
color: $eve-wh-type-color-thera !important;
fill: $eve-wh-type-color-thera;
color: var(--eve-wh-type-color-thera) !important;
fill: var(--eve-wh-type-color-thera);
}
/* WH Type backgrounds */
.eve-wh-type-background-high {
background-color: $eve-wh-type-color-high;
background-color: var(--eve-wh-type-color-high);
}
.eve-wh-type-background-low {
background-color: $eve-wh-type-color-low;
background-color: var(--eve-wh-type-color-low);
}
.eve-wh-type-background-null {
background-color: $eve-wh-type-color-null;
background-color: var(--eve-wh-type-color-null);
}
.eve-wh-type-background-c1 {
background-color: $eve-wh-type-color-c1;
background-color: var(--eve-wh-type-color-c1);
}
.eve-wh-type-background-c2 {
background-color: $eve-wh-type-color-c2;
background-color: var(--eve-wh-type-color-c2);
}
.eve-wh-type-background-c3 {
background-color: $eve-wh-type-color-c3;
background-color: var(--eve-wh-type-color-c3);
}
.eve-wh-type-background-c4 {
background-color: $eve-wh-type-color-c4;
background-color: var(--eve-wh-type-color-c4);
}
.eve-wh-type-background-c5 {
background-color: $eve-wh-type-color-c5;
background-color: var(--eve-wh-type-color-c5);
}
.eve-wh-type-background-c6 {
background-color: $eve-wh-type-color-c6;
background-color: var(--eve-wh-type-color-c6);
}
.eve-wh-type-background-c13 {
background-color: $eve-wh-type-color-c13;
background-color: var(--eve-wh-type-color-c13);
}
.eve-wh-type-background-drifter {
background-color: $eve-wh-type-color-drifter;
background-color: var(--eve-wh-type-color-drifter);
}
.eve-wh-type-background-thera {
background-color: $eve-wh-type-color-thera;
background-color: var(--eve-wh-type-color-thera);
}
.eve-wh-type-background-zarzakh {
background-color: $eve-wh-type-color-zarzakh;
background-color: var(--eve-wh-type-color-zarzakh);
}
/* Kind color classes */
.eve-kind-color-high {
color: $eve-wh-type-color-high;
fill: $eve-wh-type-color-high;
color: var(--eve-wh-type-color-high);
fill: var(--eve-wh-type-color-high);
}
.eve-kind-color-low {
color: $eve-wh-type-color-low;
fill: $eve-wh-type-color-low;
color: var(--eve-wh-type-color-low);
fill: var(--eve-wh-type-color-low);
font-weight: bold;
}
.eve-kind-color-null {
color: $eve-wh-type-color-null;
fill: $eve-wh-type-color-null;
color: var(--eve-wh-type-color-null);
fill: var(--eve-wh-type-color-null);
}
.eve-kind-color-wh {
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
}
.eve-kind-color-thera {
color: $eve-wh-type-color-thera;
fill: $eve-wh-type-color-thera;
color: var(--eve-wh-type-color-thera);
fill: var(--eve-wh-type-color-thera);
}
.eve-kind-color-abyss {
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
}
.eve-kind-color-penalty {
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
}
.eve-kind-color-pochven {
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
}
.eve-kind-color-zarzakh {
color: $eve-wh-type-color-zarzakh;
fill: $eve-wh-type-color-zarzakh;
color: var(--eve-wh-type-color-zarzakh);
fill: var(--eve-wh-type-color-zarzakh);
}
/* Kind backgrounds */
.eve-kind-background-high {
background-color: $eve-wh-type-color-high;
background-color: var(--eve-wh-type-color-high);
}
.eve-kind-background-low {
background-color: $eve-wh-type-color-low;
background-color: var(--eve-wh-type-color-low);
}
.eve-kind-background-null {
background-color: $eve-wh-type-color-null;
background-color: var(--eve-wh-type-color-null);
}
.eve-kind-background-wh {
background-color: $eve-wh-type-color-c6;
background-color: var(--eve-wh-type-color-c6);
}
.eve-kind-background-thera {
background-color: $eve-wh-type-color-thera;
background-color: var(--eve-wh-type-color-thera);
}
.eve-kind-background-abyss {
background-color: $eve-wh-type-color-c6;
background-color: var(--eve-wh-type-color-c6);
}
.eve-kind-background-penalty {
background-color: $eve-wh-type-color-c6;
background-color: var(--eve-wh-type-color-c6);
}
.eve-kind-background-pochven {
background-color: $eve-wh-type-color-c6;
background-color: var(--eve-wh-type-color-c6);
}
.eve-kind-background-zarzakh {
background-color: $eve-wh-type-color-zarzakh;
background-color: var(--eve-wh-type-color-zarzakh);
}
/* System status color classes */
.eve-system-status-color-clear {
color: $eve-solar-system-status-color-unknown;
color: var(--eve-solar-system-status-color-unknown);
}
.eve-system-status-color-home {
color: $eve-solar-system-status-color-home;
color: var(--eve-solar-system-status-color-home);
}
.eve-system-status-color-friendly {
color: $eve-solar-system-status-color-friendly;
color: var(--eve-solar-system-status-color-friendly);
}
.eve-system-status-color-lookingFor {
color: $eve-solar-system-status-color-lookingFor;
color: var(--eve-solar-system-status-color-lookingFor);
}
.eve-system-status-color-warning {
color: $eve-solar-system-status-color-warning;
color: var(--eve-solar-system-status-color-warning);
}
.eve-system-status-color-target {
color: $eve-solar-system-status-color-target;
color: var(--eve-solar-system-status-color-target);
}
.eve-system-status-color-dangerous {
color: var(--eve-solar-system-status-color-dangerous);
}
.eve-system-status-color-dangerous {
color: $eve-solar-system-status-color-dangerous;
.eve-system-status-clear {
background-color: var(--eve-solar-system-status-unknown);
}
.eve-system-status-home {
background-color: var(--eve-solar-system-status-home);
}
.eve-system-status-friendly {
background-color: var(--eve-solar-system-status-friendly);
}
.eve-system-status-lookingFor {
background-color: var(--eve-solar-system-status-lookingFor);
}
.eve-system-status-warning {
background-color: var(--eve-solar-system-status-warning);
}
.eve-system-status-target {
background-color: var(--eve-solar-system-status-target);
}
.eve-system-status-dangerous {
background-color: var(--eve-solar-system-status-dangerous);
}
.eve-system-status-clear {
background-color: var(--eve-solar-system-status-unknown);
color: var(--eve-solar-system-status-color-unknown);
}
.eve-system-status-home {
background-color: var(--eve-solar-system-status-home);
color: var(--eve-solar-system-status-color-home);
}
.eve-system-status-friendly {
background-color: var(--eve-solar-system-status-friendly);
color: var(--eve-solar-system-status-color-friendly);
}
.eve-system-status-lookingFor {
background-color: var(--eve-solar-system-status-lookingFor);
color: var(--eve-solar-system-status-color-lookingFor);
}
.eve-system-status-warning {
background-color: var(--eve-solar-system-status-warning);
color: var(--eve-solar-system-status-color-warning);
}
.eve-system-status-target {
background-color: var(--eve-solar-system-status-target);
color: var(--eve-solar-system-status-color-target);
}
.eve-system-status-dangerous {
background-color: var(--eve-solar-system-status-dangerous);
color: var(--eve-solar-system-status-color-dangerous);
}
.wd-route-system-shape-triangle {
clip-path: polygon(50% 0, 0 100%, 100% 100%);
}
.wd-route-system-shape-circle {
border-radius: 40%;
}
/* Some additional background classes */
.wd-marker-bookmark-color-shattered {
background-color: #833ca4;
margin-top: 1px;
}
.wd-marker-bookmark-color-custom {
background-color: #282828;
border: 1px solid #4c4c4c;
@@ -572,3 +541,49 @@
.wd-marker-bookmark-color-danger {
background-color: #d10600;
}
.react-flow {
color: var(--text-color);
&__pane {
cursor: auto;
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}
.p-dialog {
.p-dialog-header {
height: 40px;
padding: 1rem;
padding-right: 10px !important;
}
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -0,0 +1,2 @@
@import './default-theme.scss';
@import './pathfinder-theme.scss';

View File

@@ -1,74 +0,0 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020;
.react-flow {
// background-color: $dark-bg;
color: $text-color;
&__node {
//cursor: auto;
}
&__pane {
cursor: auto;
}
//&__edge {
// stroke: $pastel-pink;
// stroke-width: 2px;
//
// &.selected {
// stroke: $pastel-yellow;
// }
//}
&__handle {
//background-color: $pastel-green;
//box-shadow: 0 0 5px rgba($pastel-green, 0.5);
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
//backdrop-filter: blur(5px);
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}
.p-dialog {
.p-dialog-header {
height: 40px;
padding: 1rem;
padding-right: 10px !important;
}
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -1,7 +0,0 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020;

View File

@@ -0,0 +1,54 @@
@import './eve-common-variables';
@import './eve-common';
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
$homeBase: rgb(197, 253, 67);
$homeAlpha: rgba(197, 253, 67, 0.32);
$homeDark30: darken($homeBase, 30%);
.pathfinder-theme {
/* -- Override values from the default theme -- */
--rf-bg-color: #000000;
--rf-soft-bg-color: #282828;
--rf-node-soft-bg-color: #313335;
--rf-node-font-weight: bold;
--rf-text-color: #adadad;
--rf-region-name: var(--rf-text-color);
--rf-custom-name: var(--rf-text-color);
--rf-bg-variant: "lines";
--rf-bg-gap: 34;
--rf-snap-size: 17;
--rf-bg-pattern-color: #313131;
--rf-local-counter-font-weight: 700;
/* Additional node-specific overrides */
--rf-node-line-height: normal;
--rf-node-font-family: 'Oxygen', sans-serif;
--rf-tag-color: #fbbf24;
/* -- theme-specific variables -- */
--eve-effect-pulsar: #428bca;
--eve-effect-magnetar: #e06fdf;
--eve-effect-wolfRayet: #e28a0d;
--eve-effect-blackHole: #000000;
--eve-effect-cataclysmicVariable: #ffffbb;
--eve-effect-redGiant: #d9534f;
--eve-wh-type-color-high: #5cb85c;
--eve-wh-type-color-low: #e28a0d;
--eve-wh-type-color-null: #d9534f;
--eve-wh-type-color-c1: #428bca;
--eve-wh-type-color-c2: #428bca;
--eve-wh-type-color-c3: #e28a0d;
--eve-wh-type-color-c4: #e28a0d;
--eve-wh-type-color-c5: #d9534f;
--eve-wh-type-color-c6: #d9534f;
--eve-wh-type-color-c13: #7986cb;
--eve-wh-type-color-drifter: #44aa82;
--rf-node-local-counter: #5cb85c;
--rf-has-user-characters: #ffc75d;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
}

View File

@@ -1,78 +1,30 @@
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import { WidgetGridItem, WidgetsGrid } from '@/hooks/Mapper/components/mapInterface/components';
import {
LocalCharacters,
RoutesWidget,
SystemInfo,
SystemSignatures,
} from '@/hooks/Mapper/components/mapInterface/widgets';
import { useState } from 'react';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
// import { debounce } from 'lodash/debounce';
const DEFAULT_WINDOWS = [
{
name: 'info',
rightOffset: 5,
width: 5,
height: 4,
item: () => <SystemInfo />,
},
{
name: 'local',
rightOffset: 5,
topOffset: 4,
width: 5,
height: 4,
item: () => <LocalCharacters />,
},
{ name: 'signatures', width: 8, height: 4, topOffset: 8, rightOffset: 12, item: () => <SystemSignatures /> },
{
name: 'routes',
rightOffset: 0,
topOffset: 8,
width: 5,
height: 6,
item: () => <RoutesWidget />,
},
];
const saveWindowsToLS = (toSaveItems: WidgetGridItem[]) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const out = toSaveItems.map(({ item, ...rest }) => rest);
localStorage.setItem(SESSION_KEY.windows, JSON.stringify(out));
};
const restoreWindowsFromLS = (): WidgetGridItem[] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const raw = localStorage.getItem(SESSION_KEY.windows);
if (!raw) {
console.warn('No windows found in local storage!!');
return DEFAULT_WINDOWS;
}
// eslint-disable-next-line no-debugger
const out = (JSON.parse(raw) as Omit<WidgetGridItem, 'item'>[])
.filter(x => DEFAULT_WINDOWS.find(def => def.name === x.name))
.map(x => {
const windowItem = DEFAULT_WINDOWS.find(def => def.name === x.name)?.item;
return { ...x, item: windowItem! };
});
return out;
};
import { useMemo } from 'react';
import { WindowManager } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { DEFAULT_WIDGETS } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export const MapInterface = () => {
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS);
// const [items, setItems] = useState<WindowProps[]>(restoreWindowsFromLS);
const { windowsSettings, updateWidgetSettings } = useMapRootState();
const items = useMemo(() => {
return windowsSettings.windows
.map(x => {
const content = DEFAULT_WIDGETS.find(y => y.id === x.id)?.content;
return {
...x,
content: content!,
};
})
.filter(x => windowsSettings.visible.some(j => x.id === j));
}, [windowsSettings]);
return (
<WidgetsGrid
items={items}
onChange={x => {
saveWindowsToLS(x);
setItems(x);
}}
<WindowManager
windows={items}
viewPort={windowsSettings.viewPort}
dragSelector=".react-grid-dragHandleExample"
onChange={updateWidgetSettings}
/>
);
};

View File

@@ -0,0 +1,10 @@
.SearchItem {
& > * {
font-size: 13px !important;
}
}
.SearchItemEffect {
font-weight: initial !important;
}

View File

@@ -0,0 +1,203 @@
import { Dialog } from 'primereact/dialog';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useCallback, useRef, useState } from 'react';
import { Button } from 'primereact/button';
import { IconField } from 'primereact/iconfield';
import { AutoComplete } from 'primereact/autocomplete';
import { OutCommand, SearchSystemItem } from '@/hooks/Mapper/types';
import { SystemViewStandalone, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
import classes from './AddSystemDialog.module.scss';
import clsx from 'clsx';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
export type SearchOnSubmitCallback = (item: SearchSystemItem) => void;
interface AddSystemDialogProps {
title?: string;
visible: boolean;
setVisible: (visible: boolean) => void;
onSubmit?: SearchOnSubmitCallback;
excludedSystems?: number[];
}
export const AddSystemDialog = ({
title = 'Add system',
visible,
setVisible,
onSubmit,
excludedSystems = [],
}: AddSystemDialogProps) => {
const {
outCommand,
data: { wormholesData },
} = useMapRootState();
const inputRef = useRef<any>();
const onShow = useCallback(() => {
inputRef.current?.focus();
}, []);
const [filteredItems, setFilteredItems] = useState<SearchSystemItem[]>([]);
const [selectedItem, setSelectedItem] = useState<SearchSystemItem[] | null>(null);
const searchItems = useCallback(
async (event: { query: string }) => {
if (event.query.length < 2) {
setFilteredItems([]);
return;
}
const query = event.query;
if (query.length === 0) {
setFilteredItems([]);
} else {
try {
const result = await outCommand({
type: OutCommand.searchSystems,
data: {
text: query,
},
});
let prepared = (result.systems as SearchSystemItem[]).sort((a, b) => {
const amatch = a.label.indexOf(query);
const bmatch = b.label.indexOf(query);
return amatch - bmatch;
});
if (excludedSystems) {
prepared = prepared.filter(x => !excludedSystems.includes(x.system_static_info.solar_system_id));
}
setFilteredItems(prepared);
} catch (error) {
console.error('Error fetching data:', error);
setFilteredItems([]);
}
}
},
[excludedSystems, outCommand],
);
const ref = useRef({ onSubmit, selectedItem });
ref.current = { onSubmit, selectedItem };
const handleSubmit = useCallback(() => {
const { onSubmit, selectedItem } = ref.current;
setFilteredItems([]);
setSelectedItem([]);
if (!selectedItem) {
setVisible(false);
return;
}
onSubmit?.(selectedItem[0]);
setVisible(false);
}, [setVisible]);
return (
<Dialog
header={title}
visible={visible}
draggable={false}
style={{ width: '520px' }}
onShow={onShow}
onHide={() => {
if (!visible) {
return;
}
setVisible(false);
}}
>
<div className="flex flex-col gap-3 px-1.5">
<div className="flex flex-col gap-2 py-3.5">
<div className="flex flex-col gap-1">
<IconField>
<AutoComplete
ref={inputRef}
multiple
showEmptyMessage
scrollHeight="300px"
value={selectedItem}
suggestions={filteredItems}
completeMethod={searchItems}
onChange={e => {
setSelectedItem(e.value.length < 2 ? e.value : [e.value[e.value.length - 1]]);
}}
emptyMessage="Not found any system..."
placeholder="Type here..."
field="label"
id="value"
className="w-full"
itemTemplate={(item: SearchSystemItem) => {
const { security, system_class, effect_power, effect_name, statics } = item.system_static_info;
const sortedStatics = sortWHClasses(wormholesData, statics);
const isWH = isWormholeSpace(system_class);
return (
<div className={clsx('flex gap-1.5', classes.SearchItem)}>
<SystemViewStandalone
security={security}
system_class={system_class}
solar_system_id={item.value}
class_title={item.class_title}
solar_system_name={item.label}
region_name={item.region_name}
/>
{effect_name && isWH && (
<WHEffectView
effectName={effect_name}
effectPower={effect_power}
className={classes.SearchItemEffect}
/>
)}
{isWH && (
<div className="flex gap-1 grow justify-between">
<div></div>
<div className="flex gap-1">
{sortedStatics.map(x => (
<WHClassView key={x} whClassName={x} />
))}
</div>
</div>
)}
</div>
);
}}
selectedItemTemplate={(item: SearchSystemItem) => (
<SystemViewStandalone
security={item.system_static_info.security}
system_class={item.system_static_info.system_class}
solar_system_id={item.value}
class_title={item.class_title}
solar_system_name={item.label}
region_name={item.region_name}
/>
)}
/>
</IconField>
<span className="text-[12px] text-stone-400 ml-1">*to search type at least 2 symbols.</span>
</div>
</div>
<div className="flex gap-2 justify-end">
<Button
onClick={handleSubmit}
outlined
disabled={!selectedItem || selectedItem.length !== 1}
size="small"
label="Submit"
/>
</div>
</div>
</Dialog>
);
};

View File

@@ -0,0 +1 @@
export * from './AddSystemDialog';

View File

@@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { Dialog } from 'primereact/dialog';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
@@ -58,7 +58,7 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
<Dialog
header="Select signature to link"
visible
draggable={false}
draggable={true}
style={{ width: '500px' }}
onHide={handleHide}
contentClassName="!p-0"

View File

@@ -3,6 +3,7 @@ import { InputTextarea } from 'primereact/inputtextarea';
import { Dialog } from 'primereact/dialog';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button } from 'primereact/button';
import { OutCommand } from '@/hooks/Mapper/types';
@@ -22,30 +23,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
outCommand,
} = useMapRootState();
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const system = getSystemById(systems, systemId);
const [name, setName] = useState('');
const [label, setLabel] = useState('');
const [temporaryName, setTemporaryName] = useState('');
const [description, setDescription] = useState('');
const inputRef = useRef<HTMLInputElement>();
useEffect(() => {
if (!system) {
return;
}
const labels = new LabelsManager(system.labels || '');
setName(system.name || '');
setLabel(labels.customLabel);
setDescription(system.description || '');
}, [system]);
const ref = useRef({ name, description, label, outCommand, systemId, system });
ref.current = { name, description, label, outCommand, systemId, system };
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system });
ref.current = { name, description, label, temporaryName, outCommand, systemId, system };
const handleSave = useCallback(() => {
const { name, description, label, outCommand, systemId, system } = ref.current;
const { name, description, label, temporaryName, outCommand, systemId, system } = ref.current;
const outLabel = new LabelsManager(system?.labels ?? '');
outLabel.updateCustomLabel(label);
@@ -58,6 +50,14 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
},
});
outCommand({
type: OutCommand.updateSystemTemporaryName,
data: {
system_id: systemId,
value: temporaryName,
},
});
outCommand({
type: OutCommand.updateSystemName,
data: {
@@ -93,6 +93,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
}, []);
// Attention: this effect should be call only on mount.
useEffect(() => {
const { system } = ref.current;
if (!system) {
return;
}
const labels = new LabelsManager(system.labels || '');
setName(system.name || '');
setLabel(labels.customLabel);
setTemporaryName(system.temporary_name || '');
setDescription(system.description || '');
}, []);
return (
<Dialog
header="System settings"
@@ -167,6 +182,35 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
</IconField>
</div>
{isTempSystemNameEnabled && (
<div className="flex flex-col gap-1">
<label htmlFor="username">Temporary Name</label>
<IconField>
{temporaryName !== '' && (
<WdImgButton
className="pi pi-trash text-red-400"
textSize={WdImageSize.large}
tooltip={{
content: 'Remove temporary name',
className: 'pi p-input-icon',
position: TooltipPosition.top,
}}
onClick={() => setTemporaryName('')}
/>
)}
<InputText
id="temporaryName"
aria-describedby="temporaryName"
autoComplete="off"
value={temporaryName}
maxLength={10}
onChange={e => setTemporaryName(e.target.value)}
/>
</IconField>
</div>
)}
<div className="flex flex-col gap-1">
<label htmlFor="username">Description</label>
<InputTextarea

View File

@@ -5,12 +5,14 @@ import clsx from 'clsx';
export interface WidgetProps {
label: React.ReactNode | string;
windowId?: string;
children?: React.ReactNode;
}
export const Widget = ({ label, children }: WidgetProps) => {
export const Widget = ({ label, children, windowId }: WidgetProps) => {
return (
<div
data-window-id={windowId}
className={clsx(
classes.root,
'flex flex-col w-full h-full rounded',

View File

@@ -1,37 +0,0 @@
.GridLayoutWrapper {
width: 100%;
height: 100% !important;
}
.GridLayout {
width: 100%;
height: 100% !important;
pointer-events: none;
& > div {
pointer-events: initial;
}
:global {
.react-resizable-handle::after {
border-color: #696969 !important;
}
.react-grid-placeholder {
background-color: rgba(147, 147, 147, 0.3);
//filter: blur(5px);
border: 2px dashed #b6b6b6;
}
.react-grid-item {
transition-property: none !important;
}
.react-grid-item.cssTransforms {
transition-property: none !important;
}
}
}

View File

@@ -1,196 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import classes from './WidgetsGrid.module.scss';
import { ItemCallback, Layouts, Responsive, WidthProvider } from 'react-grid-layout';
import clsx from 'clsx';
import usePageVisibility from '@/hooks/Mapper/hooks/usePageVisibility.ts';
const ResponsiveGridLayout = WidthProvider(Responsive);
const colSize = 50;
const initState = { breakpoints: 100, cols: 2 };
export type WidgetGridItem = {
rightOffset?: number;
leftOffset?: number;
topOffset?: number;
width: number;
height: number;
name: string;
item: () => React.ReactNode;
};
export interface WidgetsGridProps {
items: WidgetGridItem[];
onChange: (items: WidgetGridItem[]) => void;
}
export const WidgetsGrid = ({ items, onChange }: WidgetsGridProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const [, setKey] = useState(0);
const [callRerenderOfGrid, setCallRerenderOfGrid] = useState(0);
const isTabVisible = usePageVisibility();
const refAll = useRef({
isReady: false,
layouts: {
lg: [
// { i: 'a', w: 4, h: 16, x: 22, y: 0 },
// { i: 'b', w: 5, h: 10, x: 17, y: 0 },
],
} as Layouts,
breakpoints: { lg: 100, md: 0, sm: 0, xs: 0, xxs: 0 },
cols: { lg: 26, md: 0, sm: 0, xs: 0, xxs: 0 },
containerWidth: 0,
colsPrev: 26,
needPostProcess: false,
items: [...items],
});
// TODO
// 1. onLayoutChange (original) not calling when we change x of any widget
// 2. setKey need no call rerender for update props
const onLayoutChange: ItemCallback = (newItems, _, newItem) => {
const updatedItems = newItems.map(item => {
const toLeft = (item.x + item.w / 2) / refAll.current.cols.lg <= 0.5;
const original = refAll.current.items.find(x => x.name === item.i)!;
return {
...original,
width: item.w,
height: item.h,
leftOffset: toLeft ? item.x : undefined,
rightOffset: !toLeft ? refAll.current.cols.lg - (item.x + item.w) : undefined,
topOffset: item.y,
};
});
const sortedItems = [
...updatedItems.filter(x => x.name !== newItem.i),
updatedItems.find(x => x.name === newItem.i)!,
];
refAll.current.layouts = {
lg: [...newItems.filter(x => x.i !== newItem.i), newItem],
};
onChange(sortedItems);
setKey(x => x + 1);
};
useEffect(() => {
refAll.current.items = [...items];
setKey(x => x + 1);
}, [items]);
// TODO
// 1. Unknown why but if we set layout and cols both instantly it not help...
// 1.2 it means that we should make report... until we will send new key on window resize
useEffect(() => {
const updateItems = () => {
if (!containerRef.current) {
return;
}
const { width } = containerRef.current.getBoundingClientRect();
const newColsCount = (width - (width % colSize)) / colSize;
refAll.current.layouts = {
lg: refAll.current.items.map(({ name, width, height, rightOffset, leftOffset, topOffset = 0 }) => {
return {
i: name,
x: rightOffset != null ? newColsCount - width - rightOffset : leftOffset ?? 0,
y: topOffset,
w: width,
h: height,
};
}),
};
refAll.current.cols = { lg: newColsCount, md: 0, sm: 0, xs: 0, xxs: 0 };
};
const updateContainerWidth = () => {
if (!containerRef.current) {
return;
}
const { width } = containerRef.current.getBoundingClientRect();
refAll.current.containerWidth = width;
const newColsCount = (width - (width % colSize)) / colSize;
if (width <= 100 || refAll.current.cols.lg === newColsCount) {
return false;
}
if (!refAll.current.isReady) {
updateItems();
setCallRerenderOfGrid(x => x + 1);
refAll.current.isReady = true;
return;
}
refAll.current.layouts = {
lg: refAll.current.layouts.lg.map(lgEl => {
const toLeft = (lgEl.x + lgEl.w / 2) / refAll.current.cols.lg <= 0.5;
const next = {
...lgEl,
x: toLeft ? lgEl.x : newColsCount - (refAll.current.cols.lg - lgEl.x),
};
return next;
}),
};
refAll.current.cols = { lg: newColsCount, md: 0, sm: 0, xs: 0, xxs: 0 };
setCallRerenderOfGrid(x => x + 1);
};
setTimeout(() => updateContainerWidth(), 100);
const withRerender = () => {
updateContainerWidth();
setCallRerenderOfGrid(x => x + 1);
};
window.addEventListener('resize', withRerender);
return () => {
window.removeEventListener('resize', withRerender);
};
}, []);
const isNotSet = initState.cols === refAll.current.cols.lg;
return (
<div ref={containerRef} className={clsx(classes.GridLayoutWrapper, 'relative p-4')}>
{!isNotSet && isTabVisible && (
<ResponsiveGridLayout
key={callRerenderOfGrid}
className={classes.GridLayout}
layouts={refAll.current.layouts}
breakpoints={refAll.current.breakpoints}
cols={refAll.current.cols}
rowHeight={30}
width={refAll.current.containerWidth}
preventCollision={true}
compactType={null}
allowOverlap
onDragStop={onLayoutChange}
onResizeStop={onLayoutChange}
// onResizeStart={onLayoutChange}
// onDragStart={onLayoutChange}
isBounded
containerPadding={[0, 0]}
resizeHandles={['sw', 'se']}
draggableHandle=".react-grid-dragHandleExample"
>
{refAll.current.items.map(x => (
<div key={x.name} className="grid-item">
{x.item()}
</div>
))}
</ResponsiveGridLayout>
)}
</div>
);
};

View File

@@ -1 +0,0 @@
export * from './WidgetsGrid';

View File

@@ -1,5 +1,4 @@
export * from './Widget';
export * from './WidgetsGrid';
export * from './SystemSettingsDialog';
export * from './SystemCustomLabelDialog';
export * from './SystemLinkSignatureDialog';

View File

@@ -0,0 +1,105 @@
import { WindowProps } from '@/hooks/Mapper/components/ui-kit/WindowManager/types.ts';
import {
LocalCharacters,
RoutesWidget,
SystemInfo,
SystemSignatures,
SystemStructures,
SystemKills,
} from '@/hooks/Mapper/components/mapInterface/widgets';
export const CURRENT_WINDOWS_VERSION = 8;
export const WINDOWS_LOCAL_STORE_KEY = 'windows:settings:v2';
export enum WidgetsIds {
info = 'info',
signatures = 'signatures',
local = 'local',
routes = 'routes',
structures = 'structures',
kills = 'kills',
}
export const STORED_VISIBLE_WIDGETS_DEFAULT = [
WidgetsIds.info,
WidgetsIds.local,
WidgetsIds.routes,
WidgetsIds.signatures,
];
export const DEFAULT_WIDGETS: WindowProps[] = [
{
id: WidgetsIds.info,
position: { x: 10, y: 10 },
size: { width: 250, height: 200 },
zIndex: 0,
content: () => <SystemInfo />,
},
{
id: WidgetsIds.signatures,
position: { x: 10, y: 220 },
size: { width: 250, height: 300 },
zIndex: 0,
content: () => <SystemSignatures />,
},
{
id: WidgetsIds.local,
position: { x: 270, y: 10 },
size: { width: 250, height: 510 },
zIndex: 0,
content: () => <LocalCharacters />,
},
{
id: WidgetsIds.routes,
position: { x: 10, y: 530 },
size: { width: 510, height: 200 },
zIndex: 0,
content: () => <RoutesWidget />,
},
{
id: WidgetsIds.structures,
position: { x: 10, y: 730 },
size: { width: 510, height: 200 },
zIndex: 0,
content: () => <SystemStructures />,
},
{
id: WidgetsIds.kills,
position: { x: 270, y: 730 },
size: { width: 510, height: 200 },
zIndex: 0,
content: () => <SystemKills />,
},
];
type WidgetsCheckboxesType = {
id: WidgetsIds;
label: string;
}[];
export const WIDGETS_CHECKBOXES_PROPS: WidgetsCheckboxesType = [
{
id: WidgetsIds.info,
label: 'System Info',
},
{
id: WidgetsIds.signatures,
label: 'Signatures',
},
{
id: WidgetsIds.local,
label: 'Local',
},
{
id: WidgetsIds.routes,
label: 'Routes',
},
{
id: WidgetsIds.structures,
label: 'Structures',
},
{
id: WidgetsIds.kills,
label: 'Kills',
},
];

View File

@@ -1,13 +1,3 @@
.VirtualScroller {
height: 100% !important;
}
.CharacterRow {
//border-left-width: 1px;
&.CardBorderLeftIsOwn {
border-left-color: rgb(251 146 60 / 1)
}
}

View File

@@ -1,123 +1,71 @@
import { useCallback, useMemo } from 'react';
import { useMemo } from 'react';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import clsx from 'clsx';
import classes from './LocalCharacters.module.scss';
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { CharacterCard, LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
import useLocalStorageState from 'use-local-storage-state';
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters';
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
type CharItemProps = {
compact: boolean;
} & CharacterTypeRaw &
WithIsOwnCharacter;
const useItemTemplate = () => {
const {
data: { presentCharacters },
} = useMapRootState();
return useCallback(
(char: CharItemProps, options: VirtualScrollerTemplateOptions) => {
return (
<div
className={clsx(classes.CharacterRow, 'w-full box-border', {
'surface-hover': options.odd,
['border-b border-gray-600 border-opacity-20']: !options.last,
['bg-green-500 hover:bg-green-700 transition duration-300 bg-opacity-10 hover:bg-opacity-10']: char.online,
})}
style={{ height: options.props.itemSize + 'px' }}
>
<CharacterCard showShipName {...char} />
</div>
);
},
// eslint-disable-next-line
[presentCharacters],
);
};
type WindowLocalSettingsType = {
compact: boolean;
showOffline: boolean;
version: number;
};
const STORED_DEFAULT_VALUES: WindowLocalSettingsType = {
compact: true,
showOffline: false,
version: 0,
};
import { UserPermission } from '@/hooks/Mapper/types/permissions';
import { LocalCharactersList } from './components/LocalCharactersList';
import { useLocalCharactersItemTemplate } from './hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from './hooks/useLocalWidgetSettings';
import { LocalCharactersHeader } from './components/LocalCharactersHeader';
import classes from './LocalCharacters.module.scss';
import clsx from 'clsx';
export const LocalCharacters = () => {
const {
data: { characters, userCharacters, selectedSystems, presentCharacters },
data: { characters, userCharacters, selectedSystems },
} = useMapRootState();
const [settings, setSettings] = useLocalStorageState<WindowLocalSettingsType>('window:local:settings', {
defaultValue: STORED_DEFAULT_VALUES,
});
const [settings, setSettings] = useLocalCharacterWidgetSettings();
const [systemId] = selectedSystems;
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
const showOffline = useMemo(
() => !restrictOfflineShowing || isAdminOrManager,
[isAdminOrManager, restrictOfflineShowing],
);
const itemTemplate = useItemTemplate();
const sorted = useMemo(() => {
const sorted = characters
const filtered = characters
.filter(x => x.location?.solar_system_id?.toString() === systemId)
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
.map(x => ({
...x,
isOwn: userCharacters.includes(x.eve_id),
compact: settings.compact,
showShipName: settings.showShipName,
}))
.sort(sortCharacters);
if (!showOffline || !settings.showOffline) {
return sorted.filter(c => c.online);
return filtered.filter(c => c.online);
}
return sorted;
// eslint-disable-next-line
}, [showOffline, characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
return filtered;
}, [
characters,
systemId,
userCharacters,
settings.compact,
settings.showOffline,
settings.showShipName,
showOffline,
]);
const isNobodyHere = sorted.length === 0;
const isNotSelectedSystem = selectedSystems.length !== 1;
const showList = sorted.length > 0 && selectedSystems.length === 1;
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full">
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
<LayoutEventBlocker className="flex items-center gap-2">
{showOffline && (
<WdCheckbox
size="xs"
labelSide="left"
label={'Show offline'}
value={settings.showOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
/>
)}
<span
className={clsx('w-4 h-4 cursor-pointer', {
['hero-bars-2']: settings.compact,
['hero-bars-3']: !settings.compact,
})}
onClick={() => setSettings(() => ({ ...settings, compact: !settings.compact }))}
></span>
</LayoutEventBlocker>
</div>
<LocalCharactersHeader
sortedCount={sorted.length}
showList={showList}
showOffline={showOffline}
settings={settings}
setSettings={setSettings}
/>
}
>
{isNotSelectedSystem && (
@@ -125,23 +73,20 @@ export const LocalCharacters = () => {
System is not selected
</div>
)}
{isNobodyHere && !isNotSelectedSystem && (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
Nobody here
</div>
)}
{showList && (
<VirtualScroller
<LocalCharactersList
items={sorted}
itemSize={settings.compact ? 26 : 41}
itemTemplate={itemTemplate}
className={clsx(
classes.VirtualScroller,
containerClassName={clsx(
'w-full h-full overflow-x-hidden overflow-y-auto custom-scrollbar select-none',
classes.VirtualScroller,
)}
autoSize={false}
/>
)}
</Widget>

View File

@@ -0,0 +1,76 @@
import React, { useRef } from 'react';
import clsx from 'clsx';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth';
import { LayoutEventBlocker, TooltipPosition, WdCheckbox, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
interface LocalCharactersHeaderProps {
sortedCount: number;
showList: boolean;
showOffline: boolean;
settings: {
compact: boolean;
showOffline: boolean;
showShipName: boolean;
};
setSettings: (fn: (prev: any) => any) => void;
}
export const LocalCharactersHeader: React.FC<LocalCharactersHeaderProps> = ({
sortedCount,
showList,
showOffline,
settings,
setSettings,
}) => {
const headerRef = useRef<HTMLDivElement>(null);
const compactOffline = useMaxWidth(headerRef, 145);
const compactShipName = useMaxWidth(headerRef, 195);
return (
<div className="flex w-full items-center text-xs justify-between" ref={headerRef}>
<div className="flex-shrink-0 select-none mr-2">Local{showList ? ` [${sortedCount}]` : ''}</div>
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
<div className="flex items-center gap-2">
{showOffline && (
<WdTooltipWrapper content="Show offline characters in system" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelSide="left"
label={compactOffline ? '' : 'Offline'}
value={settings.showOffline}
onChange={() => setSettings((prev: any) => ({ ...prev, showOffline: !prev.showOffline }))}
classNameLabel={clsx('whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300', {
truncate: compactOffline,
})}
/>
</WdTooltipWrapper>
)}
{settings.compact && (
<WdTooltipWrapper content="Show ship name in compact rows" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelSide="left"
label={compactShipName ? '' : 'Ship name'}
value={settings.showShipName}
onChange={() => setSettings((prev: any) => ({ ...prev, showShipName: !prev.showShipName }))}
classNameLabel={clsx('whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300', {
truncate: compactShipName,
})}
/>
</WdTooltipWrapper>
)}
</div>
<WdTooltipWrapper content="Enable compact mode" position={TooltipPosition.top}>
<span
className={clsx('w-4 h-4 min-w-[1rem] cursor-pointer', {
'hero-bars-2': settings.compact,
'hero-bars-3': !settings.compact,
})}
onClick={() => setSettings((prev: any) => ({ ...prev, compact: !prev.compact }))}
/>
</WdTooltipWrapper>
</LayoutEventBlocker>
</div>
);
};

View File

@@ -0,0 +1,6 @@
.CharacterRow {
&.CardBorderLeftIsOwn {
border-left-color: rgb(251 146 60 / 1)
}
}

View File

@@ -0,0 +1,27 @@
import classes from './LocalCharactersItemTemplate.module.scss';
import clsx from 'clsx';
import { CharacterCard } from '@/hooks/Mapper/components/ui-kit';
import { CharItemProps } from '@/hooks/Mapper/components/mapInterface/widgets/LocalCharacters/components';
import { VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
export type LocalCharactersItemTemplateProps = { showShipName: boolean } & CharItemProps &
VirtualScrollerTemplateOptions;
export const LocalCharactersItemTemplate = ({ showShipName, ...options }: LocalCharactersItemTemplateProps) => {
return (
<div
className={clsx(
classes.CharacterRow,
'box-border flex items-center w-full whitespace-nowrap overflow-hidden text-ellipsis min-w-[0px]',
{
'surface-hover': options.odd,
'border-b border-gray-600 border-opacity-20': !options.last,
'bg-green-500 hover:bg-green-700 transition duration-300 bg-opacity-10 hover:bg-opacity-10': options.online,
},
)}
style={{ height: `${options.props.itemSize}px` }}
>
<CharacterCard showShipName={showShipName} {...options} />
</div>
);
};

View File

@@ -0,0 +1 @@
export * from './LocalCharactersItemTemplate.tsx';

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import clsx from 'clsx';
import { CharItemProps } from './types';
export type LocalCharactersListProps = {
items: Array<CharItemProps>;
itemSize: number;
itemTemplate: (char: CharItemProps, options: VirtualScrollerTemplateOptions) => React.ReactNode;
containerClassName?: string;
style?: React.CSSProperties;
autoSize?: boolean;
};
export const LocalCharactersList = ({
items,
itemSize,
itemTemplate,
containerClassName,
style = {},
autoSize = false,
}: LocalCharactersListProps) => {
const computedHeight = autoSize ? `${Math.max(items.length, 1) * itemSize}px` : style.height || '100%';
const localStyle: React.CSSProperties = {
...style,
height: computedHeight,
width: '100%',
boxSizing: 'border-box',
overflowX: 'hidden',
};
return (
<VirtualScroller
items={items}
itemSize={itemSize}
orientation="vertical"
className={clsx('w-full h-full', containerClassName)}
itemTemplate={itemTemplate}
autoSize={autoSize}
style={localStyle}
/>
);
};

View File

@@ -0,0 +1,3 @@
export * from './LocalCharactersItemTemplate';
export * from './LocalCharactersList';
export * from './types';

View File

@@ -0,0 +1,6 @@
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
export type CharItemProps = {
compact: boolean;
} & CharacterTypeRaw &
WithIsOwnCharacter;

View File

@@ -0,0 +1,12 @@
import { useCallback } from 'react';
import { VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import { CharItemProps, LocalCharactersItemTemplate } from '../components';
export function useLocalCharactersItemTemplate(showShipName: boolean) {
return useCallback(
(char: CharItemProps, options: VirtualScrollerTemplateOptions) => (
<LocalCharactersItemTemplate {...char} {...options} showShipName={showShipName} />
),
[showShipName],
);
}

View File

@@ -0,0 +1,21 @@
import useLocalStorageState from 'use-local-storage-state';
export interface LocalCharacterWidgetSettings {
compact: boolean;
showOffline: boolean;
version: number;
showShipName: boolean;
}
export const LOCAL_CHARACTER_WIDGET_DEFAULT: LocalCharacterWidgetSettings = {
compact: true,
showOffline: false,
version: 0,
showShipName: false,
};
export function useLocalCharacterWidgetSettings() {
return useLocalStorageState<LocalCharacterWidgetSettings>('kills:widget:settings', {
defaultValue: LOCAL_CHARACTER_WIDGET_DEFAULT,
});
}

View File

@@ -8,7 +8,6 @@
.RouteSystem {
width: 8px;
height: 8px;
background: #ffffff;
cursor: pointer;
transition: opacity 200ms;

View File

@@ -1,7 +1,7 @@
import classes from './RoutesList.module.scss';
import { Route, SystemStaticInfoShort } from '@/hooks/Mapper/types/routes.ts';
import clsx from 'clsx';
import { SystemViewStandalone, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import { SystemViewStandalone, TooltipPosition, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import { getBackgroundClass, getShapeClass } from '@/hooks/Mapper/components/map/helpers';
import { MouseEvent, useCallback, useRef, useState } from 'react';
import { Commands } from '@/hooks/Mapper/types';
@@ -46,9 +46,11 @@ export const RouteSystem = ({
<>
<WdTooltip
ref={tooltipRef}
position={TooltipPosition.top}
// targetSelector={`.tooltip-route-sys_${destination}_${solar_system_id}`}
content={() => (
<SystemViewStandalone
className="mx-[4px]"
security={security}
system_class={system_class}
class_title={class_title}
@@ -63,8 +65,8 @@ export const RouteSystem = ({
tooltipRef.current?.show(e);
onMouseEnter?.(solar_system_id);
}}
onMouseLeave={e => {
tooltipRef.current?.hide(e);
onMouseLeave={() => {
tooltipRef.current?.hide();
onMouseLeave?.();
}}
onContextMenu={handleContext}

View File

@@ -19,6 +19,13 @@ import { PrimeIcons } from 'primereact/api';
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import {
AddSystemDialog,
SearchOnSubmitCallback,
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
import { OutCommand } from '@/hooks/Mapper/types';
const sortByDist = (a: Route, b: Route) => {
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
@@ -161,6 +168,12 @@ export const RoutesWidgetContent = () => {
export const RoutesWidgetComp = () => {
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
const { data, update } = useRouteProvider();
const {
data: { hubs = [] },
outCommand,
} = useMapRootState();
const preparedHubs = useMemo(() => hubs.map(x => parseInt(x)), [hubs]);
const isSecure = data.path_type === 'secure';
const handleSecureChange = useCallback(() => {
@@ -170,27 +183,70 @@ export const RoutesWidgetComp = () => {
});
}, [data, update]);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 170);
const [openAddSystem, setOpenAddSystem] = useState<boolean>(false);
const onAddSystem = useCallback(() => setOpenAddSystem(true), []);
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
async item => {
if (preparedHubs.includes(item.value)) {
return;
}
await outCommand({
type: OutCommand.addHub,
data: { system_id: item.value },
});
},
[hubs, outCommand],
);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full">
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<span className="select-none">Routes</span>
<LayoutEventBlocker className="flex items-center gap-2">
<WdCheckbox
size="xs"
labelSide="left"
label={'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel={clsx('text-red-400')}
<WdImgButton
className={PrimeIcons.PLUS_CIRCLE}
onClick={onAddSystem}
tooltip={{
content: 'Click here to add new system to routes',
}}
/>
<WdTooltipWrapper content="Show shortest route" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel="text-red-400 whitespace-nowrap"
/>
</WdTooltipWrapper>
<WdImgButton
className={PrimeIcons.SLIDERS_H}
onClick={() => setRouteSettingsVisible(true)}
tooltip={{
content: 'Click here to open Routes settings',
}}
/>
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
</LayoutEventBlocker>
</div>
}
>
<RoutesWidgetContent />
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
<AddSystemDialog
title="Add system to routes"
visible={openAddSystem}
setVisible={() => setOpenAddSystem(false)}
onSubmit={handleSubmitAddSystem}
/>
</Widget>
);
};

View File

@@ -0,0 +1,109 @@
import React, { useMemo, useState } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { SystemKillsContent } from './SystemKillsContent/SystemKillsContent';
import { KillsHeader } from './components/SystemKillsHeader';
import { useKillsWidgetSettings } from './hooks/useKillsWidgetSettings';
import { useSystemKills } from './hooks/useSystemKills';
import { KillsSettingsDialog } from './components/SystemKillsSettingsDialog';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { SolarSystemRawType } from '@/hooks/Mapper/types';
export const SystemKills: React.FC = React.memo(() => {
const {
data: { selectedSystems, systems, isSubscriptionActive },
outCommand,
} = useMapRootState();
const [systemId] = selectedSystems || [];
const [settingsDialogVisible, setSettingsDialogVisible] = useState(false);
const systemNameMap = useMemo(() => {
const map: Record<string, string> = {};
systems.forEach(sys => {
map[sys.id] = sys.temporary_name || sys.name || '???';
});
return map;
}, [systems]);
const systemBySolarSystemId = useMemo(() => {
const map: Record<number, SolarSystemRawType> = {};
systems.forEach(sys => {
if (sys.system_static_info?.solar_system_id != null) {
map[sys.system_static_info.solar_system_id] = sys;
}
});
return map;
}, [systems]);
const [settings] = useKillsWidgetSettings();
const visible = settings.showAll;
const { kills, isLoading, error } = useSystemKills({
systemId,
outCommand,
showAllVisible: visible,
});
const isNothingSelected = !systemId && !visible;
const showLoading = isLoading && kills.length === 0;
const filteredKills = useMemo(() => {
if (!settings.whOnly || !visible) return kills;
return kills.filter(kill => {
const system = systemBySolarSystemId[kill.solar_system_id];
if (!system) {
console.warn(`System with id ${kill.solar_system_id} not found.`);
return false;
}
return isWormholeSpace(system.system_static_info.system_class);
});
}, [kills, settings.whOnly, systemBySolarSystemId, visible]);
return (
<div className="h-full flex flex-col min-h-0">
<div className="flex flex-col flex-1 min-h-0">
<Widget label={<KillsHeader systemId={systemId} onOpenSettings={() => setSettingsDialogVisible(true)} />}>
{!isSubscriptionActive ? (
<div className="w-full h-full flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">
Kills available with &#39;Active&#39; map subscription only (contact map administrators)
</span>
</div>
) : isNothingSelected ? (
<div className="w-full h-full flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">
No system selected (or toggle Show all systems)
</span>
</div>
) : showLoading ? (
<div className="w-full h-full flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">Loading Kills...</span>
</div>
) : error ? (
<div className="w-full h-full flex items-center justify-center">
<span className="select-none text-center text-red-400 text-sm">{error}</span>
</div>
) : !filteredKills || filteredKills.length === 0 ? (
<div className="w-full h-full flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">No kills found</span>
</div>
) : (
<div className="w-full h-full" style={{ height: '100%' }}>
<SystemKillsContent
kills={filteredKills}
systemNameMap={systemNameMap}
onlyOneSystem={!visible}
timeRange={settings.timeRange}
/>
</div>
)}
</Widget>
</div>
{settingsDialogVisible && <KillsSettingsDialog visible setVisible={setSettingsDialogVisible} />}
</div>
);
});
SystemKills.displayName = 'SystemKills';

View File

@@ -0,0 +1,14 @@
.wrapper {
overflow-x: hidden;
box-sizing: border-box;
}
.scrollerContent {
width: 100%;
box-sizing: border-box;
overflow-x: hidden;
}
.VirtualScroller {
height: 100% !important;
}

View File

@@ -0,0 +1,68 @@
import React, { useMemo } from 'react';
import clsx from 'clsx';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { VirtualScroller } from 'primereact/virtualscroller';
import { useSystemKillsItemTemplate } from '../hooks/useSystemKillsItemTemplate';
import classes from './SystemKillsContent.module.scss';
export const ITEM_HEIGHT = 35;
export interface SystemKillsContentProps {
kills: DetailedKill[];
systemNameMap: Record<string, string>;
onlyOneSystem?: boolean;
autoSize?: boolean;
timeRange?: number;
limit?: number;
}
export const SystemKillsContent: React.FC<SystemKillsContentProps> = ({
kills,
systemNameMap,
onlyOneSystem = false,
autoSize = false,
timeRange = 4,
limit,
}) => {
const processedKills = useMemo(() => {
const sortedKills = kills
.filter(k => k.kill_time)
.sort((a, b) => new Date(b.kill_time!).getTime() - new Date(a.kill_time!).getTime());
if (limit !== undefined) {
return sortedKills.slice(0, limit);
} else {
const now = Date.now();
const cutoff = now - timeRange * 60 * 60 * 1000;
return sortedKills.filter(k => new Date(k.kill_time!).getTime() >= cutoff);
}
}, [kills, timeRange, limit]);
const computedHeight = autoSize ? Math.max(processedKills.length, 1) * ITEM_HEIGHT : undefined;
const scrollerHeight = autoSize ? `${computedHeight}px` : '100%';
const itemTemplate = useSystemKillsItemTemplate(systemNameMap, onlyOneSystem);
return (
<div className={clsx('w-full h-full', classes.wrapper)}>
<VirtualScroller
items={processedKills}
itemSize={ITEM_HEIGHT}
itemTemplate={itemTemplate}
autoSize={autoSize}
scrollWidth="100%"
style={{ height: scrollerHeight }}
className={clsx('w-full h-full custom-scrollbar select-none', {
[classes.VirtualScroller]: !autoSize,
})}
pt={{
content: {
className: classes.scrollerContent,
},
}}
/>
</div>
);
};
export default SystemKillsContent;

View File

@@ -0,0 +1,20 @@
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import { KillRow } from './SystemKillsRow';
import clsx from 'clsx';
export function KillItemTemplate(
systemNameMap: Record<string, string>,
onlyOneSystem: boolean,
kill: DetailedKill,
options: VirtualScrollerTemplateOptions,
) {
const systemIdStr = String(kill.solar_system_id);
const systemName = systemNameMap[systemIdStr] || `System ${systemIdStr}`;
return (
<div style={{ height: `${options.props.itemSize}px` }} className={clsx({ 'bg-gray-900': options.odd })}>
<KillRow killDetails={kill} systemName={systemName} onlyOneSystem={onlyOneSystem} />
</div>
);
}

View File

@@ -0,0 +1,30 @@
.killRowContainer {
@apply flex items-center whitespace-nowrap overflow-hidden;
&:not(:last-child) {
@apply border-b border-stone-800;
}
@apply bg-transparent transition-all hover:bg-stone-900 hover:border-stone-700;
}
.killRowImage {
@apply border border-stone-800 rounded-[4px] object-contain;
}
.attackerCountLabel {
position: absolute;
bottom: 0;
right: 0;
font-size: 10px;
padding: 0 2px;
}
.attackerCountLabelCompact {
position: absolute;
left: 0;
bottom: 0;
font-size: 0.6rem;
line-height: 1;
background-color: rgba(0, 0, 0, 0.7);
padding: 1px 2px;
pointer-events: none;
}

View File

@@ -0,0 +1,203 @@
import React from 'react';
import clsx from 'clsx';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import {
formatISK,
formatTimeMixed,
zkillLink,
getAttackerSubscript,
buildVictimImageUrls,
buildAttackerImageUrls,
getPrimaryLogoAndTooltip,
getAttackerPrimaryImageAndTooltip,
} from '../helpers';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import classes from './KillRowDetail.module.scss';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
export interface CompactKillRowProps {
killDetails: DetailedKill;
systemName: string;
onlyOneSystem: boolean;
}
export const KillRowDetail: React.FC<CompactKillRowProps> = ({ killDetails, systemName, onlyOneSystem }) => {
const {
killmail_id = 0,
// Victim data
victim_char_name = 'Unknown Pilot',
victim_alliance_ticker = '',
victim_corp_ticker = '',
victim_ship_name = 'Unknown Ship',
victim_corp_name = '',
victim_alliance_name = '',
victim_char_id = 0,
victim_corp_id = 0,
victim_alliance_id = 0,
victim_ship_type_id = 0,
// Attacker data
final_blow_char_id = 0,
final_blow_char_name = '',
final_blow_alliance_ticker = '',
final_blow_alliance_name = '',
final_blow_alliance_id = 0,
final_blow_corp_ticker = '',
final_blow_corp_id = 0,
final_blow_corp_name = '',
final_blow_ship_type_id = 0,
kill_time = '',
total_value = 0,
} = killDetails || {};
const attackerIsNpc = final_blow_char_id === 0;
// Define victim affiliation ticker.
const victimAffiliationTicker = victim_alliance_ticker || victim_corp_ticker || 'No Ticker';
const killValueFormatted = total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
const killTimeAgo = kill_time ? formatTimeMixed(kill_time) : '0h ago';
const attackerSubscript = getAttackerSubscript(killDetails);
const { victimCorpLogoUrl, victimAllianceLogoUrl, victimShipUrl } = buildVictimImageUrls({
victim_char_id,
victim_ship_type_id,
victim_corp_id,
victim_alliance_id,
});
const { attackerCorpLogoUrl, attackerAllianceLogoUrl } = buildAttackerImageUrls({
final_blow_char_id,
final_blow_corp_id,
final_blow_alliance_id,
});
const { url: victimPrimaryLogoUrl, tooltip: victimPrimaryTooltip } = getPrimaryLogoAndTooltip(
victimAllianceLogoUrl,
victimCorpLogoUrl,
victim_alliance_name,
victim_corp_name,
'Victim',
);
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } = getAttackerPrimaryImageAndTooltip(
attackerIsNpc,
attackerAllianceLogoUrl,
attackerCorpLogoUrl,
final_blow_alliance_name,
final_blow_corp_name,
final_blow_ship_type_id,
);
// Define attackerTicker to use the alliance ticker if available, otherwise the corp ticker.
const attackerTicker = attackerIsNpc ? '' : final_blow_alliance_ticker || final_blow_corp_ticker || '';
// For the attacker image link: if the attacker is not an NPC, link to the character page; otherwise, link to the kill page.
const attackerLink = attackerIsNpc ? zkillLink('kill', killmail_id) : zkillLink('character', final_blow_char_id);
return (
<div
className={clsx(
'h-10 flex items-center border-b border-stone-800',
'text-xs whitespace-nowrap overflow-hidden leading-none',
)}
>
{/* Victim Section */}
<div className="flex items-center gap-1">
{victimShipUrl && (
<div className="relative shrink-0 w-8 h-8 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={victimShipUrl}
alt="Victim Ship"
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
</a>
</div>
)}
{victimPrimaryLogoUrl && (
<WdTooltipWrapper content={victimPrimaryTooltip} position={TooltipPosition.top}>
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="relative block shrink-0 w-8 h-8 overflow-hidden"
>
<img
src={victimPrimaryLogoUrl}
alt="Victim Primary Logo"
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
</a>
</WdTooltipWrapper>
)}
</div>
<div className="flex flex-col ml-2 flex-1 min-w-0 overflow-hidden leading-[1rem]">
<div className="truncate text-stone-200">
{victim_char_name}
<span className="text-stone-400"> / {victimAffiliationTicker}</span>
</div>
<div className="truncate text-stone-300">
{victim_ship_name}
{killValueFormatted && (
<>
<span className="ml-1 text-stone-400">/</span>
<span className="ml-1 text-green-400">{killValueFormatted}</span>
</>
)}
</div>
</div>
<div className="flex items-center ml-auto gap-2">
<div className="flex flex-col items-end flex-1 min-w-0 overflow-hidden text-right leading-[1rem]">
{!attackerIsNpc && (final_blow_char_name || attackerTicker) && (
<div className="truncate text-stone-200">
{final_blow_char_name}
{!attackerIsNpc && attackerTicker && <span className="ml-1 text-stone-400">/ {attackerTicker}</span>}
</div>
)}
<div className="truncate text-stone-400">
{!onlyOneSystem && systemName ? (
<>
{systemName} / <span className="ml-1 text-red-400">{killTimeAgo}</span>
</>
) : (
<span className="text-red-400">{killTimeAgo}</span>
)}
</div>
</div>
{attackerPrimaryImageUrl && (
<WdTooltipWrapper content={attackerPrimaryTooltip} position={TooltipPosition.top}>
<a
href={attackerLink}
target="_blank"
rel="noopener noreferrer"
className="relative block shrink-0 w-8 h-8 overflow-hidden"
>
<img
src={attackerPrimaryImageUrl}
alt={attackerIsNpc ? 'NPC Ship' : 'Attacker Primary Logo'}
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
{attackerSubscript && (
<span
className={clsx(
classes.attackerCountLabel,
attackerSubscript.cssClass,
'text-[0.6rem] leading-none px-[2px]',
)}
>
{attackerSubscript.label}
</span>
)}
</a>
</WdTooltipWrapper>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,65 @@
import React, { useRef } from 'react';
import {
LayoutEventBlocker,
SystemView,
TooltipPosition,
WdCheckbox,
WdImgButton,
WdTooltipWrapper,
} from '@/hooks/Mapper/components/ui-kit';
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
import { PrimeIcons } from 'primereact/api';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
interface KillsHeaderProps {
systemId?: string;
onOpenSettings: () => void;
}
export const KillsHeader: React.FC<KillsHeaderProps> = ({ systemId, onOpenSettings }) => {
const [settings, setSettings] = useKillsWidgetSettings();
const { showAll } = settings;
const onToggleShowAllVisible = () => {
setSettings(prev => ({ ...prev, showAll: !prev.showAll }));
};
const headerRef = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(headerRef, 150);
return (
<div className="flex w-full items-center justify-between text-xs" ref={headerRef}>
<div className="flex items-center gap-1">
<div className="text-stone-400">
Kills
{systemId && !showAll && ' in '}
</div>
{systemId && !showAll && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
</div>
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
<div className="flex items-center gap-2">
<WdTooltipWrapper content="Show all systems" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? 'All' : 'Show all systems'}
value={showAll}
onChange={onToggleShowAllVisible}
classNameLabel="whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300"
/>
</WdTooltipWrapper>
<WdImgButton
className={PrimeIcons.SLIDERS_H}
onClick={onOpenSettings}
tooltip={{
content: 'Open Kills Settings',
position: TooltipPosition.top,
}}
/>
</div>
</LayoutEventBlocker>
</div>
);
};

View File

@@ -0,0 +1,15 @@
import React from 'react';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { KillRowDetail } from './KillRowDetail.tsx';
export interface KillRowProps {
killDetails: DetailedKill;
systemName: string;
onlyOneSystem?: boolean;
}
const KillRowComponent: React.FC<KillRowProps> = ({ killDetails, systemName, onlyOneSystem = false }) => {
return <KillRowDetail killDetails={killDetails} systemName={systemName} onlyOneSystem={onlyOneSystem} />;
};
export const KillRow = React.memo(KillRowComponent);

View File

@@ -0,0 +1,163 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import { WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { PrimeIcons } from 'primereact/api';
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
import {
AddSystemDialog,
SearchOnSubmitCallback,
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
import { SystemView, TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
interface KillsSettingsDialogProps {
visible: boolean;
setVisible: (visible: boolean) => void;
}
export const KillsSettingsDialog: React.FC<KillsSettingsDialogProps> = ({ visible, setVisible }) => {
const [globalSettings, setGlobalSettings] = useKillsWidgetSettings();
const localRef = useRef({
showAll: globalSettings.showAll,
whOnly: globalSettings.whOnly,
excludedSystems: globalSettings.excludedSystems || [],
timeRange: globalSettings.timeRange,
});
const [, forceRender] = useState(0);
const [addSystemDialogVisible, setAddSystemDialogVisible] = useState(false);
useEffect(() => {
if (visible) {
localRef.current = {
showAll: globalSettings.showAll,
whOnly: globalSettings.whOnly,
excludedSystems: globalSettings.excludedSystems || [],
timeRange: globalSettings.timeRange,
};
forceRender(n => n + 1);
}
}, [visible, globalSettings]);
const handleWHChange = useCallback((checked: boolean) => {
localRef.current = {
...localRef.current,
whOnly: checked,
};
forceRender(n => n + 1);
}, []);
const handleTimeRangeChange = useCallback((newTimeRange: number) => {
localRef.current = {
...localRef.current,
timeRange: newTimeRange,
};
forceRender(n => n + 1);
}, []);
const handleRemoveSystem = useCallback((sysId: number) => {
localRef.current = {
...localRef.current,
excludedSystems: localRef.current.excludedSystems.filter(id => id !== sysId),
};
forceRender(n => n + 1);
}, []);
const handleAddSystemSubmit: SearchOnSubmitCallback = useCallback(item => {
if (localRef.current.excludedSystems.includes(item.value)) {
return;
}
localRef.current = {
...localRef.current,
excludedSystems: [...localRef.current.excludedSystems, item.value],
};
forceRender(n => n + 1);
}, []);
const handleApply = useCallback(() => {
setGlobalSettings(prev => ({
...prev,
...localRef.current,
}));
setVisible(false);
}, [setGlobalSettings, setVisible]);
const handleHide = useCallback(() => {
setVisible(false);
}, [setVisible]);
const localData = localRef.current;
const excluded = localData.excludedSystems || [];
const timeRangeOptions = [4, 12, 24];
return (
<Dialog header="Kills Settings" visible={visible} style={{ width: '440px' }} draggable={false} onHide={handleHide}>
<div className="flex flex-col gap-3 p-2.5">
<div className="flex items-center gap-2">
<input
type="checkbox"
id="kills-wormhole-only-mode"
checked={localData.whOnly}
onChange={e => handleWHChange(e.target.checked)}
/>
<label htmlFor="kills-wormhole-only-mode" className="cursor-pointer">
Only show wormhole kills
</label>
</div>
<div className="flex flex-col gap-1">
<span className="text-sm">Time Range:</span>
<div className="flex flex-wrap gap-2">
{timeRangeOptions.map(option => (
<label key={option} className="cursor-pointer flex items-center gap-1">
<input
type="radio"
name="timeRange"
value={option}
checked={localData.timeRange === option}
onChange={() => handleTimeRangeChange(option)}
/>
<span className="text-sm">{option} Hours</span>
</label>
))}
</div>
</div>
{/* Excluded Systems */}
<div className="flex flex-col gap-1">
<div className="flex items-center justify-between">
<label className="text-sm text-stone-400">Excluded Systems</label>
<WdImgButton
className={PrimeIcons.PLUS_CIRCLE}
onClick={() => setAddSystemDialogVisible(true)}
tooltip={{ content: 'Add system to excluded list' }}
/>
</div>
{excluded.length === 0 && <div className="text-stone-500 text-xs italic">No systems excluded.</div>}
{excluded.map(sysId => (
<div key={sysId} className="flex items-center justify-between border-b border-stone-600 py-1 px-1 text-xs">
<SystemView systemId={sysId.toString()} hideRegion />
<WdImgButton
className={PrimeIcons.TRASH}
onClick={() => handleRemoveSystem(sysId)}
tooltip={{ content: 'Remove from excluded', position: TooltipPosition.top }}
/>
</div>
))}
</div>
<div className="flex gap-2 justify-end mt-4">
<Button onClick={handleApply} label="Apply" outlined size="small" />
</div>
</div>
<AddSystemDialog
title="Add system to kills exclude list"
visible={addSystemDialogVisible}
setVisible={() => setAddSystemDialogVisible(false)}
onSubmit={handleAddSystemSubmit}
excludedSystems={excluded}
/>
</Dialog>
);
};

View File

@@ -0,0 +1,2 @@
export * from './linkHelpers';
export * from './killRowUtils';

View File

@@ -0,0 +1,47 @@
import { DetailedKill } from '@/hooks/Mapper/types/kills';
/** Returns "5m ago", "3h ago", "2.5d ago", etc. */
export function formatTimeMixed(killTime: string): string {
const killDate = new Date(killTime);
const diffMs = Date.now() - killDate.getTime();
const diffHours = diffMs / (1000 * 60 * 60);
if (diffHours < 1) {
const mins = Math.round(diffHours * 60);
return `${mins}m ago`;
} else if (diffHours < 24) {
const hours = Math.round(diffHours);
return `${hours}h ago`;
} else {
const days = diffHours / 24;
const roundedDays = days.toFixed(1);
return `${roundedDays}d ago`;
}
}
/** Formats integer ISK values into k/M/B/T. */
export function formatISK(value: number): string {
if (value >= 1_000_000_000_000) {
return `${(value / 1_000_000_000_000).toFixed(2)}T`;
} else if (value >= 1_000_000_000) {
return `${(value / 1_000_000_000).toFixed(2)}B`;
} else if (value >= 1_000_000) {
return `${(value / 1_000_000).toFixed(2)}M`;
} else if (value >= 1_000) {
return `${(value / 1_000).toFixed(2)}k`;
}
return Math.round(value).toString();
}
export function getAttackerSubscript(kill: DetailedKill) {
if (kill.npc) {
return { label: 'npc', cssClass: 'text-purple-400' };
}
const count = kill.attacker_count ?? 0;
if (count === 1) {
return { label: 'solo', cssClass: 'text-green-400' };
} else if (count > 1) {
return { label: String(count), cssClass: 'text-white' };
}
return null;
}

View File

@@ -0,0 +1,111 @@
const ZKILL_URL = 'https://zkillboard.com';
const BASE_IMAGE_URL = 'https://images.evetech.net';
export function zkillLink(type: 'kill' | 'character' | 'corporation' | 'alliance', id?: number | null): string {
if (!id) return `${ZKILL_URL}`;
if (type === 'kill') return `${ZKILL_URL}/kill/${id}/`;
if (type === 'character') return `${ZKILL_URL}/character/${id}/`;
if (type === 'corporation') return `${ZKILL_URL}/corporation/${id}/`;
if (type === 'alliance') return `${ZKILL_URL}/alliance/${id}/`;
return `${ZKILL_URL}`;
}
export function eveImageUrl(
category: 'characters' | 'corporations' | 'alliances' | 'types',
id?: number | null,
variation: string = 'icon',
size?: number,
): string | null {
if (!id || id <= 0) {
return null;
}
let url = `${BASE_IMAGE_URL}/${category}/${id}/${variation}`;
if (size) {
url += `?size=${size}`;
}
return url;
}
export function buildVictimImageUrls(args: {
victim_char_id?: number | null;
victim_ship_type_id?: number | null;
victim_corp_id?: number | null;
victim_alliance_id?: number | null;
}) {
const { victim_char_id, victim_ship_type_id, victim_corp_id, victim_alliance_id } = args;
const victimPortraitUrl = eveImageUrl('characters', victim_char_id, 'portrait', 64);
const victimShipUrl = eveImageUrl('types', victim_ship_type_id, 'render', 64);
const victimCorpLogoUrl = eveImageUrl('corporations', victim_corp_id, 'logo', 32);
const victimAllianceLogoUrl = eveImageUrl('alliances', victim_alliance_id, 'logo', 32);
return {
victimPortraitUrl,
victimShipUrl,
victimCorpLogoUrl,
victimAllianceLogoUrl,
};
}
export function buildAttackerShipUrl(final_blow_ship_type_id?: number | null): string | null {
return eveImageUrl('types', final_blow_ship_type_id, 'render', 64);
}
export function buildAttackerImageUrls(args: {
final_blow_char_id?: number | null;
final_blow_corp_id?: number | null;
final_blow_alliance_id?: number | null;
}) {
const { final_blow_char_id, final_blow_corp_id, final_blow_alliance_id } = args;
const attackerPortraitUrl = eveImageUrl('characters', final_blow_char_id, 'portrait', 64);
const attackerCorpLogoUrl = eveImageUrl('corporations', final_blow_corp_id, 'logo', 32);
const attackerAllianceLogoUrl = eveImageUrl('alliances', final_blow_alliance_id, 'logo', 32);
return {
attackerPortraitUrl,
attackerCorpLogoUrl,
attackerAllianceLogoUrl,
};
}
export function getPrimaryLogoAndTooltip(
allianceUrl: string | null,
corpUrl: string | null,
allianceName: string,
corpName: string,
fallback: string,
) {
let url: string | null = null;
let tooltip = '';
if (allianceUrl) {
url = allianceUrl;
tooltip = allianceName || fallback;
} else if (corpUrl) {
url = corpUrl;
tooltip = corpName || fallback;
}
return { url, tooltip };
}
export function getAttackerPrimaryImageAndTooltip(
isNpc: boolean,
allianceUrl: string | null,
corpUrl: string | null,
allianceName: string,
corpName: string,
finalBlowShipTypeId: number | null,
npcFallback: string = 'NPC Attacker',
) {
if (isNpc) {
const shipUrl = buildAttackerShipUrl(finalBlowShipTypeId);
return {
url: shipUrl,
tooltip: npcFallback,
};
}
return getPrimaryLogoAndTooltip(allianceUrl, corpUrl, allianceName, corpName, 'Attacker');
}

View File

@@ -0,0 +1,53 @@
import { useMemo, useCallback } from 'react';
import useLocalStorageState from 'use-local-storage-state';
export interface KillsWidgetSettings {
showAll: boolean;
whOnly: boolean;
excludedSystems: number[];
version: number;
timeRange: number;
}
export const DEFAULT_KILLS_WIDGET_SETTINGS: KillsWidgetSettings = {
showAll: false,
whOnly: true,
excludedSystems: [],
version: 2,
timeRange: 1,
};
function mergeWithDefaults(settings?: Partial<KillsWidgetSettings>): KillsWidgetSettings {
if (!settings) {
return DEFAULT_KILLS_WIDGET_SETTINGS;
}
return {
...DEFAULT_KILLS_WIDGET_SETTINGS,
...settings,
excludedSystems: Array.isArray(settings.excludedSystems) ? settings.excludedSystems : [],
};
}
export function useKillsWidgetSettings() {
const [rawValue, setRawValue] = useLocalStorageState<KillsWidgetSettings | undefined>('kills:widget:settings');
const value = useMemo<KillsWidgetSettings>(() => {
return mergeWithDefaults(rawValue);
}, [rawValue]);
const setValue = useCallback(
(newVal: KillsWidgetSettings | ((prev: KillsWidgetSettings) => KillsWidgetSettings)) => {
setRawValue(prev => {
const mergedPrev = mergeWithDefaults(prev);
const nextUnmerged = typeof newVal === 'function' ? newVal(mergedPrev) : newVal;
return mergeWithDefaults(nextUnmerged);
});
},
[setRawValue],
);
return [value, setValue] as const;
}

View File

@@ -0,0 +1,183 @@
import { useCallback, useMemo, useState, useEffect, useRef } from 'react';
import debounce from 'lodash.debounce';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useKillsWidgetSettings } from './useKillsWidgetSettings';
interface UseSystemKillsProps {
systemId?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
outCommand: (payload: any) => Promise<any>;
showAllVisible?: boolean;
sinceHours?: number;
}
function combineKills(existing: DetailedKill[], incoming: DetailedKill[], sinceHours: number): DetailedKill[] {
const cutoff = Date.now() - sinceHours * 60 * 60 * 1000;
const byId: Record<string, DetailedKill> = {};
for (const kill of [...existing, ...incoming]) {
if (!kill.kill_time) {
continue;
}
const killTimeMs = new Date(kill.kill_time).valueOf();
if (killTimeMs >= cutoff) {
byId[kill.killmail_id] = kill;
}
}
return Object.values(byId);
}
export function useSystemKills({ systemId, outCommand, showAllVisible = false, sinceHours = 24 }: UseSystemKillsProps) {
const { data, update } = useMapRootState();
const { detailedKills = {}, systems = [] } = data;
const [settings] = useKillsWidgetSettings();
const excludedSystems = settings.excludedSystems;
// When showing all visible kills, filter out excluded systems;
// when showAllVisible is false, ignore the exclusion filter.
const effectiveSystemIds = useMemo(() => {
if (showAllVisible) {
return systems.map(s => s.id).filter(id => !excludedSystems.includes(Number(id)));
}
return systems.map(s => s.id);
}, [systems, excludedSystems, showAllVisible]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const didFallbackFetch = useRef(Object.keys(detailedKills).length !== 0);
const mergeKillsIntoGlobal = useCallback(
(killsMap: Record<string, DetailedKill[]>) => {
update(prev => {
const oldMap = prev.detailedKills ?? {};
const updated: Record<string, DetailedKill[]> = { ...oldMap };
for (const [sid, newKills] of Object.entries(killsMap)) {
const existing = updated[sid] ?? [];
const combined = combineKills(existing, newKills, sinceHours);
updated[sid] = combined;
}
return {
...prev,
detailedKills: updated,
};
});
},
[update, sinceHours],
);
const fetchKills = useCallback(
async (forceFallback = false) => {
setIsLoading(true);
setError(null);
try {
let eventType: OutCommand;
let requestData: Record<string, unknown>;
if (showAllVisible || forceFallback) {
eventType = OutCommand.getSystemsKills;
requestData = {
system_ids: effectiveSystemIds,
since_hours: sinceHours,
};
} else if (systemId) {
eventType = OutCommand.getSystemKills;
requestData = {
system_id: systemId,
since_hours: sinceHours,
};
} else {
// If there's no system and not showing all, do nothing
setIsLoading(false);
return;
}
const resp = await outCommand({
type: eventType,
data: requestData,
});
// Single system => `resp.kills`
if (resp?.kills) {
const arr = resp.kills as DetailedKill[];
const sid = systemId ?? 'unknown';
mergeKillsIntoGlobal({ [sid]: arr });
}
// multiple systems => `resp.systems_kills`
else if (resp?.systems_kills) {
mergeKillsIntoGlobal(resp.systems_kills as Record<string, DetailedKill[]>);
} else {
console.warn('[useSystemKills] Unexpected kills response =>', resp);
}
} catch (err) {
console.error('[useSystemKills] Failed to fetch kills:', err);
setError(err instanceof Error ? err.message : 'Error fetching kills');
} finally {
setIsLoading(false);
}
},
[showAllVisible, systemId, outCommand, effectiveSystemIds, sinceHours, mergeKillsIntoGlobal],
);
const debouncedFetchKills = useMemo(
() =>
debounce(fetchKills, 500, {
leading: true,
trailing: false,
}),
[fetchKills],
);
const finalKills = useMemo(() => {
if (showAllVisible) {
return effectiveSystemIds.flatMap(sid => detailedKills[sid] ?? []);
} else if (systemId) {
return detailedKills[systemId] ?? [];
} else if (didFallbackFetch.current) {
// if we already did a fallback, we may have data for multiple systems
return effectiveSystemIds.flatMap(sid => detailedKills[sid] ?? []);
}
return [];
}, [showAllVisible, systemId, effectiveSystemIds, detailedKills]);
const effectiveIsLoading = isLoading && finalKills.length === 0;
useEffect(() => {
if (!systemId && !showAllVisible && !didFallbackFetch.current) {
didFallbackFetch.current = true;
// Cancel any queued debounced calls, then do the fallback.
debouncedFetchKills.cancel();
fetchKills(true); // forceFallback => fetch as though showAllVisible is true
}
}, [systemId, showAllVisible, debouncedFetchKills, fetchKills]);
useEffect(() => {
if (effectiveSystemIds.length === 0) return;
if (showAllVisible || systemId) {
debouncedFetchKills();
// Clean up the debounce on unmount or changes
return () => debouncedFetchKills.cancel();
}
}, [showAllVisible, systemId, effectiveSystemIds, debouncedFetchKills]);
const refetch = useCallback(() => {
debouncedFetchKills.cancel();
fetchKills(); // immediate (non-debounced) call
}, [debouncedFetchKills, fetchKills]);
return {
kills: finalKills,
isLoading: effectiveIsLoading,
error,
refetch,
};
}

View File

@@ -0,0 +1,13 @@
// useSystemKillsItemTemplate.tsx
import { useCallback } from 'react';
import { VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { KillItemTemplate } from '../components/KillItemTemplate';
export function useSystemKillsItemTemplate(systemNameMap: Record<string, string>, onlyOneSystem: boolean) {
return useCallback(
(kill: DetailedKill, options: VirtualScrollerTemplateOptions) =>
KillItemTemplate(systemNameMap, onlyOneSystem, kill, options),
[systemNameMap, onlyOneSystem],
);
}

View File

@@ -0,0 +1 @@
export * from './SystemKills';

View File

@@ -0,0 +1,96 @@
import React from 'react';
import { SystemView, WdCheckbox, WdImgButton, TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { PrimeIcons } from 'primereact/api';
import { CheckboxChangeEvent } from 'primereact/checkbox';
import { InfoDrawer, LayoutEventBlocker } from '@/hooks/Mapper/components/ui-kit';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
export type HeaderProps = {
systemId: string;
isNotSelectedSystem: boolean;
sigCount: number;
isCompact: boolean;
lazyDeleteValue: boolean;
onLazyDeleteChange: (checked: boolean) => void;
pendingCount: number;
onUndoClick: () => void;
onSettingsClick: () => void;
};
function HeaderImpl({
systemId,
isNotSelectedSystem,
sigCount,
isCompact,
lazyDeleteValue,
onLazyDeleteChange,
pendingCount,
onUndoClick,
onSettingsClick,
}: HeaderProps) {
return (
<div className="flex justify-between items-center text-xs w-full h-full">
<div className="flex justify-between items-center gap-1">
{!isCompact && (
<div className="flex whitespace-nowrap text-ellipsis overflow-hidden text-stone-400">
{sigCount ? `[${sigCount}] ` : ''}Signatures {isNotSelectedSystem ? '' : 'in'}
</div>
)}
{!isNotSelectedSystem && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
</div>
<LayoutEventBlocker className="flex gap-2.5">
<WdTooltipWrapper content="Enable Lazy delete">
<WdCheckbox
size="xs"
labelSide="left"
label={isCompact ? '' : 'Lazy delete'}
value={lazyDeleteValue}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300 whitespace-nowrap text-ellipsis overflow-hidden"
onChange={(event: CheckboxChangeEvent) => onLazyDeleteChange(!!event.checked)}
/>
</WdTooltipWrapper>
{pendingCount > 0 && (
<WdImgButton
className={PrimeIcons.UNDO}
style={{ color: 'red' }}
tooltip={{ content: `Undo pending changes (${pendingCount})` }}
onClick={onUndoClick}
/>
)}
<WdImgButton
className={PrimeIcons.QUESTION_CIRCLE}
tooltip={{
position: TooltipPosition.left,
content: (
<div className="flex flex-col gap-1">
<InfoDrawer title={<b className="text-slate-50">How to add/update signature?</b>}>
In game you need to select one or more signatures <br /> in the list in{' '}
<b className="text-sky-500">Probe scanner</b>. <br /> Use next hotkeys:
<br />
<b className="text-sky-500">Shift + LMB</b> or <b className="text-sky-500">Ctrl + LMB</b>
<br /> or <b className="text-sky-500">Ctrl + A</b> for select all
<br /> and then use <b className="text-sky-500">Ctrl + C</b>, after you need to go <br />
here, select Solar system and paste it with <b className="text-sky-500">Ctrl + V</b>
</InfoDrawer>
<InfoDrawer title={<b className="text-slate-50">How to select?</b>}>
For selecting any signature, click on it <br /> with hotkeys{' '}
<b className="text-sky-500">Shift + LMB</b> or <b className="text-sky-500">Ctrl + LMB</b>
</InfoDrawer>
<InfoDrawer title={<b className="text-slate-50">How to delete?</b>}>
To delete any signature, first select it <br /> and then press <b className="text-sky-500">Del</b>
</InfoDrawer>
</div>
),
}}
/>
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={onSettingsClick} />
</LayoutEventBlocker>
</div>
);
}
export const SystemSignaturesHeader = React.memo(HeaderImpl);

Some files were not shown because too many files have changed in this diff Show More