Compare commits

..

311 Commits

Author SHA1 Message Date
CI
802e81b1cd chore: release version v1.65.7 2025-05-26 14:05:57 +00:00
Dmitry Popov
41f0834c51 chore: release version v1.65.6 2025-05-26 16:05:12 +02:00
Dmitry Popov
880de0b047 chore: release version v1.65.6 2025-05-26 15:57:34 +02:00
Dmitry Popov
bbe7fda4e0 fix(Core): Fixed map character tracking issues 2025-05-26 15:56:46 +02:00
CI
92a9274dce chore: release version v1.65.6 2025-05-26 10:19:27 +00:00
Dmitry Popov
8765d83083 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-26 12:09:13 +02:00
Dmitry Popov
a298152bc8 fix(Core): Fixed map character tracking issues 2025-05-26 12:09:09 +02:00
CI
2b7abe5774 chore: release version v1.65.5
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-05-26 00:11:32 +00:00
Dmitry Popov
3e9241892e fix(Core): Fixed map character tracking issues 2025-05-26 01:41:21 +02:00
Dmitry Popov
6ea79a7960 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-25 09:41:39 +02:00
Dmitry Popov
2af562e313 fix(Signature): Update restored signature character 2025-05-25 09:41:33 +02:00
CI
40672f6a47 chore: release version v1.65.4 2025-05-24 17:30:03 +00:00
Dmitry Popov
6d66ae3f50 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-24 19:18:57 +02:00
Dmitry Popov
94c89e0325 fix(Signature): Force signature update even if there are no any changes 2025-05-24 19:18:50 +02:00
CI
3670ef40a3 chore: release version v1.65.3 2025-05-23 18:29:03 +00:00
Dmitry Popov
16d464fba5 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-23 20:06:20 +02:00
Dmitry Popov
0b7e0b9cd0 fix(Signature): Fixed signature clenup 2025-05-23 20:06:17 +02:00
CI
dd5fd114d2 chore: release version v1.65.2 2025-05-23 15:33:00 +00:00
Dmitry Popov
6e53879344 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-23 17:24:33 +02:00
Dmitry Popov
af2bfd4d59 fix(Signature): Fixed signature updates 2025-05-23 17:24:30 +02:00
CI
a4a34c8ba7 chore: release version v1.65.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-05-22 11:47:01 +00:00
Dmitry Popov
8c609f4fdf Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-22 11:52:23 +02:00
Dmitry Popov
197f5b583f fix(Core): Added unsync map events timeout handling (force page refresh if outdated map events found) 2025-05-22 11:52:20 +02:00
CI
4eb4a03e59 chore: release version v1.65.0 2025-05-22 09:45:26 +00:00
Dmitry Popov
3d4e66d438 Merge pull request #399 from guarzo/guarzo/moreopenapi
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
fix: remove required id field from character schema
2025-05-21 20:07:32 +04:00
Guarzo
ffbc9f169a fix: remove required id field from character schema 2025-05-21 11:59:03 -04:00
Dmitry Popov
99650187e9 Merge pull request #397 from guarzo/guarzo/c1fix
fix: correct issue with connection types between connected k-space systems
2025-05-21 17:47:21 +04:00
Guarzo
92699317cd fix: update openapi spec response types 2025-05-21 09:19:12 -04:00
Guarzo
0e48315803 fix: fix issue with connection generation between k-space 2025-05-21 09:19:12 -04:00
Dmitry Popov
868ec246bd Merge pull request #395 from wanderer-industries/develop
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
Develop
2025-05-21 15:39:01 +04:00
Dmitry Popov
0030a688c6 Merge branch 'develop' of github.com:wanderer-industries/wanderer into develop 2025-05-21 11:52:18 +02:00
Dmitry Popov
3ba8f51a2f fix(Signature): Fixed signatures updates 2025-05-21 11:52:15 +02:00
Dmitry Popov
04576b335c Merge pull request #392 from guarzo/guarzo/c1qol
feat: default c1 connections to medium
2025-05-21 13:12:04 +04:00
Dmitry Popov
ea29aa176f Merge branch 'main' into develop 2025-05-21 08:46:48 +02:00
CI
9a9b7289ba chore: release version v1.64.8
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-05-20 14:33:21 +00:00
Dmitry Popov
d601790864 fix(Core): Added unsync map events timeout handling (force page refresh if outdated map events found) 2025-05-20 16:08:43 +02:00
Dmitry Popov
bf58d3ae93 Merge branch 'main' into develop 2025-05-20 11:24:46 +02:00
Guarzo
d6c32e2d39 feat: default connections from c1 holes to medium size 2025-05-20 04:39:17 -04:00
Dmitry Popov
bdc4948afb Merge pull request #366 from guarzo/guarzo/undo
feat: improve signature undo process
2025-05-19 22:44:13 +04:00
Guarzo
331db10029 fix: update openapi spec for other apis 2025-05-19 11:35:15 -04:00
Dmitry Popov
2daf9e34d2 chore: release version v1.64.7
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-05-19 15:30:06 +02:00
CI
558cd9b8b3 chore: release version v1.64.7
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-05-15 21:40:47 +00:00
Dmitry Popov
a0f02d0d2f Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-15 23:15:10 +02:00
Dmitry Popov
9feb8492aa fix(Core): Fixed connection EOL time refreshed every 2 minutes 2025-05-15 23:15:07 +02:00
CI
e5aa726899 chore: release version v1.64.6
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-05-15 10:23:04 +00:00
Dmitry Popov
93d1c28ccd Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-15 12:02:02 +02:00
Dmitry Popov
b5ba9200bc fix(Core): Added map hubs limits checking & a proper warning message shown 2025-05-15 12:01:59 +02:00
CI
699d866670 chore: release version v1.64.5
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-05-14 22:13:32 +00:00
Dmitry Popov
c3071344cb chore: Added link to YT channel 2025-05-14 23:59:06 +02:00
Dmitry Popov
9e998dd2b6 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-14 23:24:57 +02:00
Dmitry Popov
c9accf6079 fix(Core): Added character name update on re-auth 2025-05-14 23:24:49 +02:00
CI
1b41a51004 chore: release version v1.64.4 2025-05-14 20:49:45 +00:00
guarzo
3338dce900 Merge branch 'develop' into guarzo/undo 2025-05-14 15:04:42 -04:00
Guarzo
1364779f81 feat: support german and french signatures 2025-05-14 10:38:18 -04:00
Dmitry Popov
b49d3423fc fix(Core): Added 1 min timeout for ship and location updates on ESI API errors
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-05-14 16:35:21 +02:00
CI
cccab2a985 chore: release version v1.64.3 2025-05-14 10:14:49 +00:00
Dmitry Popov
1abaa90a7d Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-14 11:54:41 +02:00
Dmitry Popov
6e1993ca8a fix(Core): Fixed character tracking initialization logic & removed search caching 2025-05-14 11:54:37 +02:00
CI
171c821ac4 chore: release version v1.64.2
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 / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-05-13 21:51:53 +00:00
Dmitry Popov
7ebf9186bf Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-13 23:42:32 +02:00
Dmitry Popov
57d2f2baef fix(Core): Fixed tracking of ship & location for offline characters 2025-05-13 23:42:30 +02:00
CI
0aee13878a chore: release version v1.64.1 2025-05-13 19:40:55 +00:00
Dmitry Popov
f93ef0ca76 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-13 21:31:06 +02:00
Dmitry Popov
4ec03d8338 fix(Core): Fixed tracking stopped due to server errors 2025-05-13 21:31:02 +02:00
guarzo
cb318aa6c6 Merge branch 'develop' into guarzo/undo 2025-05-13 12:35:11 -04:00
CI
733482cd5c chore: release version v1.64.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-05-13 10:42:59 +00:00
Dmitry Popov
3969d1287d fix(Core): Fixed EOL connections cleanup 2025-05-13 12:26:58 +02:00
Dmitry Popov
1aa7854b0d chore: Added ESI API cached responces based on expire headers 2025-05-13 12:08:06 +02:00
Dmitry Popov
7b27d4a1a7 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-13 11:09:39 +02:00
Dmitry Popov
24ddb8771f fix(Core): Avoid Zarzakh system in routes widget 2025-05-13 11:09:35 +02:00
Dmitry Popov
7134714245 Merge pull request #376 from wanderer-industries/develop
Develop
2025-05-13 13:02:47 +04:00
guarzo
96b320ac26 fix: remove repeat errors for token refresh (#375) 2025-05-13 13:01:05 +04:00
guarzo
1a27b21efe Merge branch 'develop' into guarzo/undo 2025-05-12 11:55:19 -04:00
guarzo
b88e121b30 fix: updated openapi spec for character activity (#374) 2025-05-12 19:09:18 +04:00
guarzo
4ba4119c2b fix: removed error from characters endpoint, and updated routes (#372) 2025-05-12 11:15:20 +04:00
Dmitry Popov
91d1ca201c Merge branch 'main' into develop 2025-05-11 14:35:53 +02:00
guarzo
8bf063a228 fix: cleanup examples for system and connections (#370) 2025-05-11 16:20:23 +04:00
CI
4f53de39b1 chore: release version v1.63.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-05-11 12:17:06 +00:00
Dmitry Popov
8c3804f107 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-11 14:03:57 +02:00
Dmitry Popov
1be4ec2b90 feat(Core): Updated map active characters page 2025-05-11 14:03:53 +02:00
Dmitry Popov
8f0ed44b11 chore: release version v1.62.3 2025-05-10 20:46:01 +02:00
CI
cbadfc4ac4 chore: release version v1.62.4
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-05-10 12:08:58 +00:00
Dmitry Popov
3d88ae4452 fix(Core): Fixed map characters got untracked 2025-05-10 13:46:49 +02:00
guarzo
e57f565812 Merge branch 'develop' into guarzo/undo 2025-05-09 18:59:46 -04:00
Guarzo
da2605ee03 feat: improve signature undo process 2025-05-09 18:59:33 -04:00
Dmitry Popov
07e2196eb4 Merge branch 'main' into develop 2025-05-09 14:02:39 +02:00
CI
6d99c54af7 chore: release version v1.62.3
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-05-08 12:14:20 +00:00
Dmitry Popov
2b7901e9a8 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-08 12:20:14 +02:00
Dmitry Popov
fb06dd1dbc fix(Core): Fixed map characters got untracked 2025-05-08 12:20:09 +02:00
guarzo
d3b825529e fix: remove error on websocket reconnect (#367) 2025-05-07 12:24:17 +04:00
guarzo
ccf9c0db22 feat (api): add additional structure/signature methods (#365) 2025-05-06 20:42:47 +04:00
CI
f8ba36b8be chore: release version v1.62.2
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-05-05 18:51:41 +00:00
Dmitry Popov
5bf9d99b3d Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-05 20:32:17 +02:00
Dmitry Popov
7cad05342a fix(Core): Fixed audit export API 2025-05-05 20:32:14 +02:00
CI
867780e525 chore: release version v1.62.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-05-05 09:14:15 +00:00
Dmitry Popov
ff4f9a79c9 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-05 10:18:44 +02:00
Dmitry Popov
6699c36fb3 chore: load user roads for map with active subscription only 2025-05-05 10:18:41 +02:00
CI
abd4556994 chore: release version v1.62.0 2025-05-05 08:05:56 +00:00
Dmitry Popov
ccf0d17371 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-05 09:46:42 +02:00
Dmitry Popov
898584bbb6 fix(Map): Fixed link signature modal crash afrer destination system removed 2025-05-05 09:46:39 +02:00
Aleksei Chichenkov
6d7a267e39 fix(Map): Change design for tags (#358)
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
Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
2025-05-04 14:43:59 +04:00
Dmitry Popov
9f656ca3cb chore: send hubs limit as part of init event 2025-05-04 11:25:38 +02:00
Aleksei Chichenkov
fede6451e2 Merge pull request #352 from wanderer-industries/custom-hubs
Custom hubs
2025-05-04 11:39:33 +03:00
achichenkov
9797ad380c Merge remote-tracking branch 'origin/custom-hubs' into custom-hubs 2025-05-04 11:32:14 +03:00
achichenkov
33bc4a4d22 fix(Map): Removed paywall restriction from public routes 2025-05-04 11:31:56 +03:00
guarzo
6378754c57 feat (api): add additional system/connections methods (#351) 2025-05-03 19:41:21 +04:00
Dmitry Popov
30fc972d78 Merge branch 'main' into custom-hubs 2025-05-03 10:29:30 +02:00
Dmitry Popov
c022b31c79 Merge branch 'main' of github.com:wanderer-industries/wanderer
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-05-03 10:26:20 +02:00
Dmitry Popov
049b06bb39 fix(Core): Fixed issues with structures loading 2025-05-03 10:26:17 +02:00
achichenkov
e17d5213c0 fix(Map): Removed unnecessary logs 2025-04-30 13:18:17 +03:00
achichenkov
dcf681941e fix(Map): Add support user routes 2025-04-30 13:17:31 +03:00
CI
1cd7d40405 chore: release version v1.61.2
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-04-29 10:40:50 +00:00
Dmitry Popov
fbd80ba2c7 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-04-29 12:28:01 +02:00
Dmitry Popov
88ab85bd04 fix(Core): Fixed main character checking & manual systems delete logic 2025-04-29 12:27:55 +02:00
Dmitry Popov
78f98744fd chore(Map): Fixed user routes api 2025-04-28 00:21:55 +02:00
achichenkov
9c9634a927 fix(Map): Add support for User Routes on FE side. 2025-04-27 15:09:28 +03:00
CI
be47be626c chore: release version v1.61.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-04-26 21:48:21 +00:00
Dmitry Popov
2fbd3d8e19 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-04-26 23:19:31 +02:00
Dmitry Popov
d5c3d4c051 fix(Core): Fixed additional price calc for map sub updates
Apply discounts and recalc period based on subscription
2025-04-26 23:19:27 +02:00
achichenkov
fac60f7ddd fix(Map): Refactor Local - show ship name, change placement of ship name. Refactor On the Map - show corp and ally logo. Fixed problem with ellipsis at long character and ship names. 2025-04-26 16:22:24 +03:00
CI
c371478c61 chore: release version v1.61.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-04-24 12:52:40 +00:00
Dmitry Popov
5911e29f34 feat(Core): force checking main character set for all map activity 2025-04-24 13:45:01 +02:00
achichenkov
99d68dfc0e fix(Map): Refactored routes widget. Add loader for routes. Prepared for custom hubs 2025-04-24 12:46:08 +03:00
achichenkov
c9b366f3e2 fix(Map): Refactor init and update of mapper 2025-04-23 08:57:55 +03:00
Dmitry Popov
4e732e9491 chore: refactor map_init/map_update events 2025-04-22 22:35:53 +02:00
Dmitry Popov
dd5b12aa38 chore: refactor map_init/map_update events 2025-04-22 11:57:11 +02:00
Dmitry Popov
7bd960fba9 Merge branch 'main' into custom-hubs 2025-04-22 11:15:47 +02:00
CI
c338c33902 chore: release version v1.60.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-04-22 08:14:35 +00:00
achichenkov
df6b7ae635 Merge branch 'refs/heads/main' into custom-hubs 2025-04-22 11:07:34 +03:00
Aleksei Chichenkov
a3346f8223 Merge pull request #341 from wanderer-industries/whdb-types-fixes-2
Whdb types fixes 2
2025-04-22 11:06:05 +03:00
achichenkov
196f2c2c3b fix(Map): Removed unnecessary code onFE part 2025-04-20 18:34:59 +03:00
Dmitry Popov
77d549ac1b chore: fixed drifter system names 2025-04-19 12:07:17 +02:00
achichenkov
5c3cce66c1 fix(Map): Removed unnecessary debugger 2025-04-19 10:57:56 +03:00
achichenkov
cc2f09601e fix(Map): Changed name for drifters systems. Fixed static info for Barbican. 2025-04-19 10:57:05 +03:00
CI
14af04cc73 chore: release version v1.60.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-04-17 09:26:43 +00:00
guarzo
37c9e68c21 feat (api): api showing character by user and main character (#334) 2025-04-17 13:06:34 +04:00
Dmitry Popov
2bd343b2da feat(Core): force map page reload after 30 mins of user inactivity (switched browser/tab)
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-04-17 00:38:20 +02:00
Dmitry Popov
f5d502c5ad chore: introduced tracker pool servers (100 trackers per server) 2025-04-16 23:58:41 +02:00
guarzo
35cf460ccf feat: update character activity to use main character (#333) 2025-04-16 23:10:07 +04:00
guarzo
28c7b90c3f scroll activity (#331)
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-04-16 21:28:48 +04:00
CI
4fbdaf42e1 chore: release version v1.59.11
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-04-16 06:38:44 +00:00
Aleksei Chichenkov
90910620d9 fix(Map): Fixed lifetime for A009 from 16h to 4.5h. Fixed problem with no appearing icon of shattered for Drifter wormholes. Fixed wanderings for Drifter wormholes. For system J011355 added static K346. For system J011824 added static K346. (#329) 2025-04-16 10:28:15 +04:00
CI
eb4336fef7 chore: release version v1.59.10
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-04-15 22:25:50 +00:00
Dmitry Popov
69264cc8ec chore: release version v1.59.9 2025-04-16 00:18:16 +02:00
CI
ab0cb74ca9 chore: release version v1.59.9 2025-04-15 22:04:46 +00:00
Dmitry Popov
42101ab6fd chore: release version v1.59.8 2025-04-15 23:56:58 +02:00
Dmitry Popov
8d69c70076 fix(Core): Fixed issues with map server manager 2025-04-15 23:52:59 +02:00
CI
beb3077159 chore: release version v1.59.8
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-04-15 10:32:20 +00:00
Dmitry Popov
ecb3ca2b4e fix(Core): Fixed issues with main character & tracking
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-04-15 12:15:08 +02:00
Dmitry Popov
2ba42e0c25 chore: release version v1.59.7 2025-04-14 20:36:09 +02:00
Dmitry Popov
3ef5590e18 Merge branch 'main' into custom-hubs 2025-04-14 20:30:39 +02:00
CI
8412e3867d chore: release version v1.59.7
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-04-14 18:06:11 +00:00
Dmitry Popov
90c40100d1 fix(Core): Fixed auto-select splashed systems
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-04-14 19:56:23 +02:00
CI
92cb49da90 chore: release version v1.59.6
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-04-13 19:31:05 +00:00
Aleksei Chichenkov
abc09c067f fix(Map): Fix icons of main, follow and shattered (#321) 2025-04-13 22:59:56 +04:00
CI
edbd1e4bbc chore: release version v1.59.5
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-04-12 09:36:06 +00:00
Dmitry Popov
75edb91825 fix(Signatures): avoid signatures delete on wrong buffer 2025-04-12 11:23:46 +02:00
Dmitry Popov
602a61b08d chore: release version v1.59.4
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-04-12 02:14:17 +02:00
Aleksei Chichenkov
d8222d83f0 Refactoring and fixing problems (#317)
* fix(Map): fix design of kills widget, fix design of signatures widget - refactor a lot of code, fixed problem with tooltip blinking

* fix(Map): refactor Tracking dialog, refactor Activity tracker, refactor codebase and some styles

* fix(Core): don't count character passage on manual add connection

* refactor(Core): improved characters tracking API

* fix(Core): fixed link signature to system on 'leads to' set

* fix(Map): Refactor map settings and prepared it to easier using

* fix(Map): Add support new command for following update

* fix(Map): Add support new command for main update

* refactor(Core): Reduce map init data by using cached system static data

* refactor(Core): Reduce map init data by extract signatures loading to a separate event

* fix(Core): adjusted IP rate limits

* fix(Map): Update design of tracking characters. Added icons for following and main. Added ability to see that character on the station or structure

---------

Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
Co-authored-by: Dmitry Popov <dmitriypopovsamara@gmail.com>
2025-04-11 23:17:53 +04:00
CI
7da5512d45 chore: release version v1.59.4
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-04-10 22:07:50 +00:00
Dmitry Popov
8bf9ae7824 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-04-10 23:58:55 +02:00
Dmitry Popov
f57777e417 chore: release version v1.59.2 2025-04-10 23:58:52 +02:00
CI
b3cc3d857a chore: release version v1.59.3 2025-04-10 21:50:32 +00:00
Dmitry Popov
bf442d9e70 chore: release version v1.59.2 2025-04-10 23:42:06 +02:00
windstep
1a556d05ba fixed error in different localization (#312) 2025-04-11 01:41:00 +04:00
CI
dab301e6d3 chore: release version v1.59.2 2025-04-10 21:40:39 +00:00
Dmitry Popov
8ab4b4c788 fix (Core): fixed connection validation 2025-04-10 23:25:33 +02:00
Dmitry Popov
4b29060c96 feat(Core): added user routes support 2025-03-28 09:22:08 +01:00
CI
8a5f96a847 chore: release version v1.59.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-03-26 21:50:35 +00:00
guarzo
149fa57075 fix (doc): improve bot setup instructions (#309) 2025-03-27 01:41:21 +04:00
CI
affe184ccd chore: release version v1.59.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-03-23 10:26:30 +00:00
Dmitry Popov
1e5e73c4ae feat(Core): added handling cases when wrong connections created 2025-03-23 11:06:20 +01:00
Tyson GG
c76316da03 feat (api) add map connections endpoint (#301)
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-03-23 00:43:32 +04:00
CI
de6205f860 chore: release version v1.58.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-03-22 08:38:37 +00:00
Dmitry Popov
f994255091 feat(Core): Show online state on map characters page 2025-03-22 09:29:13 +01:00
Tyson GG
6d4981a3db fix (routes) fix query parameter formatting when calling esi routes endpoint (#302) 2025-03-22 11:53:12 +04:00
guarzo
06fef2296f feat (api): update character activity and api to allow date range (#299)
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
* feat (api): update character activity and api to allow date range
2025-03-21 21:05:48 +04:00
CI
999a702291 chore: release version v1.57.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-03-20 17:51:07 +00:00
Dmitry Popov
020b9bb2c2 chore: added user-agent & ensured cache handled correctly on each request 2025-03-20 18:39:40 +01:00
CI
7713caab51 chore: release version v1.57.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-03-19 15:33:39 +00:00
guarzo
97a777d729 feat (doc): update bot news (#294) 2025-03-19 19:17:25 +04:00
CI
8241d1f08c chore: release version v1.56.6 2025-03-19 14:45:22 +00:00
Dmitry Popov
2ac85bbfff chore: release version v1.56.5 2025-03-19 15:08:51 +01:00
CI
3f68ae2235 chore: release version v1.56.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 / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-03-19 13:07:55 +00:00
Dmitry Popov
0f7b6f75df Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-03-19 13:57:58 +01:00
Dmitry Popov
b048e8f5ca chore: added fallback chipher options 2025-03-19 13:55:39 +01:00
CI
9783dc45ff chore: release version v1.56.4 2025-03-19 11:36:46 +00:00
Dmitry Popov
badbefbade Revert "fix: cloak key error behavior (#288)" (#290)
This reverts commit 9b5ea2f84b.
2025-03-19 15:30:07 +04:00
CI
b6a265cfad chore: release version v1.56.3 2025-03-19 07:26:24 +00:00
guarzo
9b5ea2f84b fix: cloak key error behavior (#288) 2025-03-19 11:13:54 +04:00
guarzo
d8acfa5c05 refactor: standalone unit tests (#278)
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-03-18 21:37:52 +04:00
CI
2a5b6924eb chore: release version v1.56.2 2025-03-18 16:47:40 +00:00
Dmitry Popov
3b9aee1eb9 fix: show signature tooltip on top 2025-03-18 17:33:18 +01:00
CI
83801c9063 chore: release version v1.56.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-03-18 09:34:32 +00:00
guarzo
0f34350c58 fix: update activity api (#284) 2025-03-18 11:51:29 +04:00
guarzo
1c4c0f0715 fix: qol updates for dev (#283) 2025-03-18 11:50:33 +04:00
Dmitry Popov
3825fc831a refactor: removal of legacy event
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-03-17 22:51:29 +01:00
guarzo
654670cbc8 refactor: removal of legacy event (#277)
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-03-18 00:11:32 +04:00
CI
947570072c chore: release version v1.56.0 2025-03-17 18:25:38 +00:00
Dmitry Popov
01b6b45380 fix: character activity hide error 2025-03-17 18:54:22 +01:00
guarzo
b9dc1f8357 fix: character added to map on follow (#272) 2025-03-17 19:42:42 +04:00
guarzo
b4bd810c9d refactor: updates to track and follow (#270) 2025-03-17 17:36:08 +04:00
guarzo
490b037920 refactor: simplify track and follow (#265)
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-03-17 16:08:45 +04:00
guarzo
cdff5458bc feat: add static wh info (#262)
* feat: add static wh info
2025-03-17 16:03:57 +04:00
guarzo
09314a09e9 feat [doc]: new bot release (#234) 2025-03-17 11:43:27 +04:00
guarzo
49ea8edb27 feat (api): add character activity api (#263)
* feat (api): add character activity api
2025-03-17 11:36:45 +04:00
CI
86e5ff2fff chore: release version v1.55.2
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-03-16 20:16:11 +00:00
Dmitry Popov
1ade0ae6b9 Merge branch 'main' of github.com:wanderer-industries/wanderer
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-03-16 20:50:02 +01:00
Dmitry Popov
cadfb59b8d fix(Core): fixed lazy delete reset state 2025-03-16 20:49:58 +01:00
CI
5c2013f19c chore: release version v1.55.1 2025-03-16 12:48:21 +00:00
Dmitry Popov
8db46113f4 fix(Core): fixed lazy delete timeouts 2025-03-16 13:32:39 +01:00
Dmitry Popov
3028a0b1c0 Merge branch 'main' of github.com:wanderer-industries/wanderer
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-03-15 23:45:01 +01:00
Dmitry Popov
e9b4e39061 fix(Core): fixed lazy delete settings 2025-03-15 23:44:57 +01:00
guarzo
9c6715e4e5 fix: keep character api off by default (#258)
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-03-15 22:38:50 +04:00
CI
0b03d5ee90 chore: release version v1.55.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-03-15 10:54:13 +00:00
Dmitry Popov
30fecf6428 feat(News): added map subscription news 2025-03-15 11:31:26 +01:00
Dmitry Popov
752eaaa0f5 feat(Api): added map audit base API. Added comments server validations. 2025-03-15 09:46:18 +01:00
Dmitry Popov
006d10381f chore: updated phoenix_live_view version
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-03-14 17:39:11 +01:00
Dmitry Popov
a1ffe3cc0e fix(Core): updated balance top up instructions 2025-03-14 17:38:31 +01:00
Dmitry Popov
b4a1cbbf55 chore: fix yarn deps 2025-03-14 13:05:31 +01:00
Aleksei Chichenkov
b2ae5a33ae System comments & refactoring (#253)
* feat(Map): Add widget for comments. Refactor design of Signatures widget. Refactor a lot of code. Add Transition component in ui-kit. Sync versions of react.

---------

Co-authored-by: Dmitry Popov <dmitriypopovsamara@gmail.com>
Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
2025-03-14 15:34:12 +04:00
Dmitry Popov
aec0a75e87 fix: updated connections cleanup logic
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-03-13 19:18:29 +01:00
Dmitry Popov
7086413f0c Audit pagination (#250)
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
* feat(Audit): updated audit page pagination
2025-03-13 01:46:57 +04:00
Dmitry Popov
d55f03b63c chore: fill map owner info while registering map license
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-03-12 18:59:23 +01:00
Dmitry Popov
aa4fd2fe90 Lm (#246)
* feat(Core): integration with license management

---------

Co-authored-by: guarzo <guarzo.eve@gmail.com>
2025-03-12 16:30:07 +04:00
Dmitry Popov
6abf628a38 Additional character activity cleanup (#241)
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
* fix: character activity names displayed properly
2025-03-12 10:09:32 +01:00
guarzo
ad46002c85 Additional character activity cleanup (#241)
* fix: character activity names displayed properly
2025-03-12 12:55:57 +04:00
guarzo
2f21bd0f44 fix: removed placeholder favicon (#240) 2025-03-12 11:53:59 +04:00
Dmitry Popov
993608f911 fix: fixed activity aggregation and new user tracking (#230)
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-03-11 19:51:45 +01:00
Dmitry Popov
c6c6adb7d8 fix: fixed activity aggregation and new user tracking (#230) 2025-03-11 18:43:19 +01:00
Dmitry Popov
3937330ce4 fix: fixed activity aggregation and new user tracking (#230)
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-03-11 17:47:30 +01:00
guarzo
1590c848c9 fix: fixed activity aggregation and new user tracking (#230) 2025-03-11 19:20:10 +04:00
Dmitry Popov
2bb45b312c chore: release version v1.54.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-03-10 09:10:45 +01:00
Dmitry Popov
1fc95c96eb feat: enhance character activty and summmarize by user (#206)
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-03-10 00:24:16 +01:00
guarzo
ee7a453a72 feat: enhance character activty and summmarize by user (#206) 2025-03-09 23:45:28 +04:00
CI
4b79afbac0 chore: release version v1.54.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-03-06 22:18:13 +00:00
guarzo
c8fc31257b Add api specs (#217) 2025-03-07 00:31:31 +04:00
guarzo
8e0b8fd7f9 fix [kills]: prevent virtual scroller from showing unless needed (#225) 2025-03-07 00:25:29 +04:00
guarzo
ee8f9e4d24 fix: fix scroll and size issues with kills widget (#219)
* fix: fix scroll and size issues with kills widget
2025-03-06 20:15:22 +04:00
CI
994e03945d chore: release version v1.54.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-03-05 17:50:22 +00:00
Dmitry Popov
aff00a18b5 feat: added auto-refresh timeout for cloud new version updates 2025-03-05 18:32:48 +01:00
guarzo
6c22e6554d feat: add selectable sig deletion timing, and color options (#208)
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-03-04 23:51:10 +04:00
CI
2a0d7654e7 chore: release version v1.53.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-03-04 08:26:26 +00:00
guarzo
4eb1f641ae fix: add retry on kills retrieval (#207) 2025-03-04 11:13:59 +04:00
alpha02x
2da5a243ec fix: add missing masses to wh sizes const (#215) 2025-03-04 11:10:00 +04:00
guarzo
5ac8ccbe5c refactor: split up node hooks (#173)
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-03-01 13:45:34 +04:00
CI
0568533550 chore: release version v1.53.3
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-27 16:55:52 +00:00
achichenkov
178abc2af2 fix(Map): little bit up performance for windows manager 2025-02-27 17:17:12 +03:00
CI
adb2a5f459 chore: release version v1.53.2
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-27 10:36:38 +00:00
Dmitry Popov
ada1571e1e Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-02-27 10:15:23 +01:00
Dmitry Popov
5931c00ff3 chore: release version v1.53.0 2025-02-27 10:15:20 +01:00
CI
a6e7c1bf74 chore: release version v1.53.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-26 22:40:17 +00:00
Dmitry Popov
1a5374f2f6 chore: release version v1.53.0 2025-02-26 23:30:25 +01:00
Dmitry Popov
c9e3683b8e Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-02-26 23:30:13 +01:00
Dmitry Popov
aba93b342a fix(Core): Fixed map ACLs add/remove behaviour 2025-02-26 23:29:23 +01:00
CI
dee78b77a9 chore: release version v1.53.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-26 09:58:30 +00:00
alpha02x
d21705f355 feat: Auto-set connection EOL status and ship size when linking/editing signatures (#194)
* feat: Automatically set connection EOL status and ship size type when linking/updating signatures
2025-02-26 13:33:16 +04:00
CI
9abcd4bd0b chore: release version v1.52.8 2025-02-26 08:34:52 +00:00
Dmitry Popov
b052943e34 fix(Map): Added delete systems hotkey 2025-02-26 09:22:57 +01:00
CI
e1e9b4c2e8 chore: release version v1.52.7
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-24 09:14:05 +00:00
guarzo
42c30e0741 fix: update news image link (#204) 2025-02-24 12:38:15 +04:00
Dmitry Popov
3b45e77e65 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-02-24 09:35:27 +01:00
Dmitry Popov
dcb2b6b912 fix(Map): Block map events for old client versions 2025-02-24 09:35:24 +01:00
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
445 changed files with 53002 additions and 8226 deletions

View File

@@ -1,6 +1,27 @@
FROM elixir:1.17-otp-27
RUN apt install -yq curl gnupg
# Install OS packages and Node.js (via nodesource),
# plus inotify-tools and yarn
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
curl \
make \
git \
bash \
build-essential \
ca-certificates \
jq \
vim \
net-tools \
procps \
# Optionally add any other tools you need, e.g. vim, wget...
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs inotify-tools \
&& npm install -g yarn \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN apt --fix-broken install
RUN mix local.hex --force

View File

@@ -1,15 +1,30 @@
{
"name": "wanderer-dev",
"dockerComposeFile": ["./docker-compose.yml"],
"extensions": ["jakebecker.elixir-ls"],
"customizations": {
"vscode": {
"extensions": [
"jakebecker.elixir-ls",
"JakeBecker.elixir-ls",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"settings": {
"editor.formatOnSave": true,
"search.exclude": {
"**/doc": true
},
"elixirLS.dialyzerEnabled": false
}
}
},
"service": "wanderer",
"workspaceFolder": "/app",
"shutdownAction": "stopCompose",
"settings": {
"editor.formatOnSave": true,
"search.exclude": {
"**/doc": true
},
"elixirLS.dialyzerEnabled": false
}
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"networkArgs": ["--add-host=host.docker.internal:host-gateway"]
}
},
"forwardPorts": [4444]
}

View File

@@ -14,15 +14,15 @@ services:
wanderer:
environment:
PORT: 8000
PORT: 4444
DB_HOST: db
WEB_APP_URL: "http://localhost:8000"
WEB_APP_URL: "http://localhost:4444"
ERL_AFLAGS: "-kernel shell_history enabled"
build:
context: .
dockerfile: Dockerfile
ports:
- 8000:8000
- 4444:4444
volumes:
- ..:/app:delegated
- ~/.gitconfig:/root/.gitconfig

View File

@@ -7,4 +7,5 @@ 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
@@ -20,27 +18,6 @@ permissions:
contents: write
jobs:
deploy-test:
name: 🚀 Deploy to test env (fly.io)
runs-on: ubuntu-latest
if: ${{ github.base_ref == 'main' || (github.ref == 'refs/heads/main' && github.event_name == 'push') }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
- uses: superfly/flyctl-actions/setup-flyctl@master
- name: 👀 Read app name
uses: SebRollen/toml-action@v1.0.0
id: app_name
with:
file: "fly.toml"
field: "app"
- name: 🚀 Deploy Test
run: flyctl deploy --remote-only --wait-timeout=300 --ha=false
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
build:
name: 🛠 Build
runs-on: ubuntu-22.04
@@ -139,8 +116,6 @@ jobs:
matrix:
platform:
- linux/amd64
- linux/arm64
- linux/arm64/v8
steps:
- name: Prepare
run: |

1
.gitignore vendored
View File

@@ -26,6 +26,7 @@
.env
*.local.env
test/manual/.auto*
.direnv/
.cache/

View File

@@ -2,6 +2,816 @@
<!-- changelog -->
## [v1.65.7](https://github.com/wanderer-industries/wanderer/compare/v1.65.6...v1.65.7) (2025-05-26)
### Bug Fixes:
* Core: Fixed map character tracking issues
## [v1.65.6](https://github.com/wanderer-industries/wanderer/compare/v1.65.5...v1.65.6) (2025-05-26)
### Bug Fixes:
* Core: Fixed map character tracking issues
## [v1.65.5](https://github.com/wanderer-industries/wanderer/compare/v1.65.4...v1.65.5) (2025-05-26)
### Bug Fixes:
* Core: Fixed map character tracking issues
* Signature: Update restored signature character
## [v1.65.4](https://github.com/wanderer-industries/wanderer/compare/v1.65.3...v1.65.4) (2025-05-24)
### Bug Fixes:
* Signature: Force signature update even if there are no any changes
## [v1.65.3](https://github.com/wanderer-industries/wanderer/compare/v1.65.2...v1.65.3) (2025-05-23)
### Bug Fixes:
* Signature: Fixed signature clenup
## [v1.65.2](https://github.com/wanderer-industries/wanderer/compare/v1.65.1...v1.65.2) (2025-05-23)
### Bug Fixes:
* Signature: Fixed signature updates
## [v1.65.1](https://github.com/wanderer-industries/wanderer/compare/v1.65.0...v1.65.1) (2025-05-22)
### Bug Fixes:
* Core: Added unsync map events timeout handling (force page refresh if outdated map events found)
## [v1.65.0](https://github.com/wanderer-industries/wanderer/compare/v1.64.8...v1.65.0) (2025-05-22)
### Features:
* default connections from c1 holes to medium size
* support german and french signatures
* improve signature undo process
### Bug Fixes:
* remove required id field from character schema
* update openapi spec response types
* fix issue with connection generation between k-space
* Signature: Fixed signatures updates
* update openapi spec for other apis
## [v1.64.8](https://github.com/wanderer-industries/wanderer/compare/v1.64.7...v1.64.8) (2025-05-20)
### Bug Fixes:
* Core: Added unsync map events timeout handling (force page refresh if outdated map events found)
## [v1.64.7](https://github.com/wanderer-industries/wanderer/compare/v1.64.6...v1.64.7) (2025-05-15)
### Bug Fixes:
* Core: Fixed connection EOL time refreshed every 2 minutes
## [v1.64.6](https://github.com/wanderer-industries/wanderer/compare/v1.64.5...v1.64.6) (2025-05-15)
### Bug Fixes:
* Core: Added map hubs limits checking & a proper warning message shown
## [v1.64.5](https://github.com/wanderer-industries/wanderer/compare/v1.64.4...v1.64.5) (2025-05-14)
### Bug Fixes:
* Core: Added character name update on re-auth
## [v1.64.4](https://github.com/wanderer-industries/wanderer/compare/v1.64.3...v1.64.4) (2025-05-14)
### Bug Fixes:
* Core: Added 1 min timeout for ship and location updates on ESI API errors
## [v1.64.3](https://github.com/wanderer-industries/wanderer/compare/v1.64.2...v1.64.3) (2025-05-14)
### Bug Fixes:
* Core: Fixed character tracking initialization logic & removed search caching
## [v1.64.2](https://github.com/wanderer-industries/wanderer/compare/v1.64.1...v1.64.2) (2025-05-13)
### Bug Fixes:
* Core: Fixed tracking of ship & location for offline characters
## [v1.64.1](https://github.com/wanderer-industries/wanderer/compare/v1.64.0...v1.64.1) (2025-05-13)
### Bug Fixes:
* Core: Fixed tracking stopped due to server errors
## [v1.64.0](https://github.com/wanderer-industries/wanderer/compare/v1.63.0...v1.64.0) (2025-05-13)
### Features:
* api: add additional structure/signature methods (#365)
* api: add additional system/connections methods (#351)
### Bug Fixes:
* Core: Fixed EOL connections cleanup
* Core: Avoid Zarzakh system in routes widget
* remove repeat errors for token refresh (#375)
* updated openapi spec for character activity (#374)
* removed error from characters endpoint, and updated routes (#372)
* cleanup examples for system and connections (#370)
* remove error on websocket reconnect (#367)
## [v1.63.0](https://github.com/wanderer-industries/wanderer/compare/v1.62.4...v1.63.0) (2025-05-11)
### Features:
* Core: Updated map active characters page
## [v1.62.4](https://github.com/wanderer-industries/wanderer/compare/v1.62.3...v1.62.4) (2025-05-10)
### Bug Fixes:
* Core: Fixed map characters got untracked
## [v1.62.3](https://github.com/wanderer-industries/wanderer/compare/v1.62.2...v1.62.3) (2025-05-08)
### Bug Fixes:
* Core: Fixed map characters got untracked
## [v1.62.2](https://github.com/wanderer-industries/wanderer/compare/v1.62.1...v1.62.2) (2025-05-05)
### Bug Fixes:
* Core: Fixed audit export API
## [v1.62.1](https://github.com/wanderer-industries/wanderer/compare/v1.62.0...v1.62.1) (2025-05-05)
## [v1.62.0](https://github.com/wanderer-industries/wanderer/compare/v1.61.2...v1.62.0) (2025-05-05)
### Features:
* Core: added user routes support
### Bug Fixes:
* Map: Fixed link signature modal crash afrer destination system removed
* Map: Change design for tags (#358)
* Map: Removed paywall restriction from public routes
* Core: Fixed issues with structures loading
* Map: Removed unnecessary logs
* Map: Add support user routes
* Map: Add support for User Routes on FE side.
* Map: Refactor Local - show ship name, change placement of ship name. Refactor On the Map - show corp and ally logo. Fixed problem with ellipsis at long character and ship names.
* Map: Refactored routes widget. Add loader for routes. Prepared for custom hubs
* Map: Refactor init and update of mapper
## [v1.61.2](https://github.com/wanderer-industries/wanderer/compare/v1.61.1...v1.61.2) (2025-04-29)
### Bug Fixes:
* Core: Fixed main character checking & manual systems delete logic
## [v1.61.1](https://github.com/wanderer-industries/wanderer/compare/v1.61.0...v1.61.1) (2025-04-26)
### Bug Fixes:
* Core: Fixed additional price calc for map sub updates
## [v1.61.0](https://github.com/wanderer-industries/wanderer/compare/v1.60.1...v1.61.0) (2025-04-24)
### Features:
* Core: force checking main character set for all map activity
## [v1.60.1](https://github.com/wanderer-industries/wanderer/compare/v1.60.0...v1.60.1) (2025-04-22)
### Bug Fixes:
* Map: Removed unnecessary code onFE part
* Map: Removed unnecessary debugger
* Map: Changed name for drifters systems. Fixed static info for Barbican.
## [v1.60.0](https://github.com/wanderer-industries/wanderer/compare/v1.59.11...v1.60.0) (2025-04-17)
### Features:
* api: api showing character by user and main character (#334)
* Core: force map page reload after 30 mins of user inactivity (switched browser/tab)
* update character activity to use main character (#333)
## [v1.59.11](https://github.com/wanderer-industries/wanderer/compare/v1.59.10...v1.59.11) (2025-04-16)
### Bug Fixes:
* Map: Fixed lifetime for A009 from 16h to 4.5h. Fixed problem with no appearing icon of shattered for Drifter wormholes. Fixed wanderings for Drifter wormholes. For system J011355 added static K346. For system J011824 added static K346. (#329)
## [v1.59.10](https://github.com/wanderer-industries/wanderer/compare/v1.59.9...v1.59.10) (2025-04-15)
## [v1.59.9](https://github.com/wanderer-industries/wanderer/compare/v1.59.8...v1.59.9) (2025-04-15)
### Bug Fixes:
* Core: Fixed issues with map server manager
## [v1.59.8](https://github.com/wanderer-industries/wanderer/compare/v1.59.7...v1.59.8) (2025-04-15)
### Bug Fixes:
* Core: Fixed issues with main character & tracking
## [v1.59.7](https://github.com/wanderer-industries/wanderer/compare/v1.59.6...v1.59.7) (2025-04-14)
### Bug Fixes:
* Core: Fixed auto-select splashed systems
## [v1.59.6](https://github.com/wanderer-industries/wanderer/compare/v1.59.5...v1.59.6) (2025-04-13)
### Bug Fixes:
* Map: Fix icons of main, follow and shattered (#321)
## [v1.59.5](https://github.com/wanderer-industries/wanderer/compare/v1.59.4...v1.59.5) (2025-04-12)
### Bug Fixes:
* Signatures: avoid signatures delete on wrong buffer
## [v1.59.4](https://github.com/wanderer-industries/wanderer/compare/v1.59.3...v1.59.4) (2025-04-10)
## [v1.59.3](https://github.com/wanderer-industries/wanderer/compare/v1.59.2...v1.59.3) (2025-04-10)
## [v1.59.2](https://github.com/wanderer-industries/wanderer/compare/v1.59.1...v1.59.2) (2025-04-10)
### Bug Fixes:
* Core: fixed connection validation
## [v1.59.1](https://github.com/wanderer-industries/wanderer/compare/v1.59.0...v1.59.1) (2025-03-26)
### Bug Fixes:
* doc: improve bot setup instructions (#309)
## [v1.59.0](https://github.com/wanderer-industries/wanderer/compare/v1.58.0...v1.59.0) (2025-03-23)
### Features:
* Core: added handling cases when wrong connections created
## [v1.58.0](https://github.com/wanderer-industries/wanderer/compare/v1.57.1...v1.58.0) (2025-03-22)
### Features:
* Core: Show online state on map characters page
* api: update character activity and api to allow date range (#299)
* api: update character activity and api to allow date range
## [v1.57.1](https://github.com/wanderer-industries/wanderer/compare/v1.57.0...v1.57.1) (2025-03-20)
## [v1.57.0](https://github.com/wanderer-industries/wanderer/compare/v1.56.6...v1.57.0) (2025-03-19)
### Features:
* doc: update bot news (#294)
## [v1.56.6](https://github.com/wanderer-industries/wanderer/compare/v1.56.5...v1.56.6) (2025-03-19)
## [v1.56.5](https://github.com/wanderer-industries/wanderer/compare/v1.56.4...v1.56.5) (2025-03-19)
## [v1.56.4](https://github.com/wanderer-industries/wanderer/compare/v1.56.3...v1.56.4) (2025-03-19)
## [v1.56.3](https://github.com/wanderer-industries/wanderer/compare/v1.56.2...v1.56.3) (2025-03-19)
### Bug Fixes:
* cloak key error behavior (#288)
## [v1.56.2](https://github.com/wanderer-industries/wanderer/compare/v1.56.1...v1.56.2) (2025-03-18)
### Bug Fixes:
* show signature tooltip on top
## [v1.56.1](https://github.com/wanderer-industries/wanderer/compare/v1.56.0...v1.56.1) (2025-03-18)
### Bug Fixes:
* update activity api (#284)
* qol updates for dev (#283)
## [v1.56.0](https://github.com/wanderer-industries/wanderer/compare/v1.55.2...v1.56.0) (2025-03-17)
### Features:
* add static wh info (#262)
* add static wh info
* api: add character activity api (#263)
* api: add character activity api
### Bug Fixes:
* character activity hide error
* character added to map on follow (#272)
## [v1.55.2](https://github.com/wanderer-industries/wanderer/compare/v1.55.1...v1.55.2) (2025-03-16)
### Bug Fixes:
* Core: fixed lazy delete reset state
## [v1.55.1](https://github.com/wanderer-industries/wanderer/compare/v1.55.0...v1.55.1) (2025-03-16)
### Bug Fixes:
* Core: fixed lazy delete timeouts
* Core: fixed lazy delete settings
* keep character api off by default (#258)
## [v1.55.0](https://github.com/wanderer-industries/wanderer/compare/v1.54.1...v1.55.0) (2025-03-15)
### Features:
* News: added map subscription news
* Api: added map audit base API. Added comments server validations.
* enhance character activty and summmarize by user (#206)
* enhance character activty and summmarize by user (#206)
### Bug Fixes:
* Core: updated balance top up instructions
* updated connections cleanup logic
* removed placeholder favicon (#240)
* fixed activity aggregation and new user tracking (#230)
* fixed activity aggregation and new user tracking (#230)
* fixed activity aggregation and new user tracking (#230)
* fixed activity aggregation and new user tracking (#230)
## [v1.54.1](https://github.com/wanderer-industries/wanderer/compare/v1.54.0...v1.54.1) (2025-03-06)
### Bug Fixes:
* fix scroll and size issues with kills widget (#219)
* fix scroll and size issues with kills widget
## [v1.54.0](https://github.com/wanderer-industries/wanderer/compare/v1.53.4...v1.54.0) (2025-03-05)
### Features:
* added auto-refresh timeout for cloud new version updates
* add selectable sig deletion timing, and color options (#208)
## [v1.53.4](https://github.com/wanderer-industries/wanderer/compare/v1.53.3...v1.53.4) (2025-03-04)
### Bug Fixes:
* add retry on kills retrieval (#207)
* add missing masses to wh sizes const (#215)
## [v1.53.3](https://github.com/wanderer-industries/wanderer/compare/v1.53.2...v1.53.3) (2025-02-27)
### Bug Fixes:
* Map: little bit up performance for windows manager
## [v1.53.2](https://github.com/wanderer-industries/wanderer/compare/v1.53.1...v1.53.2) (2025-02-27)
## [v1.53.1](https://github.com/wanderer-industries/wanderer/compare/v1.53.0...v1.53.1) (2025-02-26)
### Bug Fixes:
* Core: Fixed map ACLs add/remove behaviour
## [v1.53.0](https://github.com/wanderer-industries/wanderer/compare/v1.52.8...v1.53.0) (2025-02-26)
### Features:
* Auto-set connection EOL status and ship size when linking/editing signatures (#194)
* Automatically set connection EOL status and ship size type when linking/updating signatures
## [v1.52.8](https://github.com/wanderer-industries/wanderer/compare/v1.52.7...v1.52.8) (2025-02-26)
### Bug Fixes:
* Map: Added delete systems hotkey
## [v1.52.7](https://github.com/wanderer-industries/wanderer/compare/v1.52.6...v1.52.7) (2025-02-24)
### Bug Fixes:
* update news image link (#204)
* Map: Block map events for old client versions
## [v1.52.6](https://github.com/wanderer-industries/wanderer/compare/v1.52.5...v1.52.6) (2025-02-23)
### Bug Fixes:
* Map: Fixed delete systems on map changes
## [v1.52.5](https://github.com/wanderer-industries/wanderer/compare/v1.52.4...v1.52.5) (2025-02-22)
### Bug Fixes:
* Map: Fixed delete system on signature deletion
* Map: Fixed delete system on signature deletion
## [v1.52.4](https://github.com/wanderer-industries/wanderer/compare/v1.52.3...v1.52.4) (2025-02-21)
### Bug Fixes:
* signature paste for russian lang
## [v1.52.3](https://github.com/wanderer-industries/wanderer/compare/v1.52.2...v1.52.3) (2025-02-21)
### Bug Fixes:
* remove signature expiration (#196)
## [v1.52.2](https://github.com/wanderer-industries/wanderer/compare/v1.52.1...v1.52.2) (2025-02-21)
### Bug Fixes:
* prevent constant full signature widget rerender (#195)
## [v1.52.1](https://github.com/wanderer-industries/wanderer/compare/v1.52.0...v1.52.1) (2025-02-20)
### Bug Fixes:
* proper virtual scroller usage (#192)
* restore delete key functionality for nodes (#191)
## [v1.52.0](https://github.com/wanderer-industries/wanderer/compare/v1.51.3...v1.52.0) (2025-02-19)
### Features:
* Map: Added map characters view
## [v1.51.3](https://github.com/wanderer-industries/wanderer/compare/v1.51.2...v1.51.3) (2025-02-19)
### Bug Fixes:
* pending deletion working again (#185)
## [v1.51.2](https://github.com/wanderer-industries/wanderer/compare/v1.51.1...v1.51.2) (2025-02-18)
## [v1.51.1](https://github.com/wanderer-industries/wanderer/compare/v1.51.0...v1.51.1) (2025-02-18)
## [v1.51.0](https://github.com/wanderer-industries/wanderer/compare/v1.50.0...v1.51.0) (2025-02-17)
### Features:
* add undo deletion for signatures (#155)
* add undo for signature deletion and addition
## [v1.50.0](https://github.com/wanderer-industries/wanderer/compare/v1.49.0...v1.50.0) (2025-02-17)
### Features:
* allow addition of characters to acl without preregistration (#176)
## [v1.49.0](https://github.com/wanderer-industries/wanderer/compare/v1.48.1...v1.49.0) (2025-02-15)
### Features:
* add api for acl management (#171)
## [v1.48.1](https://github.com/wanderer-industries/wanderer/compare/v1.48.0...v1.48.1) (2025-02-13)
## [v1.48.0](https://github.com/wanderer-industries/wanderer/compare/v1.47.6...v1.48.0) (2025-02-12)
### Features:
* autosize local character tooltip and increase hover target (#165)
## [v1.47.6](https://github.com/wanderer-industries/wanderer/compare/v1.47.5...v1.47.6) (2025-02-12)
## [v1.47.5](https://github.com/wanderer-industries/wanderer/compare/v1.47.4...v1.47.5) (2025-02-12)
### Bug Fixes:
* sync kills count bookmark and the kills widget (#160)
* lazy load kills widget
## [v1.47.4](https://github.com/wanderer-industries/wanderer/compare/v1.47.3...v1.47.4) (2025-02-11)
## [v1.47.3](https://github.com/wanderer-industries/wanderer/compare/v1.47.2...v1.47.3) (2025-02-11)
## [v1.47.2](https://github.com/wanderer-industries/wanderer/compare/v1.47.1...v1.47.2) (2025-02-11)
### Bug Fixes:
* lazy load kills widget (#157)
* lazy load kills widget
* updates for eslint and pr feedback
## [v1.47.1](https://github.com/wanderer-industries/wanderer/compare/v1.47.0...v1.47.1) (2025-02-09)

View File

@@ -1,4 +1,4 @@
.PHONY: deploy install cleanup start yarn migrate format test coverage versions
.PHONY: deploy install cleanup start yarn migrate format test coverage versions standalone-tests
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
SHELL := /bin/bash
@@ -35,6 +35,11 @@ test t:
coverage cover co:
mix test --cover
unit-tests ut:
@echo "Running unit tests..."
@find test/unit -name "*.exs" -exec elixir {} \;
@echo "All unit tests completed."
versions v:
@echo "Tool Versions"
@cat .tool-versions

View File

@@ -112,19 +112,19 @@ body > div:first-of-type {
}
.wd-characters-icons {
display: flex;
transition:
border-color 250ms,
opacity 250ms;
width: 35px;
height: 35px;
border-radius: 50%;
border-width: 2px;
border-style: solid;
border-color: #5a5a5a;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
opacity: 0.6;
/*display: flex;*/
/*transition:*/
/* border-color 250ms,*/
/* opacity 250ms;*/
/*width: 35px;*/
/*height: 35px;*/
/*border-radius: 50%;*/
/*border-width: 2px;*/
/*border-style: solid;*/
/*border-color: #5a5a5a;*/
/*background-color: rgba(0, 0, 0, 0);*/
/*cursor: pointer;*/
/*opacity: 0.6;*/
}
.wd-bg-default {
@@ -936,3 +936,66 @@ body > div:first-of-type {
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

@@ -1,14 +1,14 @@
import { ErrorBoundary } from 'react-error-boundary';
import { PrimeReactProvider } from 'primereact/api';
import { ErrorBoundary } from 'react-error-boundary';
import { ReactFlowProvider } from 'reactflow';
import { MapHandlers } from '@/hooks/Mapper/types/mapHandlers.ts';
import { ErrorInfo, useCallback, useEffect, useRef } from 'react';
import { ReactFlowProvider } from 'reactflow';
import { useMapperHandlers } from './useMapperHandlers';
import './common-styles/main.scss';
import { MapRootProvider } from '@/hooks/Mapper/mapRootProvider';
import { MapRootContent } from '@/hooks/Mapper/components/mapRootContent/MapRootContent.tsx';
import { MapRootProvider } from '@/hooks/Mapper/mapRootProvider';
import './common-styles/main.scss';
const ErrorFallback = () => {
return <div className="!z-100 absolute w-screen h-screen bg-transparent"></div>;
@@ -20,7 +20,7 @@ export default function MapRoot({ hooks }) {
const mapperHandlerRefs = useRef([providerRef]);
const { handleCommand, handleMapEvent, handleMapEvents } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
const { handleCommand, handleMapEvent } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
const logError = useCallback((error: Error, info: ErrorInfo) => {
if (!hooksRef.current) {
@@ -35,7 +35,6 @@ export default function MapRoot({ hooks }) {
}
hooksRef.current.handleEvent('map_event', handleMapEvent);
hooksRef.current.handleEvent('map_events', handleMapEvents);
}, []);
return (

View File

@@ -67,16 +67,22 @@
}
}
.p-sortable-column {
font-size: 12px;
font-weight: bold;
padding: 3px 4px;
.p-datatable-thead {
th, th.p-sortable-column {
font-size: 12px;
font-weight: bold;
padding: 3px 4px;
}
}
.p-selectable-row td {
padding: 4px 4px;
}
.p-datatable.p-datatable-sm .p-datatable-tbody > tr > td {
padding: 3px 4px;
}
.p-sortable-column > .p-column-header-content > span:last-child {
transform: scale(0.7);
@@ -93,6 +99,11 @@
.p-dropdown-item {
padding: 0.25rem 0.5rem;
font-size: 14px;
width: 100%;
.p-dropdown-item-label {
width: 100%;
}
}
.p-dropdown-item-group {
@@ -137,3 +148,53 @@
background: #966d3d;
}
}
.p-datatable-wrapper {
height: 100%;
& {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.5) transparent;
}
&::-webkit-scrollbar {
width: 10px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.5);
border-radius: 5px;
border: 2px solid transparent;
background-clip: content-box;
}
&::-webkit-scrollbar-thumb:hover {
background-color: rgba(255, 255, 255, 0.7);
}
&::-webkit-scrollbar-button {
display: none;
height: 0;
width: 0;
}
}
.p-datatable .p-datatable-tbody > tr.p-highlight {
background: initial;
}
.suppress-menu-behaviour {
pointer-events: none;
.p-menuitem-content {
pointer-events: initial;
background-color: initial !important;
}
.p-menuitem-content:hover {
background-color: initial !important;
}
}

View File

@@ -1,11 +1,8 @@
/* Основной класс диалога */
body .p-dialog {
display: flex;
flex-direction: column;
//position: absolute;
top: 0;
left: 0;
//visibility: hidden;
overflow: hidden;
border-radius: 2px;
box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2);
@@ -29,12 +26,10 @@ body .p-dialog {
}
}
/* Стиль видимого диалога */
.p-dialog-visible {
visibility: visible;
}
/* Анимации */
.p-dialog-enter {
opacity: 0;
}
@@ -53,31 +48,27 @@ body .p-dialog {
transition: opacity 0.3s;
}
/* Заголовок диалога */
.p-dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
background: #f4f4f4;
//border-bottom: 1px solid #ddd;
height: 40px;
}
/* Содержимое диалога */
.p-dialog-content {
padding: 0.5rem;
overflow-y: auto;
flex: 1;
}
/* Подвал диалога */
.p-dialog-footer {
padding: 1rem;
border-top: 1px solid #ddd;
background: #f4f4f4;
}
/* Кнопка закрытия диалога */
.p-dialog-header-close {
display: flex;
align-items: center;
@@ -93,3 +84,12 @@ body .p-dialog {
.p-dialog-header-close .pi {
font-size: 1.25rem;
}
.p-dialog {
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -0,0 +1,45 @@
.p-confirm-popup {
display: flex;
flex-direction: column;
gap: 6px;
@apply p-[12px];
&::before, &::after {
display: none;
}
.p-confirm-popup-content, .p-confirm-popup-footer {
@apply p-0 m-0;
}
.p-confirm-popup-content {
display: flex;
gap: 6px;
}
.p-confirm-popup-footer {
display: flex;
justify-content: flex-end;
gap: 4px;
}
.p-confirm-popup-icon {
font-size: 14px;
}
.p-confirm-popup-message {
@apply m-0;
font-size: 12px;
}
.p-confirm-popup-reject.p-button-sm,
.p-confirm-popup-accept.p-button-sm {
@apply px-1.5 py-1 m-0;
& > span {
font-size: 12px;
line-height: 12px;
}
}
}

View File

@@ -0,0 +1,77 @@
.vertical-tabs-container {
display: flex;
width: 100%;
min-height: 300px;
.p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.p-tabview-panels {
padding: 6px 1rem;
flex-grow: 1;
height: 100%;
}
.p-tabview-nav-container {
border-right: none;
height: 100%;
}
.p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition: background-color 200ms, border-right-color 200ms;
&:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.p-tabview-nav-link {
transition: color 200ms;
justify-content: flex-end;
padding: 10px;
//background-color: var(--surface-card);
background-color: initial;
border: none;
color: var(--gray-400);
border-radius: initial;
font-weight: 400;
margin: 0;
}
&.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
.p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
&:hover {
//background-color: var(--surface-hover);
border-right: 4px solid var(--primary-color);
}
}
}
}
.p-tabview-panel {
flex-grow: 1;
}
}

View File

@@ -1,4 +1,6 @@
@import "fix-dialog";
@import "fix-popup";
@import "fix-tabs";
//@import "fix-input";
//@import "theme";

View File

@@ -0,0 +1,18 @@
.Docked {
content: " ";
display: inline-block;
width: 11px;
height: 11px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
position: absolute;
z-index: 1;
overflow: hidden;
border-radius: 1px;
background-image: url(/images/citadelLarge.png);
left: 2px;
top: 22px;
transform: rotateZ(0deg);
}

View File

@@ -4,10 +4,22 @@ import { useAutoAnimate } from '@formkit/auto-animate/react';
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import classes from './Characters.module.scss';
import { isDocked } from '@/hooks/Mapper/helpers/isDocked.ts';
import { PrimeIcons } from 'primereact/api';
const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
interface CharactersProps {
data: CharacterTypeRaw[];
}
export const Characters = ({ data }: CharactersProps) => {
const [parent] = useAutoAnimate();
const {
data: { mainCharacterEveId, followingCharacterEveId },
} = useMapRootState();
const handleSelect = useCallback((character: CharacterTypeRaw) => {
emitMapEvent({
name: Commands.centerSystem,
@@ -21,21 +33,58 @@ const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
className="flex flex-col items-center justify-center"
onClick={() => handleSelect(character)}
>
<div className="tooltip tooltip-bottom" title={character.name}>
<a
className={clsx('wd-characters-icons wd-bg-default', { ['character-online']: character.online })}
<div
className={clsx(
'overflow-hidden relative',
'flex w-[35px] h-[35px] rounded-[4px] border-[1px] border-solid bg-transparent cursor-pointer',
'transition-colors duration-250',
{
['border-stone-800/90']: !character.online,
['border-lime-600/70']: character.online,
},
)}
title={character.name}
>
{mainCharacterEveId === character.eve_id && (
<span
className={clsx(
'absolute top-[2px] left-[22px] w-[9px] h-[9px]',
'text-yellow-500 text-[9px] rounded-[1px] z-10',
'pi',
PrimeIcons.STAR_FILL,
)}
/>
)}
{followingCharacterEveId === character.eve_id && (
<span
className={clsx(
'absolute top-[23px] left-[22px] w-[10px] h-[10px]',
'text-sky-300 text-[10px] rounded-[1px] z-10',
'pi pi-angle-double-right',
)}
/>
)}
{isDocked(character.location) && <div className={classes.Docked} />}
<div
className={clsx(
'flex w-full h-full bg-transparent cursor-pointer',
'bg-center bg-no-repeat bg-[length:100%]',
'transition-opacity',
'shadow-[inset_0_1px_6px_1px_#000000]',
{
['opacity-60']: !character.online,
['opacity-100']: character.online,
},
)}
style={{ backgroundImage: `url(https://images.evetech.net/characters/${character.eve_id}/portrait)` }}
></a>
></div>
</div>
</li>
));
return (
<ul className="flex characters" id="characters" ref={parent}>
<ul className="flex gap-1 characters" id="characters" ref={parent}>
{items}
</ul>
);
};
// eslint-disable-next-line react/display-name
export default Characters;

View File

@@ -6,6 +6,7 @@ import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/ty
export interface ContextMenuSystemProps {
hubs: string[];
userHubs: string[];
contextMenuRef: RefObject<ContextMenu>;
systemId: string | undefined;
systems: SolarSystemRawType[];
@@ -13,6 +14,7 @@ export interface ContextMenuSystemProps {
onLockToggle(): void;
onOpenSettings(): void;
onHubToggle(): void;
onUserHubToggle(): void;
onSystemTag(val?: string): void;
onSystemStatus(val: number): void;
onSystemLabels(val: string): void;
@@ -25,7 +27,7 @@ export const ContextMenuSystem: React.FC<ContextMenuSystemProps> = ({ contextMen
return (
<>
<ContextMenu model={items} ref={contextMenuRef} breakpoint="767px" />
<ContextMenu className="min-w-[200px]" model={items} ref={contextMenuRef} breakpoint="767px" />
</>
);
};

View File

@@ -1 +1 @@
export * from './useTagMenu.ts';
export * from './useTagMenu.tsx';

View File

@@ -1,68 +0,0 @@
import { MenuItem } from 'primereact/menuitem';
import { PrimeIcons } from 'primereact/api';
import { useCallback, useRef } from 'react';
import { SolarSystemRawType } from '@/hooks/Mapper/types';
import { getSystemById } from '@/hooks/Mapper/helpers';
import clsx from 'clsx';
import { GRADIENT_MENU_ACTIVE_CLASSES } from '@/hooks/Mapper/constants.ts';
const AVAILABLE_LETTERS = ['A', 'B', 'C', 'D', 'E', 'F', 'X', 'Y', 'Z'];
const AVAILABLE_NUMBERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
export const useTagMenu = (
systems: SolarSystemRawType[],
systemId: string | undefined,
onSystemTag: (val?: string) => void,
): (() => MenuItem) => {
const ref = useRef({ onSystemTag, systems, systemId });
ref.current = { onSystemTag, systems, systemId };
return useCallback(() => {
const { onSystemTag, systemId, systems } = ref.current;
const system = systemId ? getSystemById(systems, systemId) : undefined;
const isSelectedLetters = AVAILABLE_LETTERS.includes(system?.tag ?? '');
const isSelectedNumbers = AVAILABLE_NUMBERS.includes(system?.tag ?? '');
const menuItem: MenuItem = {
label: 'Tag',
icon: PrimeIcons.HASHTAG,
className: clsx({ [GRADIENT_MENU_ACTIVE_CLASSES]: isSelectedLetters || isSelectedNumbers }),
items: [
...(system?.tag !== '' && system?.tag !== null
? [
{
label: 'Clear',
icon: PrimeIcons.BAN,
command: () => onSystemTag(),
},
]
: []),
{
label: 'Letter',
icon: PrimeIcons.TAGS,
className: clsx({ [GRADIENT_MENU_ACTIVE_CLASSES]: isSelectedLetters }),
items: AVAILABLE_LETTERS.map(x => ({
label: x,
icon: PrimeIcons.TAG,
command: () => onSystemTag(x),
className: clsx({ [GRADIENT_MENU_ACTIVE_CLASSES]: system?.tag === x }),
})),
},
{
label: 'Digit',
icon: PrimeIcons.TAGS,
className: clsx({ [GRADIENT_MENU_ACTIVE_CLASSES]: isSelectedNumbers }),
items: AVAILABLE_NUMBERS.map(x => ({
label: x,
icon: PrimeIcons.TAG,
command: () => onSystemTag(x),
className: clsx({ [GRADIENT_MENU_ACTIVE_CLASSES]: system?.tag === x }),
})),
},
],
};
return menuItem;
}, []);
};

View File

@@ -0,0 +1,95 @@
import { MenuItem } from 'primereact/menuitem';
import { PrimeIcons } from 'primereact/api';
import { useCallback, useRef } from 'react';
import { SolarSystemRawType } from '@/hooks/Mapper/types';
import { getSystemById } from '@/hooks/Mapper/helpers';
import clsx from 'clsx';
import { GRADIENT_MENU_ACTIVE_CLASSES } from '@/hooks/Mapper/constants.ts';
import { LayoutEventBlocker } from '@/hooks/Mapper/components/ui-kit';
import { Button } from 'primereact/button';
const AVAILABLE_TAGS = [
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'X',
'Y',
'Z',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
];
export const useTagMenu = (
systems: SolarSystemRawType[],
systemId: string | undefined,
onSystemTag: (val?: string) => void,
): (() => MenuItem) => {
const ref = useRef({ onSystemTag, systems, systemId });
ref.current = { onSystemTag, systems, systemId };
return useCallback(() => {
const { onSystemTag, systemId, systems } = ref.current;
const system = systemId ? getSystemById(systems, systemId) : undefined;
const isSelectedTag = AVAILABLE_TAGS.includes(system?.tag ?? '');
const menuItem: MenuItem = {
label: 'Tag',
icon: PrimeIcons.HASHTAG,
className: clsx({ [GRADIENT_MENU_ACTIVE_CLASSES]: isSelectedTag }),
items: [
{
label: 'Digit',
icon: PrimeIcons.TAGS,
className: '!h-[128px] suppress-menu-behaviour',
template: () => {
return (
<LayoutEventBlocker className="flex flex-col gap-1 w-[200px] h-full px-2">
<div className="grid grid-cols-[auto_auto_auto_auto_auto_auto] gap-1">
{AVAILABLE_TAGS.map(x => (
<Button
outlined={system?.tag !== x}
severity="warning"
key={x}
value={x}
size="small"
className="p-[3px] justify-center"
onClick={() => system?.tag !== x && onSystemTag(x)}
>
{x}
</Button>
))}
<Button
disabled={!isSelectedTag}
icon="pi pi-ban"
size="small"
className="!p-0 !w-[initial] justify-center"
outlined
severity="help"
onClick={() => onSystemTag()}
></Button>
</div>
</LayoutEventBlocker>
);
},
},
],
};
return menuItem;
}, []);
};

View File

@@ -8,19 +8,25 @@ import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
interface UseContextMenuSystemHandlersProps {
hubs: string[];
userHubs: string[];
systems: SolarSystemRawType[];
outCommand: OutCommandHandler;
}
export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseContextMenuSystemHandlersProps) => {
export const useContextMenuSystemHandlers = ({
systems,
hubs,
userHubs,
outCommand,
}: UseContextMenuSystemHandlersProps) => {
const contextMenuRef = useRef<ContextMenu | null>(null);
const [system, setSystem] = useState<string>();
const { deleteSystems } = useDeleteSystems();
const ref = useRef({ hubs, system, systems, outCommand, deleteSystems });
ref.current = { hubs, system, systems, outCommand, deleteSystems };
const ref = useRef({ hubs, userHubs, system, systems, outCommand, deleteSystems });
ref.current = { hubs, userHubs, system, systems, outCommand, deleteSystems };
const open = useCallback((ev: any, systemId: string) => {
setSystem(systemId);
@@ -72,6 +78,21 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
setSystem(undefined);
}, []);
const onUserHubToggle = useCallback(() => {
const { userHubs, system, outCommand } = ref.current;
if (!system) {
return;
}
outCommand({
type: !userHubs.includes(system) ? OutCommand.addUserHub : OutCommand.deleteUserHub,
data: {
system_id: system,
},
});
setSystem(undefined);
}, []);
const onSystemTag = useCallback((tag?: string) => {
const { system, outCommand } = ref.current;
if (!system) {
@@ -104,7 +125,6 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
setSystem(undefined);
}, []);
const onSystemStatus = useCallback((status: number) => {
const { system, outCommand } = ref.current;
if (!system) {
@@ -177,6 +197,7 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
onDeleteSystem,
onLockToggle,
onHubToggle,
onUserHubToggle,
onSystemTag,
onSystemTemporaryName,
onSystemStatus,

View File

@@ -9,11 +9,14 @@ 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';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
import { MapAddIcon, MapDeleteIcon, MapUserAddIcon, MapUserDeleteIcon } from '@/hooks/Mapper/icons';
export const useContextMenuSystemItems = ({
onDeleteSystem,
onLockToggle,
onHubToggle,
onUserHubToggle,
onSystemTag,
onSystemStatus,
onSystemLabels,
@@ -22,6 +25,7 @@ export const useContextMenuSystemItems = ({
onWaypointSet,
systemId,
hubs,
userHubs,
systems,
}: Omit<ContextMenuSystemProps, 'contextMenuRef'>) => {
const getTags = useTagMenu(systems, systemId, onSystemTag);
@@ -32,6 +36,7 @@ export const useContextMenuSystemItems = ({
return useMemo(() => {
const system = systemId ? getSystemById(systems, systemId) : undefined;
const systemStaticInfo = getSystemStaticInfo(systemId)!;
if (!system || !systemId) {
return [];
@@ -44,9 +49,9 @@ export const useContextMenuSystemItems = ({
return (
<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)}
systemName={systemStaticInfo.solar_system_name}
regionName={systemStaticInfo.region_name}
isWH={isWormholeSpace(systemStaticInfo.system_class)}
showEdit
onOpenSettings={onOpenSettings}
/>
@@ -57,12 +62,25 @@ export const useContextMenuSystemItems = ({
getTags(),
getStatus(),
...getLabels(),
...getWaypointMenu(systemId, system.system_static_info.system_class),
...getWaypointMenu(systemId, systemStaticInfo.system_class),
{
label: !hubs.includes(systemId) ? 'Add in Routes' : 'Remove from Routes',
icon: PrimeIcons.MAP_MARKER,
label: !hubs.includes(systemId) ? 'Add Route' : 'Remove Route',
icon: !hubs.includes(systemId) ? (
<MapAddIcon className="mr-1 relative left-[-2px]" />
) : (
<MapDeleteIcon className="mr-1 relative left-[-2px]" />
),
command: onHubToggle,
},
{
label: !userHubs.includes(systemId) ? 'Add User Route' : 'Remove User Route',
icon: !userHubs.includes(systemId) ? (
<MapUserAddIcon className="mr-1 relative left-[-2px]" />
) : (
<MapUserDeleteIcon className="mr-1 relative left-[-2px]" />
),
command: onUserHubToggle,
},
...(system.locked
? canLockSystem
? [

View File

@@ -11,6 +11,7 @@ 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';
import { MapAddIcon, MapDeleteIcon } from '@/hooks/Mapper/icons';
export interface ContextMenuSystemInfoProps {
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
@@ -69,8 +70,12 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
...getJumpPlannerMenu(system, routes),
...getWaypointMenu(systemId, system.system_class),
{
label: !hubs.includes(systemId) ? 'Add in Routes' : 'Remove from Routes',
icon: PrimeIcons.MAP_MARKER,
label: !hubs.includes(systemId) ? 'Add Route' : 'Remove Route',
icon: !hubs.includes(systemId) ? (
<MapAddIcon className="mr-1 relative left-[-2px]" />
) : (
<MapDeleteIcon className="mr-1 relative left-[-2px]" />
),
command: onHubToggle,
},
...(!systemOnMap

View File

@@ -1,25 +1,25 @@
import * as React from 'react';
import { useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { Commands, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useRouteProvider } from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
interface UseContextMenuSystemHandlersProps {
hubs: string[];
outCommand: OutCommandHandler;
}
export const useContextMenuSystemInfoHandlers = () => {
const { outCommand } = useMapRootState();
const { hubs = [], toggleHubCommand } = useRouteProvider();
export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContextMenuSystemHandlersProps) => {
const contextMenuRef = useRef<ContextMenu | null>(null);
const [system, setSystem] = useState<string>();
const routeRef = useRef<(SolarSystemStaticInfoRaw | undefined)[]>([]);
const ref = useRef({ hubs, system, outCommand });
ref.current = { hubs, system, outCommand };
const ref = useRef({ hubs, system, outCommand, toggleHubCommand });
ref.current = { hubs, system, outCommand, toggleHubCommand };
const open = useCallback(
(ev: React.SyntheticEvent, systemId: string, route: (SolarSystemStaticInfoRaw | undefined)[]) => {
@@ -33,17 +33,12 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContex
);
const onHubToggle = useCallback(() => {
const { hubs, system, outCommand } = ref.current;
const { system } = ref.current;
if (!system) {
return;
}
outCommand({
type: !hubs.includes(system) ? OutCommand.addHub : OutCommand.deleteHub,
data: {
system_id: system,
},
});
ref.current.toggleHubCommand(system);
setSystem(undefined);
}, []);
@@ -59,6 +54,8 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContex
system_id: solarSystemId,
},
});
// TODO add it to some queue
setTimeout(() => {
emitMapEvent({
name: Commands.centerSystem,

View File

@@ -1,6 +1,6 @@
import { useCallback, useRef } from 'react';
import { LayoutEventBlocker, WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { ANOIK_ICON, DOTLAN_ICON, ZKB_ICON } from '@/hooks/Mapper/icons.ts';
import { ANOIK_ICON, DOTLAN_ICON, ZKB_ICON } from '@/hooks/Mapper/icons';
import classes from './FastSystemActions.module.scss';
import clsx from 'clsx';

View File

@@ -4,8 +4,8 @@ import { useCallback } from 'react';
import { isPossibleSpace } from '@/hooks/Mapper/components/map/helpers/isKnownSpace.ts';
import { Route } from '@/hooks/Mapper/types/routes.ts';
import { SolarSystemRawType, SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { SOLAR_SYSTEM_CLASS_IDS } from '@/hooks/Mapper/components/map/constants.ts';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
const imperialSpace = [SOLAR_SYSTEM_CLASS_IDS.hs, SOLAR_SYSTEM_CLASS_IDS.ls, SOLAR_SYSTEM_CLASS_IDS.ns];
const criminalSpace = [SOLAR_SYSTEM_CLASS_IDS.ls, SOLAR_SYSTEM_CLASS_IDS.ns];
@@ -47,7 +47,7 @@ export const useJumpPlannerMenu = (
return [];
}
const origin = getSystemById(systems, systemIdFrom)?.system_static_info;
const origin = getSystemStaticInfo(systemIdFrom);
if (!origin) {
return [];

View File

@@ -1,7 +1,7 @@
import { getSystemById } from '@/hooks/Mapper/helpers';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMemo } from 'react';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
import { getSystemStaticInfo } from '../../mapRootProvider/hooks/useLoadSystemStatic';
interface UseSystemInfoProps {
systemId: string;
@@ -12,14 +12,12 @@ export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
data: { systems, connections },
} = useMapRootState();
const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] });
return useMemo(() => {
const staticInfo = systemStatics.get(parseInt(systemId));
const staticInfo = getSystemStaticInfo(parseInt(systemId));
const dynamicInfo = getSystemById(systems, systemId);
if (!staticInfo || !dynamicInfo) {
throw new Error(`Error on getting system ${systemId}`);
return { dynamicInfo, staticInfo, leadsTo: [] };
}
const leadsTo = connections
@@ -29,5 +27,5 @@ export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
.filter(x => x !== systemId);
return { dynamicInfo, staticInfo, leadsTo };
}, [systemStatics, systemId, systems, connections]);
}, [systemId, systems, connections]);
};

View File

@@ -2,7 +2,6 @@ import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo }
import ReactFlow, {
Background,
Edge,
EdgeChange,
MiniMap,
Node,
NodeChange,
@@ -79,11 +78,12 @@ const edgeTypes = {
floating: SolarSystemEdge,
};
export const MAP_ROOT_ID = 'MAP_ROOT_ID';
interface MapCompProps {
refn: ForwardedRef<MapHandlers>;
onCommand: OutCommandHandler;
onSelectionChange: OnMapSelectionChange;
onManualDelete(systems: string[]): void;
onConnectionInfoClick?(e: SolarSystemConnection): void;
onAddSystem?: OnMapAddSystemCallback;
onSelectionContextMenu?: NodeSelectionMouseHandler;
@@ -105,7 +105,6 @@ const MapComp = ({
onSystemContextMenu,
onConnectionInfoClick,
onSelectionContextMenu,
onManualDelete,
isShowMinimap,
showKSpaceBG,
isThickConnections,
@@ -114,7 +113,7 @@ const MapComp = ({
theme,
onAddSystem,
}: MapCompProps) => {
const { getNode, getNodes } = useReactFlow();
const { getNodes } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
@@ -187,8 +186,6 @@ const MapComp = ({
const handleNodesChange = useCallback(
(changes: NodeChange[]) => {
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) {
@@ -196,30 +193,12 @@ const MapComp = ({
}
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];
}, [] as NodeChange[]);
if (systemsIdsToRemove.length > 0) {
onManualDelete(systemsIdsToRemove);
}
onNodesChange(nextChanges);
},
[getNode, getNodes, onManualDelete, onNodesChange],
[getNodes, onNodesChange],
);
useEffect(() => {
@@ -232,7 +211,10 @@ const MapComp = ({
return (
<>
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
<div
data-window-id={MAP_ROOT_ID}
className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}
>
<ReactFlow
nodes={nodes}
edges={edges}
@@ -276,7 +258,7 @@ const MapComp = ({
minZoom={0.2}
maxZoom={1.5}
elevateNodesOnSelect
deleteKeyCode={['Delete']}
deleteKeyCode={['']}
{...(isPanAndDrag
? {
selectionOnDrag: true,

View File

@@ -37,7 +37,9 @@ const INITIAL_DATA: MapData = {
userPermissions: {},
systemSignatures: {} as Record<string, SystemSignature[]>,
options: {} as Record<string, string | boolean>,
is_subscription_active: false,
isSubscriptionActive: false,
mainCharacterEveId: null,
followingCharacterEveId: null,
};
export interface MapContextProps {
@@ -53,7 +55,7 @@ const MapContext = createContext<MapContextProps>({
outCommand: async () => void 0,
});
export const MapProvider: React.FC<MapProviderProps> = ({ children, onCommand }) => {
export const MapProvider = ({ children, onCommand }: MapProviderProps) => {
const { update, ref } = useContextStore<MapData>({ ...INITIAL_DATA });
return (

View File

@@ -2,7 +2,6 @@ import React, { RefObject, useMemo } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { PrimeIcons } from 'primereact/api';
import { MenuItem } from 'primereact/menuitem';
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';
@@ -14,6 +13,7 @@ import {
SHIP_SIZES_NAMES_SHORT,
SHIP_SIZES_SIZE,
} from '@/hooks/Mapper/components/map/constants.ts';
import { Edge } from 'reactflow';
export interface ContextMenuConnectionProps {
contextMenuRef: RefObject<ContextMenu>;

View File

@@ -1,9 +1,8 @@
import { EdgeMouseHandler } from 'reactflow';
import { Edge, EdgeMouseHandler } from 'reactflow';
import { useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { useMapState } from '../../MapProvider.tsx';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { Edge } from '@reactflow/core/dist/esm/types/edges';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';

View File

@@ -1,9 +1,14 @@
import { SystemKillsContent } from '../../../mapInterface/widgets/SystemKills/SystemKillsContent/SystemKillsContent';
import { useMemo } from 'react';
import { useKillsCounter } from '../../hooks/useKillsCounter';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { WithChildren, WithClassName } from '@/hooks/Mapper/types/common.ts';
import { WithChildren, WithClassName } from '@/hooks/Mapper/types/common';
import {
KILLS_ROW_HEIGHT,
SystemKillsList,
} from '@/hooks/Mapper/components/mapInterface/widgets/WSystemKills/SystemKillsList';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
type TooltipSize = 'xs' | 'sm' | 'md' | 'lg';
const MIN_TOOLTIP_HEIGHT = 40;
type KillsBookmarkTooltipProps = {
killsCount: number;
@@ -14,18 +19,45 @@ type KillsBookmarkTooltipProps = {
} & WithChildren &
WithClassName;
export const KillsCounter = ({ killsCount, systemId, className, children, size = 'xs' }: KillsBookmarkTooltipProps) => {
const { isLoading, kills: detailedKills, systemNameMap } = useKillsCounter({ realSystemId: systemId });
export const KillsCounter = ({
killsCount,
systemId,
className,
children,
size = TooltipSize.xs,
}: KillsBookmarkTooltipProps) => {
const { isLoading, kills: detailedKills } = useKillsCounter({
realSystemId: systemId,
});
if (!killsCount || detailedKills.length === 0 || !systemId || isLoading) return null;
const limitedKills = useMemo(() => {
if (!detailedKills || detailedKills.length === 0) return [];
return detailedKills.slice(0, killsCount);
}, [detailedKills, killsCount]);
const tooltipContent = (
<SystemKillsContent kills={detailedKills} systemNameMap={systemNameMap} compact={true} onlyOneSystem={true} />
);
if (!killsCount || limitedKills.length === 0 || !systemId || isLoading) {
return null;
}
// Calculate height based on number of kills, but ensure a minimum height
const killsNeededHeight = limitedKills.length * KILLS_ROW_HEIGHT;
// Add a small buffer (10px) to prevent scrollbar from appearing unnecessarily
const tooltipHeight = Math.max(MIN_TOOLTIP_HEIGHT, Math.min(killsNeededHeight + 10, 500));
return (
// @ts-ignore
<WdTooltipWrapper content={tooltipContent} className={className} size={size} interactive={true}>
<WdTooltipWrapper
content={
<div className="overflow-hidden flex w-[450px] flex-col" style={{ height: `${tooltipHeight}px` }}>
<div className="flex-1 h-full">
<SystemKillsList kills={limitedKills} onlyOneSystem />
</div>
</div>
}
className={className}
tooltipClassName="!px-0"
size={size}
interactive={true}
>
{children}
</WdTooltipWrapper>
);

View File

@@ -4,6 +4,12 @@
z-index: 3;
}
.hoverTarget {
padding: 0.5rem;
margin: -0.5rem;
display: inline-block;
}
.localCounter {
mix-blend-mode: screen;
display: flex;

View File

@@ -6,8 +6,8 @@ import { CharItemProps, LocalCharactersList } from '../../../mapInterface/widget
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';
import { AvailableThemes } from '@/hooks/Mapper/mapRootProvider/types.ts';
interface LocalCounterProps {
localCounterCharacters: Array<CharItemProps>;
@@ -24,13 +24,12 @@ export const LocalCounter = ({ localCounterCharacters, hasUserCharacters, showIc
return (
<div
style={{
width: '300px',
overflowX: 'hidden',
overflowY: 'auto',
height: '300px',
width: '100%',
minWidth: '300px',
overflow: 'hidden',
}}
>
<LocalCharactersList items={localCounterCharacters} itemTemplate={itemTemplate} itemSize={26} />
<LocalCharactersList items={localCounterCharacters} itemTemplate={itemTemplate} itemSize={26} autoSize={true} />
</div>
);
}, [localCounterCharacters, itemTemplate]);
@@ -45,19 +44,16 @@ export const LocalCounter = ({ localCounterCharacters, hasUserCharacters, showIc
[classes.Pathfinder]: theme === AvailableThemes.pathfinder,
})}
>
<WdTooltipWrapper
// @ts-ignore
content={pilotTooltipContent}
position={TooltipPosition.right}
offset={0}
>
<div
className={clsx(classes.localCounter, {
[classes.hasUserCharacters]: hasUserCharacters,
})}
>
{showIcon && <i className="pi pi-users" />}
<span>{localCounterCharacters.length}</span>
<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

@@ -90,11 +90,7 @@ $tooltip-bg: #202020;
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(
45deg,
var(--eve-solar-system-status-color-background),
transparent
);
background-image: linear-gradient(45deg, var(--eve-solar-system-status-color-background), transparent);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
}
@@ -102,11 +98,7 @@ $tooltip-bg: #202020;
&.eve-system-status-friendly {
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-friendly-dark30),
transparent
);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly-dark30), transparent);
&.selected {
border-color: var(--eve-solar-system-status-color-friendly-dark5);
}
@@ -121,27 +113,15 @@ $tooltip-bg: #202020;
}
&.eve-system-status-warning {
background-image: linear-gradient(
275deg,
var(--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,
var(--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,
var(--eve-solar-system-status-target),
transparent
);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-target), transparent);
}
}
@@ -254,7 +234,7 @@ $tooltip-bg: #202020;
font-size: 11px;
font-weight: 500;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
color: var(--rf-tag-color, #38BDF8);
color: var(--rf-tag-color, #38bdf8);
}
/* Firefox kostyl */
@@ -263,7 +243,6 @@ $tooltip-bg: #202020;
font-weight: bold;
}
}
}
.BottomRow {
@@ -377,3 +356,14 @@ $tooltip-bg: #202020;
}
}
.ShatteredIcon {
position: relative;
//top: -1px;
left: -1px;
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
background-image: url(/images/chart-network-svgrepo-com.svg)
}

View File

@@ -4,7 +4,7 @@ import { Handle, NodeProps, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeDefault.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useSolarSystemNode } from '../../hooks/useSolarSystemLogic';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
@@ -14,32 +14,35 @@ import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/Worm
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { Tag } from 'primereact/tag';
// let render = 0;
export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
// console.log('JOipP', `render ${nodeVars.id}`, render++);
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 className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered, '!pr-[2px]')}>
<WdTooltipWrapper content="Shattered" position={TooltipPosition.top}>
<span className={clsx('block w-[10px] h-[10px]', classes.ShatteredIcon)} />
</WdTooltipWrapper>
</div>
)}
{nodeVars.killsCount && nodeVars.killsCount > 0 && nodeVars.solarSystemId && (
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={nodeVars.killsCount}
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size="lg"
size={TooltipSize.lg}
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
@@ -50,6 +53,12 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
</KillsCounter>
)}
{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.labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
@@ -81,7 +90,11 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{nodeVars.tag}</div>
<Tag
value={nodeVars.tag}
severity="warning"
className="py-0 px-[2px] text-[9px] [&_.p-tag-value]:leading-[1.3]"
></Tag>
)}
<div

View File

@@ -4,7 +4,7 @@ import { Handle, NodeProps, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeTheme.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useSolarSystemNode } from '../../hooks/useSolarSystemLogic';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
@@ -14,32 +14,34 @@ import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/Worm
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
// let render = 0;
export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
// console.log('JOipP', `render ${nodeVars.id}`, render++);
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 className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered, '!pr-[2px]')}>
<WdTooltipWrapper content="Shattered" position={TooltipPosition.top}>
<span className={clsx('block w-[10px] h-[10px]', classes.ShatteredIcon)} />
</WdTooltipWrapper>
</div>
)}
{nodeVars.killsCount && nodeVars.killsCount > 0 && nodeVars.solarSystemId && (
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={nodeVars.killsCount}
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size="lg"
size={TooltipSize.lg}
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
@@ -50,6 +52,12 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
</KillsCounter>
)}
{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.labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}

View File

@@ -16,7 +16,7 @@ export enum SOLAR_SYSTEM_CLASS_IDS {
thera = 12,
c13 = 13,
sentinel = 14,
baribican = 15,
barbican = 15,
vidette = 16,
conflux = 17,
redoubt = 18,
@@ -82,7 +82,7 @@ export const SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS = {
thera: SOLAR_SYSTEM_CLASS_GROUPS.thera,
c13: SOLAR_SYSTEM_CLASS_GROUPS.c13,
sentinel: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
baribican: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
barbican: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
vidette: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
conflux: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
redoubt: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
@@ -217,7 +217,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 14,
effectPower: 2,
title: 'Class 14 (Sentinel Drifter)',
shortTitle: 'Sentinel',
shortTitle: 'Sentinel MZ',
},
{
id: 'barbican',
@@ -225,7 +225,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 15,
effectPower: 2,
title: 'Class 15 (Barbican Drifter)',
shortTitle: 'Barbican',
shortTitle: 'Liberated Barbican',
},
{
id: 'vidette',
@@ -233,7 +233,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 16,
effectPower: 2,
title: 'Class 16 (Vidette Drifter)',
shortTitle: 'Vidette',
shortTitle: 'Sanctified Vidette',
},
{
id: 'conflux',
@@ -241,7 +241,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 17,
effectPower: 2,
title: 'Class 17 (Conflux Drifter)',
shortTitle: 'Conflux',
shortTitle: 'Conflux Eyrie',
},
{
id: 'redoubt',
@@ -249,7 +249,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 18,
effectPower: 2,
title: 'Class 18 (Redoubt Drifter)',
shortTitle: 'Redoubt',
shortTitle: 'Azdaja Redoubt',
},
{
id: 'a1',
@@ -322,6 +322,9 @@ export const WORMHOLES_ADDITIONAL_INFO: Record<string, WormholesAdditionalInfoTy
export const WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.wormholeClassID]: x }), {});
export const WORMHOLES_ADDITIONAL_INFO_BY_SHORT_NAME: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.shortName]: x }), {});
// export const SOLAR_SYSTEM_CLASS_NAMES = {
// ccp1 = ,
// c1 = ,
@@ -650,6 +653,7 @@ export enum LABELS {
l3 = '3',
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const LABELS_INFO: Record<string, any> = {
[LABELS.clear]: { id: 'clear', name: 'Clear', shortName: '', icon: '' },
[LABELS.la]: { id: 'la', name: 'Label A', shortName: 'A', icon: '' },
@@ -750,6 +754,17 @@ export const SHIP_SIZES_SIZE = {
[ShipSizeStatus.capital]: '2M',
};
export const SHIP_MASSES_SIZE: Record<number, ShipSizeStatus> = {
5_000_000: ShipSizeStatus.small,
62_000_000: ShipSizeStatus.medium,
300_000_000: ShipSizeStatus.large,
375_000_000: ShipSizeStatus.large,
1_000_000_000: ShipSizeStatus.freight,
1_350_000_000: ShipSizeStatus.capital,
1_800_000_000: ShipSizeStatus.capital,
2_000_000_000: ShipSizeStatus.capital,
};
export const SHIP_SIZES_DESCRIPTION = {
[ShipSizeStatus.small]: 'Frigate wormhole - up to Destroyer | 5K t.',
[ShipSizeStatus.medium]: 'Cruise wormhole - up to Battlecruiser | 62K t.',

View File

@@ -10,7 +10,7 @@ export const isWormholeSpace = (wormholeClassID: number) => {
case SOLAR_SYSTEM_CLASS_IDS.c6:
case SOLAR_SYSTEM_CLASS_IDS.c13:
case SOLAR_SYSTEM_CLASS_IDS.thera:
case SOLAR_SYSTEM_CLASS_IDS.baribican:
case SOLAR_SYSTEM_CLASS_IDS.barbican:
case SOLAR_SYSTEM_CLASS_IDS.vidette:
case SOLAR_SYSTEM_CLASS_IDS.conflux:
case SOLAR_SYSTEM_CLASS_IDS.redoubt:

View File

@@ -2,10 +2,13 @@ import { Node, useReactFlow } from 'reactflow';
import { useCallback, useRef } from 'react';
import { CommandAddSystems } from '@/hooks/Mapper/types/mapHandlers.ts';
import { convertSystem2Node } from '../../helpers';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
export const useMapAddSystems = () => {
const rf = useReactFlow();
const { addSystemStatic } = useLoadSystemStatic({ systems: [] });
const ref = useRef({ rf });
ref.current = { rf };
@@ -13,7 +16,10 @@ export const useMapAddSystems = () => {
const { rf } = ref.current;
const nodes = rf.getNodes();
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
const newSystems = systems.filter(x => !nodes.some(y => x.id === y.id));
newSystems.forEach(x => addSystemStatic(x.system_static_info));
const prepared: Node[] = newSystems.map(convertSystem2Node);
rf.addNodes(prepared);
}, []);
};

View File

@@ -1,6 +1,6 @@
import { MapData, useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { useCallback, useRef } from 'react';
import { CommandKillsUpdated, CommandMapUpdated } from '@/hooks/Mapper/types';
import { useCallback, useRef } from 'react';
export const useMapCommands = () => {
const { update } = useMapState();
@@ -8,13 +8,21 @@ export const useMapCommands = () => {
const ref = useRef({ update });
ref.current = { update };
const mapUpdated = useCallback(({ hubs }: CommandMapUpdated) => {
const mapUpdated = useCallback(({ hubs, system_signatures, kills }: CommandMapUpdated) => {
const out: Partial<MapData> = {};
if (hubs) {
out.hubs = hubs;
}
if (system_signatures) {
out.systemSignatures = system_signatures;
}
if (kills) {
out.kills = kills.reduce((acc, x) => ({ ...acc, [x.solar_system_id]: x.kills }), {});
}
ref.current.update(out);
}, []);

View File

@@ -14,6 +14,7 @@ export const useMapInit = () => {
return useCallback(
({
systems,
system_signatures,
kills,
connections,
wormholes,
@@ -51,6 +52,10 @@ export const useMapInit = () => {
updateData.systems = systems;
}
if (system_signatures) {
updateData.systemSignatures = system_signatures;
}
if (kills) {
updateData.kills = kills.reduce((acc, x) => ({ ...acc, [x.solar_system_id]: x.kills }), {});
}

View File

@@ -1,3 +1,11 @@
export * from './useMapHandlers';
export * from './useUpdateNodes';
export * from './useNodesEdgesState';
export * from './useBackgroundVars';
export * from './useKillsCounter';
export * from './useSystemName';
export * from './useNodesEdgesState';
export * from './useSolarSystemNode';
export * from './useUnsplashedSignatures';
export * from './useUpdateNodes';
export * from './useNodeKillsCount';

View File

@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useSystemKills } from '../../mapInterface/widgets/SystemKills/hooks/useSystemKills';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useSystemKills } from '@/hooks/Mapper/components/mapInterface/widgets/WSystemKills/hooks/useSystemKills.ts';
interface UseKillsCounterProps {
realSystemId: string;
@@ -27,13 +27,12 @@ export function useKillsCounter({ realSystemId }: UseKillsCounterProps) {
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);
// Sort kills by time, most recent first, but don't limit the number of kills
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;
});
}, [allKills]);
return {

View File

@@ -0,0 +1,31 @@
import { useMemo } from 'react';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
interface UseLabelsInfoParams {
labels: string | null;
linkedSigPrefix: string | null;
isShowLinkedSigId: boolean;
}
export type LabelInfo = {
id: string;
shortName: string;
};
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 useLabelsInfo({ labels, linkedSigPrefix, isShowLinkedSigId }: UseLabelsInfoParams) {
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]);
return { labelsInfo, labelCustom };
}

View File

@@ -131,6 +131,21 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
// do nothing here
break;
case Commands.characterActivityData:
break;
case Commands.trackingCharactersData:
break;
case Commands.updateActivity:
break;
case Commands.updateTracking:
break;
case Commands.userSettingsUpdated:
break;
default:
console.warn(`Map handlers: Unknown command: ${type}`, data);
break;

View File

@@ -0,0 +1,40 @@
import { useEffect, useState, useCallback } from 'react';
import { useMapEventListener } from '@/hooks/Mapper/events';
import { Commands } from '@/hooks/Mapper/types';
interface Kill {
solar_system_id: number | string;
kills: number;
}
interface MapEvent {
name: Commands;
data?: any;
payload?: Kill[];
}
export function useNodeKillsCount(systemId: number | string, initialKillsCount: number | null): number | null {
const [killsCount, setKillsCount] = useState<number | null>(initialKillsCount);
useEffect(() => {
setKillsCount(initialKillsCount);
}, [initialKillsCount]);
const handleEvent = useCallback(
(event: MapEvent): boolean => {
if (event.name === Commands.killsUpdated && Array.isArray(event.payload)) {
const killForSystem = event.payload.find(kill => kill.solar_system_id.toString() === systemId.toString());
if (killForSystem && typeof killForSystem.kills === 'number') {
setKillsCount(killForSystem.kills);
}
return true;
}
return false;
},
[systemId],
);
useMapEventListener(handleEvent);
return killsCount;
}

View File

@@ -7,18 +7,13 @@ 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 { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { CharacterTypeRaw, OutCommand, SystemSignature } from '@/hooks/Mapper/types';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
export type LabelInfo = {
id: string;
shortName: string;
};
export type UnsplashedSignatureType = SystemSignature & { sig_id: string };
import { useUnsplashedSignatures } from './useUnsplashedSignatures';
import { useSystemName } from './useSystemName';
import { LabelInfo, useLabelsInfo } from './useLabelsInfo';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
function getActivityType(count: number): string {
if (count <= 5) return 'activityNormal';
@@ -33,11 +28,6 @@ const SpaceToClass: Record<string, string> = {
[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
@@ -54,8 +44,7 @@ export function useLocalCounter(nodeVars: SolarSystemNodeVars) {
export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarSystemNodeVars {
const { id, data, selected } = props;
const {
system_static_info,
system_signatures,
id: solar_system_id,
locked,
name,
tag,
@@ -65,23 +54,26 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
linked_sig_eve_id: linkedSigEveId = '',
} = data;
const {
storedSettings: { interfaceSettings },
data: { systemSignatures: mapSystemSignatures },
} = useMapRootState();
const systemStaticInfo = useMemo(() => {
return getSystemStaticInfo(solar_system_id)!;
}, [solar_system_id]);
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();
} = systemStaticInfo;
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
@@ -106,13 +98,10 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const systemSigs = useMemo(
() => mapSystemSignatures[solar_system_id] || system_signatures,
[system_signatures, solar_system_id, mapSystemSignatures],
);
const systemSigs = useMemo(() => mapSystemSignatures[solar_system_id] || [], [solar_system_id, mapSystemSignatures]);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === solar_system_id && c.online);
return characters.filter(c => c.location?.solar_system_id === parseInt(solar_system_id) && c.online);
}, [characters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
@@ -126,81 +115,43 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
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 { labelsInfo, labelCustom } = useLabelsInfo({
labels,
linkedSigPrefix,
isShowLinkedSigId,
});
const killsCount = useMemo(() => kills[solar_system_id] ?? null, [kills, solar_system_id]);
const killsCount = useMemo(() => kills[parseInt(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 hasUserCharacters = useMemo(
() => charactersInSystem.some(x => userCharacters.includes(x.eve_id)),
[charactersInSystem, userCharacters],
);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: { system_id: solar_system_id.toString() },
data: { system_id: solar_system_id },
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const regionClass = showKSpaceBG ? SpaceToClass[space] || null : 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, computedTemporaryName, customName } = useSystemName({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
});
const systemName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName) {
return computedTemporaryName;
}
return solar_system_name;
}, [isTempSystemNameEnabled, solar_system_name, computedTemporaryName]);
const { unsplashedLeft, unsplashedRight } = useUnsplashedSignatures(systemSigs, isShowUnsplashedSignatures);
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,
sig_id: s.eve_id, // Add a unique key property
})) as UnsplashedSignatureType[],
);
}, [isShowUnsplashedSignatures, systemSigs]);
// Ensure hubs are always strings.
const hubsAsStrings = useMemo(() => hubs.map(item => item.toString()), [hubs]);
const nodeVars: SolarSystemNodeVars = {
@@ -225,12 +176,10 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
dbClick,
sortedStatics,
effectName: effect_name,
regionName: region_name,
solarSystemId: solar_system_id.toString(),
solarSystemName: solar_system_name,
locked,
hubs: hubsAsStrings,
name: name,
name,
isConnecting,
hoverNodeId,
charactersInSystem,
@@ -239,6 +188,8 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
isThickConnections,
classTitle: class_title,
temporaryName: computedTemporaryName,
regionName: region_name,
solarSystemName: solar_system_name,
};
return nodeVars;

View File

@@ -0,0 +1,56 @@
import { useMemo } from 'react';
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
interface UseSystemNameParams {
isTempSystemNameEnabled: boolean;
temporary_name?: string | null;
isShowLinkedSigIdTempName: boolean;
linkedSigPrefix: string | null;
name?: string | null;
systemStaticInfo: SolarSystemStaticInfoRaw;
}
export const useSystemName = ({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
}: UseSystemNameParams) => {
const { solar_system_name = '' } = systemStaticInfo;
const computedTemporaryName = useMemo(() => {
if (!isTempSystemNameEnabled) {
return '';
}
if (isShowLinkedSigIdTempName && linkedSigPrefix) {
return temporary_name ? `${linkedSigPrefix}${temporary_name}` : `${linkedSigPrefix}${solar_system_name}`;
}
return temporary_name ?? '';
}, [isTempSystemNameEnabled, temporary_name, solar_system_name, isShowLinkedSigIdTempName, linkedSigPrefix]);
const systemName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName) {
return computedTemporaryName;
}
return solar_system_name;
}, [isTempSystemNameEnabled, computedTemporaryName, solar_system_name]);
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]);
return { systemName, computedTemporaryName, customName };
};

View File

@@ -0,0 +1,30 @@
import { useMemo } from 'react';
import { SystemSignature } from '@/hooks/Mapper/types';
import { prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
export type UnsplashedSignatureType = SystemSignature & { sig_id: string };
export function useUnsplashedSignatures(systemSigs: SystemSignature[], isShowUnsplashedSignatures: boolean) {
return useMemo(() => {
if (!isShowUnsplashedSignatures) {
return {
unsplashedLeft: [] as SystemSignature[],
unsplashedRight: [] as SystemSignature[],
};
}
const chunks = 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[],
);
const [unsplashedLeft, unsplashedRight] = chunks;
return { unsplashedLeft, unsplashedRight };
}, [isShowUnsplashedSignatures, systemSigs]);
}

View File

@@ -6,9 +6,9 @@ import { SolarSystemRawType } from '@/hooks/Mapper/types';
const useThrottle = () => {
const throttleSeed = useRef<number | null>(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const throttleFunction = useRef((func: any, delay = 200) => {
if (!throttleSeed.current) {
// Call the callback immediately for the first time
func();
throttleSeed.current = setTimeout(() => {
throttleSeed.current = null;
@@ -75,7 +75,7 @@ export const useUpdateNodes = (nodes: Node<SolarSystemRawType>[]) => {
const visibleNodes = new Set(nodes.filter(x => isNodeVisible(x, viewport)).map(x => x.id));
update({ visibleNodes });
}, [nodes]);
}, [getViewport, nodes, update]);
useOnViewportChange({
onChange: () => throttle(updateNodesVisibility.bind(this)),
@@ -84,5 +84,5 @@ export const useUpdateNodes = (nodes: Node<SolarSystemRawType>[]) => {
useEffect(() => {
updateNodesVisibility();
}, [nodes]);
}, [nodes, updateNodesVisibility]);
};

View File

@@ -542,48 +542,32 @@
background-color: #d10600;
}
.react-flow {
color: var(--text-color);
&__pane {
cursor: auto;
}
.react-flow__minimap-node {
fill: #ffb03a;
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
.react-flow__minimap {
border: 1px solid #282828;
border-radius: 4px;
background-color: rgb(47 37 37) !important;
overflow: hidden;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
.react-flow__minimap-mask {
stroke-width: 2px;
fill: rgba(0, 0, 0, 0.5);
mix-blend-mode: overlay;
}
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
.react-flow__minimap-mask {
stroke-width: 2px;
fill: rgb(0 0 0 / 50%) !important;
mix-blend-mode: inherit;
opacity: 1;
stroke: #fff;
}
.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,52 @@
import { MarkdownComment } from '@/hooks/Mapper/components/mapInterface/components/Comments/components';
import { useEffect, useRef, useState } from 'react';
import { CommentType } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export interface CommentsProps {}
// eslint-disable-next-line no-empty-pattern
export const Comments = ({}: CommentsProps) => {
const [commentsList, setCommentsList] = useState<CommentType[]>([]);
const {
data: { selectedSystems },
comments: { loadComments, comments, lastUpdateKey },
} = useMapRootState();
const [systemId] = selectedSystems;
const ref = useRef({ loadComments, systemId });
ref.current = { loadComments, systemId };
useEffect(() => {
const commentsBySystem = comments.get(systemId);
if (!commentsBySystem) {
return;
}
const els = [...commentsBySystem.comments].sort((a, b) => +new Date(b.updated_at) - +new Date(a.updated_at));
setCommentsList(els);
}, [systemId, lastUpdateKey, comments]);
useEffect(() => {
ref.current.loadComments(systemId);
}, [systemId]);
if (commentsList.length === 0) {
return (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
Not comments found here
</div>
);
}
return (
<div className="flex flex-col gap-1 whitespace-nowrap overflow-auto text-ellipsis custom-scrollbar">
{commentsList.map(({ id, text, updated_at, characterEveId }) => (
<MarkdownComment key={id} text={text} time={updated_at} characterEveId={characterEveId} id={id} />
))}
</div>
);
};

View File

@@ -0,0 +1,70 @@
.MarkdownCommentRoot {
border-left-width: 3px;
@apply text-[12px] leading-[1.2] text-stone-300 break-words;
@apply bg-gradient-to-r from-stone-600/40 via-stone-600/10 to-stone-600/0;
.h1 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h2 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h3 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h4 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h5 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h6 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
p {
@apply m-0 p-0 break-words whitespace-normal;
}
ul, ol {
@apply m-0 p-0 list-none;
}
li {
@apply m-0 break-words whitespace-normal;
}
blockquote {
@apply border-l-4 border-cyan-400 p-2 m-0 font-normal text-stone-300 italic break-words whitespace-normal;
}
a {
@apply text-violet-400 cursor-pointer transition-colors duration-200 break-words whitespace-normal;
&:hover {
@apply underline;
}
}
b, strong {
@apply font-bold text-green-400 break-words whitespace-normal;
}
i, em {
@apply italic text-pink-400 break-words whitespace-normal;
}
del {
@apply line-through text-stone-500 break-words whitespace-normal;
}
hr {
@apply border-none h-[1px] bg-cyan-400 opacity-50 my-2;
}
}

View File

@@ -0,0 +1,95 @@
import classes from './MarkdownComment.module.scss';
import clsx from 'clsx';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { InfoDrawer, TimeAgo, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import remarkBreaks from 'remark-breaks';
import { useGetCacheCharacter } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useCallback, useRef, useState } from 'react';
import { WdTransition } from '@/hooks/Mapper/components/ui-kit/WdTransition/WdTransition.tsx';
import { PrimeIcons } from 'primereact/api';
import { ConfirmPopup } from 'primereact/confirmpopup';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { OutCommand } from '@/hooks/Mapper/types';
const TOOLTIP_PROPS = { content: 'Remove comment', position: TooltipPosition.top };
const REMARK_PLUGINS = [remarkGfm, remarkBreaks];
export interface MarkdownCommentProps {
text: string;
time: string;
characterEveId: string;
id: string;
}
export const MarkdownComment = ({ text, time, characterEveId, id }: MarkdownCommentProps) => {
const char = useGetCacheCharacter(characterEveId);
const [hovered, setHovered] = useState(false);
const cpRemoveBtnRef = useRef<HTMLElement>();
const [cpRemoveVisible, setCpRemoveVisible] = useState(false);
const { outCommand } = useMapRootState();
const ref = useRef({ outCommand, id });
ref.current = { outCommand, id };
const handleDelete = useCallback(async () => {
await ref.current.outCommand({
type: OutCommand.deleteSystemComment,
data: ref.current.id,
});
}, []);
const handleMouseEnter = useCallback(() => setHovered(true), []);
const handleMouseLeave = useCallback(() => setHovered(false), []);
const handleShowCP = useCallback(() => setCpRemoveVisible(true), []);
const handleHideCP = useCallback(() => setCpRemoveVisible(false), []);
return (
<>
<InfoDrawer
labelClassName="mb-[3px]"
className={clsx(classes.MarkdownCommentRoot, 'p-1 bg-stone-700/20 ')}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
title={
<div className="flex items-center justify-between">
<div>
<span className="text-stone-500">
by <span className="text-orange-300/70">{char?.data?.name ?? ''}</span>
</span>
</div>
<WdTransition active={hovered} timeout={100}>
<div className="text-stone-500 max-h-[12px]">
{!hovered && <TimeAgo timestamp={time} />}
{hovered && (
// @ts-ignore
<div ref={cpRemoveBtnRef}>
<WdImgButton
className={clsx(PrimeIcons.TRASH, 'hover:text-red-400')}
tooltip={TOOLTIP_PROPS}
onClick={handleShowCP}
/>
</div>
)}
</div>
</WdTransition>
</div>
}
>
<Markdown remarkPlugins={REMARK_PLUGINS}>{text}</Markdown>
</InfoDrawer>
<ConfirmPopup
target={cpRemoveBtnRef.current}
visible={cpRemoveVisible}
onHide={handleHideCP}
message="Are you sure you want to delete?"
icon="pi pi-exclamation-triangle"
accept={handleDelete}
/>
</>
);
};

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,26 @@
export const markdown = `
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
---
## Paragraphs
This is a regular text paragraph.
Another paragraph, but with **bold** and *italic* text, as well as ~~strikethrough~~.
> This is a block quote.
> Second line of the quote.
## Links
[Link to Google](https://www.google.com)
## Horizontal Line
A block quote with ~strikethrough~ and a URL: https://reactjs.org.
---
`;

View File

@@ -0,0 +1,72 @@
import { TooltipPosition, WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import clsx from 'clsx';
import { PrimeIcons } from 'primereact/api';
import { MarkdownEditor } from '@/hooks/Mapper/components/mapInterface/components/MarkdownEditor';
import { useHotkey } from '@/hooks/Mapper/hooks';
import { useCallback, useRef, useState } from 'react';
import { OutCommand } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export interface CommentsEditorProps {}
export const CommentsEditor = ({}: CommentsEditorProps) => {
const [textVal, setTextVal] = useState('');
const {
data: { selectedSystems },
outCommand,
} = useMapRootState();
const [systemId] = selectedSystems;
const ref = useRef({ outCommand, systemId, textVal });
ref.current = { outCommand, systemId, textVal };
const handleFinishEdit = useCallback(async () => {
if (ref.current.textVal === '') {
return;
}
await ref.current.outCommand({
type: OutCommand.addSystemComment,
data: {
solarSystemId: ref.current.systemId,
value: ref.current.textVal,
},
});
setTextVal('');
}, []);
const handleClick = async () => {
await handleFinishEdit();
};
useHotkey(true, ['Enter'], async () => {
await handleFinishEdit();
});
return (
<MarkdownEditor
value={textVal}
onChange={setTextVal}
overlayContent={
<div className="w-full h-full flex justify-end items-end pointer-events-none pb-[1px] pr-[8px]">
<WdImgButton
disabled={textVal.length === 0}
tooltip={{
position: TooltipPosition.bottom,
content: (
<span>
Also you may use <span className="text-cyan-400">Meta + Enter</span> hotkey.
</span>
),
}}
textSize={WdImageSize.large}
className={clsx(PrimeIcons.SEND, 'text-[14px]')}
onClick={handleClick}
/>
</div>
}
/>
);
};

View File

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

View File

@@ -0,0 +1,40 @@
.CERoot {
@apply border border-stone-400/30 rounded-[2px];
:global {
.cm-content {
@apply bg-stone-600/40;
}
.cm-scroller {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.5) transparent;
}
.cm-scroller::-webkit-scrollbar {
width: 10px;
}
.cm-scroller::-webkit-scrollbar-track {
background: transparent;
}
.cm-scroller::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.5);
border-radius: 5px;
border: 2px solid transparent;
background-clip: content-box;
}
.cm-scroller::-webkit-scrollbar-thumb:hover {
background-color: rgba(255, 255, 255, 0.7);
}
.cm-scroller::-webkit-scrollbar-button {
display: none;
height: 0;
width: 0;
}
}
}

View File

@@ -0,0 +1,86 @@
import { ReactNode, useCallback, useRef, useState } from 'react';
import CodeMirror, { ViewPlugin } from '@uiw/react-codemirror';
import { markdown } from '@codemirror/lang-markdown';
import { oneDark } from '@codemirror/theme-one-dark';
import { EditorView, type ViewUpdate } from '@codemirror/view';
import classes from './MarkdownEditor.module.scss';
import clsx from 'clsx';
// TODO special plugin which force CodeMirror using capture for paste event
const stopEventPropagationPlugin = ViewPlugin.fromClass(
class {
constructor(view: EditorView) {
// @ts-ignore
this.view = view;
// @ts-ignore
this.pasteHandler = (event: Event) => {
event.stopPropagation();
};
// @ts-ignore
view.dom.addEventListener('paste', this.pasteHandler);
}
destroy() {
// @ts-ignore
this.view.dom.removeEventListener('paste', this.pasteHandler);
}
},
);
const CODE_MIRROR_EXTENSIONS = [
markdown(),
EditorView.lineWrapping,
EditorView.theme({
'&': { backgroundColor: 'transparent !important' },
'& .cm-gutterElement': { display: 'none' },
}),
stopEventPropagationPlugin,
];
export interface MarkdownEditorProps {
overlayContent?: ReactNode;
value: string;
onChange: (value: string) => void;
}
export const MarkdownEditor = ({ value, onChange, overlayContent }: MarkdownEditorProps) => {
const [hasShift, setHasShift] = useState(false);
const refData = useRef({ onChange });
refData.current = { onChange };
const handleOnChange = useCallback((value: string, viewUpdate: ViewUpdate) => {
// Rerender happens after change
setTimeout(() => {
const scrollDOM = viewUpdate.view.scrollDOM;
setHasShift(scrollDOM.scrollHeight > scrollDOM.clientHeight);
}, 0);
refData.current.onChange(value);
}, []);
return (
<div className={clsx(classes.MarkdownEditor, 'relative')}>
<CodeMirror
value={value}
height="70px"
extensions={CODE_MIRROR_EXTENSIONS}
className={classes.CERoot}
theme={oneDark}
onChange={handleOnChange}
placeholder="Start typing..."
/>
<div
className={clsx('absolute top-0 left-0 h-full pointer-events-none', {
'w-full': !hasShift,
'w-[calc(100%-10px)]': hasShift,
})}
>
{overlayContent}
</div>
</div>
);
};

View File

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

View File

@@ -1,59 +1,173 @@
import { useCallback, useEffect, useRef } from 'react';
import { Dialog } from 'primereact/dialog';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { SystemSignature } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types';
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
import { SHOW_DESCRIPTION_COLUMN_SETTING } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatures';
import { useSystemInfo } from '@/hooks/Mapper/components/hooks';
import {
Setting,
COSMIC_SIGNATURE,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
import { SignatureGroup } from '@/hooks/Mapper/types';
SOLAR_SYSTEM_CLASS_IDS,
SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS,
WORMHOLES_ADDITIONAL_INFO_BY_SHORT_NAME,
} from '@/hooks/Mapper/components/map/constants.ts';
import {
SETTINGS_KEYS,
SignatureSettingsType,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
import { K162_TYPES_MAP } from '@/hooks/Mapper/constants.ts';
import { getWhSize } from '@/hooks/Mapper/helpers/getWhSize';
import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureCustomInfo';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { CommandLinkSignatureToSystem, SignatureGroup, SystemSignature, TimeStatus } from '@/hooks/Mapper/types';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
const K162_SIGNATURE_TYPE = WORMHOLES_ADDITIONAL_INFO_BY_SHORT_NAME['K162'].shortName;
interface SystemLinkSignatureDialogProps {
data: CommandLinkSignatureToSystem;
setVisible: (visible: boolean) => void;
}
const signatureSettings: Setting[] = [
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true },
{ key: SignatureGroup.Wormhole, name: 'Wormhole', value: true },
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: true, isFilter: false },
];
export const LINK_SIGNTATURE_SETTINGS: SignatureSettingsType = {
[SETTINGS_KEYS.COSMIC_SIGNATURE]: true,
[SETTINGS_KEYS.WORMHOLE]: true,
[SETTINGS_KEYS.SHOW_DESCRIPTION_COLUMN]: true,
};
// Extend the SignatureCustomInfo type to include k162Type
interface ExtendedSignatureCustomInfo {
k162Type?: string;
isEOL?: boolean;
[key: string]: unknown;
}
export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => {
const { outCommand } = useMapRootState();
const {
outCommand,
data: { wormholes },
} = useMapRootState();
const ref = useRef({ outCommand });
ref.current = { outCommand };
// Get system info for the target system
const { staticInfo: targetSystemInfo, dynamicInfo: targetSystemDynamicInfo } = useSystemInfo({
systemId: `${data.solar_system_target}`,
});
// Get the system class group for the target system
const targetSystemClassGroup = useMemo(() => {
if (!targetSystemInfo) return null;
const systemClassId = targetSystemInfo.system_class;
const systemClassKey = Object.keys(SOLAR_SYSTEM_CLASS_IDS).find(
key => SOLAR_SYSTEM_CLASS_IDS[key as keyof typeof SOLAR_SYSTEM_CLASS_IDS] === systemClassId,
);
if (!systemClassKey) return null;
return (
SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[systemClassKey as keyof typeof SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS] || null
);
}, [targetSystemInfo]);
const handleHide = useCallback(() => {
setVisible(false);
}, [setVisible]);
const handleSelect = useCallback(
const filterSignature = useCallback(
(signature: SystemSignature) => {
if (signature.group !== SignatureGroup.Wormhole || !targetSystemClassGroup) {
return true;
}
if (!signature.type) {
return true;
}
if (signature.type === K162_SIGNATURE_TYPE) {
// Parse the custom info to see if the user has specified what class this K162 leads to
const customInfo = parseSignatureCustomInfo(signature.custom_info) as ExtendedSignatureCustomInfo;
// If the user has specified a k162Type for this K162
if (customInfo.k162Type) {
// Get the K162 type information
const k162TypeInfo = K162_TYPES_MAP[customInfo.k162Type];
if (k162TypeInfo) {
// Check if the k162Type matches our target system class
return customInfo.k162Type === targetSystemClassGroup;
}
}
// If no k162Type is specified or we couldn't find type info, allow it
return true;
}
// Find the wormhole data for this signature type
const wormholeData = wormholes.find(wh => wh.name === signature.type);
if (!wormholeData) {
return true; // If we don't know the destination, don't filter it out
}
// Get the destination system class from the wormhole data
const destinationClass = wormholeData.dest;
// Check if the destination class matches the target system class
const isMatch = destinationClass === targetSystemClassGroup;
return isMatch;
},
[targetSystemClassGroup, wormholes],
);
const handleSelect = useCallback(
async (signature: SystemSignature) => {
if (!signature) {
return;
}
const { outCommand } = ref.current;
outCommand({
await outCommand({
type: OutCommand.linkSignatureToSystem,
data: {
...data,
signature_eve_id: signature.eve_id,
},
});
if (parseSignatureCustomInfo(signature.custom_info).isEOL === true) {
await outCommand({
type: OutCommand.updateConnectionTimeStatus,
data: {
source: data.solar_system_source,
target: data.solar_system_target,
value: TimeStatus.eol,
},
});
}
const whShipSize = getWhSize(wormholes, signature.type);
if (whShipSize) {
await outCommand({
type: OutCommand.updateConnectionShipSizeType,
data: {
source: data.solar_system_source,
target: data.solar_system_target,
value: whShipSize,
},
});
}
setVisible(false);
},
[data, setVisible],
[data, setVisible, wormholes],
);
useEffect(() => {
if (!targetSystemDynamicInfo) {
handleHide();
}
}, [targetSystemDynamicInfo]);
return (
<Dialog
header="Select signature to link"
@@ -66,9 +180,10 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
<SystemSignaturesContent
systemId={`${data.solar_system_source}`}
hideLinkedSignatures
settings={signatureSettings}
settings={LINK_SIGNTATURE_SETTINGS}
onSelect={handleSelect}
selectable={true}
filterSignature={filterSignature}
/>
</Dialog>
);

View File

@@ -10,6 +10,7 @@ import { OutCommand } from '@/hooks/Mapper/types';
import { IconField } from 'primereact/iconfield';
import { TooltipPosition, WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
interface SystemSettingsDialog {
systemId: string;
@@ -26,6 +27,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const system = getSystemById(systems, systemId);
const systemStaticInfo = getSystemStaticInfo(systemId);
const [name, setName] = useState('');
const [label, setLabel] = useState('');
@@ -33,11 +35,11 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
const [description, setDescription] = useState('');
const inputRef = useRef<HTMLInputElement>();
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system });
ref.current = { name, description, label, temporaryName, outCommand, systemId, system };
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system, systemStaticInfo });
ref.current = { name, description, label, temporaryName, outCommand, systemId, system, systemStaticInfo };
const handleSave = useCallback(() => {
const { name, description, label, temporaryName, outCommand, systemId, system } = ref.current;
const { name, description, label, temporaryName, outCommand, systemId, system, systemStaticInfo } = ref.current;
const outLabel = new LabelsManager(system?.labels ?? '');
outLabel.updateCustomLabel(label);
@@ -62,7 +64,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
type: OutCommand.updateSystemName,
data: {
system_id: systemId,
value: name.trim() || system?.system_static_info.solar_system_name,
value: name.trim() || systemStaticInfo?.solar_system_name,
},
});
@@ -78,11 +80,11 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
}, [setVisible]);
const handleResetSystemName = useCallback(() => {
const { system } = ref.current;
if (!system) {
const { systemStaticInfo } = ref.current;
if (!systemStaticInfo) {
return;
}
setName(system.system_static_info.solar_system_name);
setName(systemStaticInfo.solar_system_name);
}, []);
const onShow = useCallback(() => {
@@ -130,7 +132,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
<label htmlFor="username">Custom name</label>
<IconField>
{name !== system?.system_static_info.solar_system_name && (
{name !== systemStaticInfo?.solar_system_name && (
<WdImgButton
className="pi pi-undo"
textSize={WdImageSize.large}

View File

@@ -1,5 +1,5 @@
.root {
padding-bottom: 5px;
}
.Header {

View File

@@ -2,15 +2,18 @@ import React from 'react';
import classes from './Widget.module.scss';
import clsx from 'clsx';
import { WithChildren } from '@/hooks/Mapper/types/common.ts';
export interface WidgetProps {
export type WidgetProps = {
label: React.ReactNode | string;
children?: React.ReactNode;
}
windowId?: string;
contentClassName?: string;
} & WithChildren;
export const Widget = ({ label, children }: WidgetProps) => {
export const Widget = ({ label, children, windowId, contentClassName }: WidgetProps) => {
return (
<div
data-window-id={windowId}
className={clsx(
classes.root,
'flex flex-col w-full h-full rounded',
@@ -32,7 +35,7 @@ export const Widget = ({ label, children }: WidgetProps) => {
{label}
</div>
<div
className={clsx(classes.Content, 'overflow-auto', 'bg-opacity-5 custom-scrollbar')}
className={clsx(classes.Content, 'overflow-auto', 'bg-opacity-5 custom-scrollbar', contentClassName)}
style={{ flexGrow: 1 }}
onContextMenu={e => {
e.preventDefault();

View File

@@ -1,14 +1,16 @@
import { WindowProps } from '@/hooks/Mapper/components/ui-kit/WindowManager/types.ts';
import {
CommentsWidget,
LocalCharacters,
RoutesWidget,
SystemInfo,
SystemSignatures,
SystemStructures,
SystemKills,
WRoutesPublic,
WRoutesUser,
WSystemKills,
} from '@/hooks/Mapper/components/mapInterface/widgets';
export const CURRENT_WINDOWS_VERSION = 8;
export const CURRENT_WINDOWS_VERSION = 9;
export const WINDOWS_LOCAL_STORE_KEY = 'windows:settings:v2';
export enum WidgetsIds {
@@ -18,6 +20,8 @@ export enum WidgetsIds {
routes = 'routes',
structures = 'structures',
kills = 'kills',
comments = 'comments',
userRoutes = 'userRoutes',
}
export const STORED_VISIBLE_WIDGETS_DEFAULT = [
@@ -54,7 +58,14 @@ export const DEFAULT_WIDGETS: WindowProps[] = [
position: { x: 10, y: 530 },
size: { width: 510, height: 200 },
zIndex: 0,
content: () => <RoutesWidget />,
content: () => <WRoutesPublic />,
},
{
id: WidgetsIds.userRoutes,
position: { x: 10, y: 530 },
size: { width: 510, height: 200 },
zIndex: 0,
content: () => <WRoutesUser />,
},
{
id: WidgetsIds.structures,
@@ -68,7 +79,14 @@ export const DEFAULT_WIDGETS: WindowProps[] = [
position: { x: 270, y: 730 },
size: { width: 510, height: 200 },
zIndex: 0,
content: () => <SystemKills />,
content: () => <WSystemKills />,
},
{
id: WidgetsIds.comments,
position: { x: 10, y: 10 },
size: { width: 250, height: 300 },
zIndex: 0,
content: () => <CommentsWidget />,
},
];
@@ -94,6 +112,10 @@ export const WIDGETS_CHECKBOXES_PROPS: WidgetsCheckboxesType = [
id: WidgetsIds.routes,
label: 'Routes',
},
{
id: WidgetsIds.userRoutes,
label: 'User Routes',
},
{
id: WidgetsIds.structures,
label: 'Structures',
@@ -102,4 +124,8 @@ export const WIDGETS_CHECKBOXES_PROPS: WidgetsCheckboxesType = [
id: WidgetsIds.kills,
label: 'Kills',
},
{
id: WidgetsIds.comments,
label: 'Comments',
},
];

View File

@@ -0,0 +1,83 @@
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { Comments } from '@/hooks/Mapper/components/mapInterface/components/Comments';
import { InfoDrawer, SystemView, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { useRef } from 'react';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { COMPACT_MAX_WIDTH } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import clsx from 'clsx';
import { CommentsEditor } from '@/hooks/Mapper/components/mapInterface/components/CommentsEditor';
import { PrimeIcons } from 'primereact/api';
export const CommentsWidgetContent = () => {
const {
data: { selectedSystems },
} = useMapRootState();
const isNotSelectedSystem = selectedSystems.length !== 1;
if (isNotSelectedSystem) {
return (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
System is not selected
</div>
);
}
return (
<div className={clsx('h-full grid grid-rows-[1fr_auto] gap-1 px-[4px]')}>
<Comments />
<CommentsEditor />
</div>
);
};
export const CommentsWidget = () => {
const containerRef = useRef<HTMLDivElement>(null);
const isCompact = useMaxWidth(containerRef, COMPACT_MAX_WIDTH);
const {
data: { selectedSystems, isSubscriptionActive },
} = useMapRootState();
const [systemId] = selectedSystems;
const isNotSelectedSystem = selectedSystems.length !== 1;
return (
<Widget
contentClassName="my-1"
label={
<div ref={containerRef} className="flex justify-between items-center gap-1 text-xs w-full">
<div className="flex items-center gap-1">
{!isCompact && (
<div className="flex whitespace-nowrap text-ellipsis overflow-hidden text-stone-400">
Comments {isNotSelectedSystem ? '' : 'in'}
</div>
)}
{!isNotSelectedSystem && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
</div>
<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/delete comment?</b>}>
It is possible to use markdown formating. <br />
Only users with tracking permission can add/delete comments. <br />
</InfoDrawer>
<InfoDrawer title={<b className="text-slate-50">Limitations</b>}>
Each comment length is limited to <b>500</b> characters. <br />
No more than <b>{isSubscriptionActive ? '500' : '30'}</b> comments are allowed per system*. <br />
<small>* based on active map subscription.</small>
</InfoDrawer>
</div>
),
}}
/>
</div>
}
>
<CommentsWidgetContent />
</Widget>
);
};

View File

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

View File

@@ -13,6 +13,7 @@ export const LocalCharactersItemTemplate = ({ showShipName, ...options }: LocalC
className={clsx(
classes.CharacterRow,
'box-border flex items-center w-full whitespace-nowrap overflow-hidden text-ellipsis min-w-[0px]',
'px-1',
{
'surface-hover': options.odd,
'border-b border-gray-600 border-opacity-20': !options.last,
@@ -21,7 +22,7 @@ export const LocalCharactersItemTemplate = ({ showShipName, ...options }: LocalC
)}
style={{ height: `${options.props.itemSize}px` }}
>
<CharacterCard showShipName={showShipName} {...options} />
<CharacterCard showShipName={showShipName} showTicker {...options} />
</div>
);
};

View File

@@ -3,11 +3,13 @@ import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virt
import clsx from 'clsx';
import { CharItemProps } from './types';
type LocalCharactersListProps = {
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 = ({
@@ -15,7 +17,19 @@ export const LocalCharactersList = ({
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}
@@ -23,6 +37,8 @@ export const LocalCharactersList = ({
orientation="vertical"
className={clsx('w-full h-full', containerClassName)}
itemTemplate={itemTemplate}
autoSize={autoSize}
style={localStyle}
/>
);
};

View File

@@ -1,79 +1,34 @@
import React, { createContext, useContext, useEffect } from 'react';
import { ContextStoreDataUpdate, useContextStore } from '@/hooks/Mapper/utils';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import React, { createContext, forwardRef, useContext, useImperativeHandle, useState } from 'react';
import {
RoutesImperativeHandle,
RoutesProviderInnerProps,
RoutesWidgetProps,
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/types.ts';
export type RoutesType = {
path_type: 'shortest' | 'secure' | 'insecure';
include_mass_crit: boolean;
include_eol: boolean;
include_frig: boolean;
include_cruise: boolean;
include_thera: boolean;
avoid_wormholes: boolean;
avoid_pochven: boolean;
avoid_edencom: boolean;
avoid_triglavian: boolean;
avoid: number[];
};
interface MapProviderProps {
type MapProviderProps = {
children: React.ReactNode;
}
} & RoutesWidgetProps;
export const DEFAULT_SETTINGS: RoutesType = {
path_type: 'shortest',
include_mass_crit: true,
include_eol: true,
include_frig: true,
include_cruise: true,
include_thera: true,
avoid_wormholes: false,
avoid_pochven: false,
avoid_edencom: false,
avoid_triglavian: false,
avoid: [],
};
export interface MapContextProps {
update: ContextStoreDataUpdate<RoutesType>;
data: RoutesType;
}
const RoutesContext = createContext<MapContextProps>({
const RoutesContext = createContext<RoutesProviderInnerProps>({
update: () => {},
data: { ...DEFAULT_SETTINGS },
// @ts-ignore
data: {},
});
export const RoutesProvider: React.FC<MapProviderProps> = ({ children }) => {
const { update, ref } = useContextStore<RoutesType>(
{ ...DEFAULT_SETTINGS },
{
onAfterAUpdate: values => {
localStorage.setItem(SESSION_KEY.routes, JSON.stringify(values));
},
export const RoutesProvider = forwardRef<RoutesImperativeHandle, MapProviderProps>(({ children, ...props }, ref) => {
const [loading, setLoading] = useState(false);
useImperativeHandle(ref, () => ({
stopLoading() {
setLoading(false);
},
);
}));
useEffect(() => {
const items = localStorage.getItem(SESSION_KEY.routes);
if (items) {
update(JSON.parse(items));
}
}, [update]);
return (
<RoutesContext.Provider
value={{
update,
data: ref,
}}
>
{children}
</RoutesContext.Provider>
);
};
return <RoutesContext.Provider value={{ ...props, loading, setLoading }}>{children}</RoutesContext.Provider>;
});
RoutesProvider.displayName = 'RoutesProvider';
export const useRouteProvider = () => {
const context = useContext<MapContextProps>(RoutesContext);
const context = useContext<RoutesProviderInnerProps>(RoutesContext);
return context;
};

View File

@@ -2,13 +2,14 @@ import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import {
LayoutEventBlocker,
LoadingWrapper,
SystemViewStandalone,
TooltipPosition,
WdCheckbox,
WdImgButton,
} from '@/hooks/Mapper/components/ui-kit';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
import { MouseEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { forwardRef, MouseEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { getSystemById } from '@/hooks/Mapper/helpers/getSystemById.ts';
import classes from './RoutesWidget.module.scss';
import { useLoadRoutes } from './hooks';
@@ -25,7 +26,10 @@ import {
AddSystemDialog,
SearchOnSubmitCallback,
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
import { OutCommand } from '@/hooks/Mapper/types';
import {
RoutesImperativeHandle,
RoutesWidgetProps,
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/types.ts';
const sortByDist = (a: Route, b: Route) => {
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
@@ -36,19 +40,16 @@ const sortByDist = (a: Route, b: Route) => {
export const RoutesWidgetContent = () => {
const {
data: { selectedSystems, hubs = [], systems, routes },
outCommand,
data: { selectedSystems, systems, isSubscriptionActive },
} = useMapRootState();
const { hubs = [], routesList, isRestricted } = useRouteProvider();
const [systemId] = selectedSystems;
const { loading } = useLoadRoutes();
const { systems: systemStatics, loadSystems, lastUpdateKey } = useLoadSystemStatic({ systems: hubs ?? [] });
const { open, ...systemCtxProps } = useContextMenuSystemInfoHandlers({
outCommand,
hubs,
});
const { open, ...systemCtxProps } = useContextMenuSystemInfoHandlers();
const preparedHubs = useMemo(() => {
return hubs.map(x => {
@@ -61,20 +62,20 @@ export const RoutesWidgetContent = () => {
const preparedRoutes: Route[] = useMemo(() => {
return (
routes?.routes
routesList?.routes
.sort(sortByDist)
.filter(x => x.destination.toString() !== systemId)
// .filter(x => x.destination.toString() !== systemId)
.map(route => ({
...route,
mapped_systems:
route.systems?.map(solar_system_id =>
routes?.systems_static_data.find(
routesList?.systems_static_data.find(
system_static_data => system_static_data.solar_system_id === solar_system_id,
),
) ?? [],
})) ?? []
);
}, [routes?.routes, routes?.systems_static_data, systemId]);
}, [routesList?.routes, routesList?.systems_static_data, systemId]);
const refData = useRef({ open, loadSystems, preparedRoutes });
refData.current = { open, loadSystems, preparedRoutes };
@@ -97,9 +98,13 @@ export const RoutesWidgetContent = () => {
[handleClick],
);
if (loading) {
if (isRestricted && !isSubscriptionActive) {
return (
<div className="w-full h-full flex justify-center items-center select-none text-center">Loading routes...</div>
<div className="w-full h-full flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">
User Routes available with &#39;Active&#39; map subscription only (contact map administrators)
</span>
</div>
);
}
@@ -117,7 +122,7 @@ export const RoutesWidgetContent = () => {
return (
<>
{systemId !== undefined && routes && (
<LoadingWrapper loading={loading}>
<div className={clsx(classes.RoutesGrid, 'px-2 py-2')}>
{preparedRoutes.map(route => {
const sys = preparedHubs.find(x => x.solar_system_id === route.destination)!;
@@ -132,7 +137,11 @@ export const RoutesWidgetContent = () => {
<WdImgButton
className={clsx(PrimeIcons.BARS, classes.RemoveBtn)}
onClick={e => handleClick(e, route.destination.toString())}
tooltip={{ content: 'Click here to open system menu', position: TooltipPosition.top, offset: 10 }}
tooltip={{
content: 'Click here to open system menu',
position: TooltipPosition.top,
offset: 10,
}}
/>
<SystemViewStandalone
@@ -151,7 +160,7 @@ export const RoutesWidgetContent = () => {
);
})}
</div>
)}
</LoadingWrapper>
<ContextMenuSystemInfo
hubs={hubs}
@@ -165,15 +174,13 @@ export const RoutesWidgetContent = () => {
);
};
export const RoutesWidgetComp = () => {
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
const { data, update } = useRouteProvider();
const {
data: { hubs = [] },
outCommand,
} = useMapRootState();
type RoutesWidgetCompProps = {
title: ReactNode | string;
};
const preparedHubs = useMemo(() => hubs.map(x => parseInt(x)), [hubs]);
export const RoutesWidgetComp = ({ title }: RoutesWidgetCompProps) => {
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
const { data, update, addHubCommand } = useRouteProvider();
const isSecure = data.path_type === 'secure';
const handleSecureChange = useCallback(() => {
@@ -190,24 +197,15 @@ export const RoutesWidgetComp = () => {
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],
async item => addHubCommand(item.value.toString()),
[addHubCommand],
);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<span className="select-none">Routes</span>
<span className="select-none">{title}</span>
<LayoutEventBlocker className="flex items-center gap-2">
<WdImgButton
className={PrimeIcons.PLUS_CIRCLE}
@@ -231,6 +229,7 @@ export const RoutesWidgetComp = () => {
className={PrimeIcons.SLIDERS_H}
onClick={() => setRouteSettingsVisible(true)}
tooltip={{
position: TooltipPosition.top,
content: 'Click here to open Routes settings',
}}
/>
@@ -251,10 +250,13 @@ export const RoutesWidgetComp = () => {
);
};
export const RoutesWidget = () => {
return (
<RoutesProvider>
<RoutesWidgetComp />
</RoutesProvider>
);
};
export const RoutesWidget = forwardRef<RoutesImperativeHandle, RoutesWidgetProps & RoutesWidgetCompProps>(
({ title, ...props }, ref) => {
return (
<RoutesProvider {...props} ref={ref}>
<RoutesWidgetComp title={title} />
</RoutesProvider>
);
},
);
RoutesWidget.displayName = 'RoutesWidget';

View File

@@ -1,10 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { OutCommand } from '@/hooks/Mapper/types';
import { useCallback, useEffect, useRef } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import {
RoutesType,
useRouteProvider,
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
import { useRouteProvider } from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
import { RoutesType } from '@/hooks/Mapper/mapRootProvider/types.ts';
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T>();
@@ -17,12 +14,10 @@ function usePrevious<T>(value: T): T | undefined {
}
export const useLoadRoutes = () => {
const [loading, setLoading] = useState(false);
const { data: routesSettings } = useRouteProvider();
const { data: routesSettings, loadRoutesCommand, hubs, routesList, loading, setLoading } = useRouteProvider();
const {
outCommand,
data: { selectedSystems, hubs, systems, connections },
data: { selectedSystems, systems, connections },
} = useMapRootState();
const prevSys = usePrevious(systems);
@@ -31,17 +26,16 @@ export const useLoadRoutes = () => {
const loadRoutes = useCallback(
(systemId: string, routesSettings: RoutesType) => {
outCommand({
type: OutCommand.getRoutes,
data: {
system_id: systemId,
routes_settings: routesSettings,
},
});
loadRoutesCommand(systemId, routesSettings);
setLoading(true);
},
[outCommand],
[loadRoutesCommand],
);
useEffect(() => {
setLoading(false);
}, [routesList]);
useEffect(() => {
if (selectedSystems.length !== 1) {
return;

View File

@@ -0,0 +1,27 @@
import { RoutesType } from '@/hooks/Mapper/mapRootProvider/types.ts';
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
export type LoadRoutesCommand = (systemId: string, routesSettings: RoutesType) => Promise<void>;
export type AddHubCommand = (systemId: string) => Promise<void>;
export type ToggleHubCommand = (systemId: string) => Promise<void>;
export type RoutesWidgetProps = {
data: RoutesType;
update: (d: RoutesType) => void;
hubs: string[];
routesList: RoutesList | undefined;
loadRoutesCommand: LoadRoutesCommand;
addHubCommand: AddHubCommand;
toggleHubCommand: ToggleHubCommand;
isRestricted?: boolean;
};
export type RoutesProviderInnerProps = RoutesWidgetProps & {
loading: boolean;
setLoading(loading: boolean): void;
};
export type RoutesImperativeHandle = {
stopLoading: () => void;
};

View File

@@ -1,24 +1,24 @@
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { LayoutEventBlocker, SystemView, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { LayoutEventBlocker, SystemView, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { SystemInfoContent } from './SystemInfoContent';
import { PrimeIcons } from 'primereact/api';
import { useState, useCallback } from 'react';
import { useCallback, useState } from 'react';
import { SystemSettingsDialog } from '@/hooks/Mapper/components/mapInterface/components/SystemSettingsDialog/SystemSettingsDialog.tsx';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { ANOIK_ICON, DOTLAN_ICON, ZKB_ICON } from '@/hooks/Mapper/icons';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
export const SystemInfo = () => {
const [visible, setVisible] = useState(false);
const {
data: { selectedSystems, systems },
data: { selectedSystems },
} = useMapRootState();
const [systemId] = selectedSystems;
const sys = getSystemById(systems, systemId)!;
const { solar_system_name: solarSystemName } = sys?.system_static_info || {};
const systemStaticInfo = getSystemStaticInfo(systemId)!;
const { solar_system_name: solarSystemName } = systemStaticInfo || {};
const isNotSelectedSystem = selectedSystems.length !== 1;
@@ -42,7 +42,7 @@ export const SystemInfo = () => {
<WdImgButton
className="pi pi-pen-to-square"
onClick={() => setVisible(true)}
tooltip={{ content: 'Edit system name and description' }}
tooltip={{ position: TooltipPosition.top, content: 'Edit system name and description' }}
/>
</LayoutEventBlocker>
</div>

View File

@@ -3,6 +3,7 @@ import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormhol
import { useMemo } from 'react';
import { getSystemById, sortWHClasses } from '@/hooks/Mapper/helpers';
import { InfoDrawer, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
interface SystemInfoContentProps {
systemId: string;
@@ -14,9 +15,9 @@ export const SystemInfoContent = ({ systemId }: SystemInfoContentProps) => {
} = useMapRootState();
const sys = getSystemById(systems, systemId)! || {};
const systemStaticInfo = getSystemStaticInfo(systemId)!;
const { description } = sys;
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } =
sys.system_static_info || {};
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } = systemStaticInfo || {};
const isWH = isWormholeSpace(system_class);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);

View File

@@ -1,117 +0,0 @@
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';
export const SystemKills: React.FC = () => {
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 [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 = systems.find(
sys => sys.system_static_info.solar_system_id === 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, systems]);
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)}
/>
}
>
<div className="relative h-full">
{!isSubscriptionActive ? (
<div className="absolute inset-0 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="absolute inset-0 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="absolute inset-0 flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">
Loading Kills...
</span>
</div>
) : error ? (
<div className="absolute inset-0 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="absolute inset-0 flex items-center justify-center">
<span className="select-none text-center text-stone-400/80 text-sm">
No kills found
</span>
</div>
) : (
<div className="h-full overflow-y-auto">
<SystemKillsContent
key={settings.compact ? 'compact' : 'normal'}
kills={filteredKills}
systemNameMap={systemNameMap}
compact={settings.compact}
onlyOneSystem={!visible}
/>
</div>
)}
</div>
</Widget>
</div>
<KillsSettingsDialog
visible={settingsDialogVisible}
setVisible={setSettingsDialogVisible}
/>
</div>
);
};

View File

@@ -1,16 +0,0 @@
.TableRowCompact {
height: 8px;
max-height: 8px;
font-size: 12px !important;
line-height: 8px;
}
.Table {
font-size: 12px;
border-collapse: collapse;
}
.Tooltip {
white-space: pre-line;
line-height: 1.2rem;
}

View File

@@ -1,50 +0,0 @@
import React, { useMemo } from 'react';
import clsx from 'clsx';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { KillRow } from '../components/SystemKillsRow';
interface SystemKillsContentProps {
kills: DetailedKill[];
systemNameMap: Record<string, string>;
compact?: boolean;
onlyOneSystem?: boolean;
}
export const SystemKillsContent: React.FC<SystemKillsContentProps> = ({
kills,
systemNameMap,
compact = false,
onlyOneSystem = false,
}) => {
const sortedKills = useMemo(() => {
return [...kills].sort((a, b) => {
const timeA = a.kill_time ? new Date(a.kill_time).getTime() : 0;
const timeB = b.kill_time ? new Date(b.kill_time).getTime() : 0;
return timeB - timeA;
});
}, [kills]);
return (
<div
className={clsx(
'flex flex-col w-full text-stone-200 text-xs transition-all duration-300',
compact ? 'p-1' : 'p-1'
)}
>
{sortedKills.map(kill => {
const systemIdStr = String(kill.solar_system_id);
const systemName = systemNameMap[systemIdStr] || `System ${systemIdStr}`;
return (
<KillRow
key={kill.killmail_id}
killDetails={kill}
systemName={systemName}
isCompact={compact}
onlyOneSystem={onlyOneSystem}
/>
);
})}
</div>
);
};

View File

@@ -1,268 +0,0 @@
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 { VictimRowSubInfo } from './VictimRowSubInfo';
import { WdTooltipWrapper } from '../../../../ui-kit/WdTooltipWrapper';
import classes from './SystemKillRow.module.scss';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
export interface FullKillRowProps {
killDetails: DetailedKill;
systemName: string;
onlyOneSystem: boolean;
}
export const FullKillRow: React.FC<FullKillRowProps> = ({
killDetails,
systemName,
onlyOneSystem,
}) => {
const {
killmail_id = 0,
// Victim data
victim_char_name = '',
victim_alliance_ticker = '',
victim_corp_ticker = '',
victim_ship_name = '',
victim_char_id = 0,
victim_corp_id = 0,
victim_alliance_id = 0,
victim_ship_type_id = 0,
victim_corp_name = '',
victim_alliance_name = '',
// Attacker data
final_blow_char_id = 0,
final_blow_char_name = '',
final_blow_alliance_ticker = '',
final_blow_corp_ticker = '',
final_blow_corp_name = '',
final_blow_alliance_name = '',
final_blow_corp_id = 0,
final_blow_alliance_id = 0,
final_blow_ship_name = '',
final_blow_ship_type_id = 0,
total_value = 0,
kill_time = '',
} = killDetails || {};
const attackerIsNpc = final_blow_char_id === 0;
const victimAffiliation = victim_alliance_ticker || victim_corp_ticker || null;
const attackerAffiliation = attackerIsNpc
? ''
: final_blow_alliance_ticker || final_blow_corp_ticker || '';
const killValueFormatted =
total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
const killTimeAgo = kill_time ? formatTimeMixed(kill_time) : '0h ago';
// Build victim images
const {
victimPortraitUrl,
victimCorpLogoUrl,
victimAllianceLogoUrl,
victimShipUrl,
} = buildVictimImageUrls({
victim_char_id,
victim_ship_type_id,
victim_corp_id,
victim_alliance_id,
});
// Build attacker images
const {
attackerPortraitUrl,
attackerCorpLogoUrl,
attackerAllianceLogoUrl,
} = buildAttackerImageUrls({
final_blow_char_id,
final_blow_corp_id,
final_blow_alliance_id,
});
// Primary image for victim
const { url: victimPrimaryImageUrl, tooltip: victimPrimaryTooltip } =
getPrimaryLogoAndTooltip(
victimAllianceLogoUrl,
victimCorpLogoUrl,
victim_alliance_name,
victim_corp_name,
'Victim'
);
// Primary image for attacker
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } =
getAttackerPrimaryImageAndTooltip(
attackerIsNpc,
attackerAllianceLogoUrl,
attackerCorpLogoUrl,
final_blow_alliance_name,
final_blow_corp_name,
final_blow_ship_type_id
);
const attackerSubscript = getAttackerSubscript(killDetails);
return (
<div
className={clsx(
classes.killRowContainer,
'w-full text-sm py-1 px-2',
'flex flex-col sm:flex-row'
)}
>
<div className="w-full flex flex-col sm:flex-row items-start gap-2">
{/* Victim Section */}
<div className="flex items-start gap-1 min-w-0">
{victimShipUrl && (
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={victimShipUrl}
alt="VictimShip"
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
</a>
</div>
)}
{victimPrimaryImageUrl && (
<WdTooltipWrapper
content={victimPrimaryTooltip}
position={TooltipPosition.top}
>
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={victimPrimaryImageUrl}
alt="VictimPrimaryLogo"
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
</a>
</div>
</WdTooltipWrapper>
)}
<VictimRowSubInfo
victimCharName={victim_char_name}
victimCharacterId={victim_char_id}
victimPortraitUrl={victimPortraitUrl}
/>
<div className="flex flex-col text-stone-200 leading-4 min-w-0 overflow-hidden">
<div className="truncate font-semibold">
{victim_char_name}
{victimAffiliation && (
<span className="ml-1 text-stone-400">/ {victimAffiliation}</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 className="truncate text-stone-400">
{!onlyOneSystem && systemName && <span>{systemName}</span>}
</div>
</div>
</div>
{/* Attacker Section */}
<div className="flex items-start gap-1 min-w-0 sm:ml-auto">
<div className="flex flex-col items-end leading-4 min-w-0 overflow-hidden text-right">
{!attackerIsNpc && (
<div className="truncate font-semibold">
{final_blow_char_name}
{attackerAffiliation && (
<span className="ml-1 text-stone-400">/ {attackerAffiliation}</span>
)}
</div>
)}
{!attackerIsNpc && final_blow_ship_name && (
<div className="truncate text-stone-300">{final_blow_ship_name}</div>
)}
<div className="truncate text-red-400">{killTimeAgo}</div>
</div>
{(!attackerIsNpc && attackerPortraitUrl && final_blow_char_id > 0) && (
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
<a
href={zkillLink('character', final_blow_char_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={attackerPortraitUrl}
alt="AttackerPortrait"
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
</a>
</div>
)}
{attackerPrimaryImageUrl && (
<WdTooltipWrapper
content={attackerPrimaryTooltip}
position={TooltipPosition.top}
>
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={attackerPrimaryImageUrl}
alt={attackerIsNpc ? 'NpcShip' : 'AttackerPrimaryLogo'}
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
{attackerSubscript && (
<span
className={clsx(
attackerSubscript.cssClass,
classes.attackerCountLabel
)}
>
{attackerSubscript.label}
</span>
)}
</a>
</div>
</WdTooltipWrapper>
)}
</div>
</div>
</div>
);
};

View File

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

View File

@@ -1,39 +0,0 @@
import React from 'react';
import clsx from 'clsx';
import { zkillLink } from '../helpers';
import classes from './SystemKillRow.module.scss';
interface VictimRowSubInfoProps {
victimCharacterId: number | null;
victimPortraitUrl: string | null;
victimCharName?: string;
}
export const VictimRowSubInfo: React.FC<VictimRowSubInfoProps> = ({
victimCharacterId = 0,
victimPortraitUrl,
victimCharName,
}) => {
if (!victimPortraitUrl || !victimCharacterId || victimCharacterId <= 0) {
return null;
}
return (
<div className="flex items-start gap-1">
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
<a
href={zkillLink('character', victimCharacterId)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={victimPortraitUrl}
alt={victimCharName || 'Victim Portrait'}
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
</a>
</div>
</div>
);
};

View File

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

View File

@@ -1,15 +1,36 @@
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { renderIcon } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
import { getCharacterPortraitUrl } from '@/hooks/Mapper/helpers';
export interface SignatureViewProps {}
export interface SignatureViewProps {
signature: SystemSignature;
showCharacterPortrait?: boolean;
}
export const SignatureView = ({ signature, showCharacterPortrait = false }: SignatureViewProps) => {
const isWormhole = signature?.group === SignatureGroup.Wormhole;
const hasCharacterInfo = showCharacterPortrait && signature.character_eve_id;
const groupDisplay = isWormhole ? SignatureGroup.Wormhole : signature?.group ?? SignatureGroup.CosmicSignature;
const characterName = signature.character_name || 'Unknown character';
export const SignatureView = (sig: SignatureViewProps & SystemSignature) => {
return (
<div className="flex gap-2 items-center">
{renderIcon(sig)}
<div>{sig?.eve_id}</div>
<div>{sig?.group ?? SignatureGroup.CosmicSignature}</div>
<div>{sig?.name}</div>
<div className="flex flex-col gap-2">
<div className="flex gap-2 items-center px-2">
{renderIcon(signature)}
<div>{signature?.eve_id}</div>
<div>{groupDisplay}</div>
{!isWormhole && <div>{signature?.name}</div>}
{hasCharacterInfo && (
<div className="flex items-center gap-1 ml-2 pl-2 border-l border-stone-700">
<img
src={getCharacterPortraitUrl(signature.character_eve_id)}
alt={characterName}
className="w-5 h-5 rounded-sm border border-stone-700"
/>
<div className="text-xs text-stone-300">{characterName}</div>
</div>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,113 @@
import { useRef } from 'react';
import {
InfoDrawer,
LayoutEventBlocker,
SystemView,
TooltipPosition,
WdCheckbox,
WdImgButton,
} from '@/hooks/Mapper/components/ui-kit';
import { PrimeIcons } from 'primereact/api';
import { CheckboxChangeEvent } from 'primereact/checkbox';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { COMPACT_MAX_WIDTH } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export type HeaderProps = {
sigCount: number;
lazyDeleteValue: boolean;
onLazyDeleteChange: (checked: boolean) => void;
pendingCount: number;
undoCountdown?: number;
onUndoClick: () => void;
onSettingsClick: () => void;
};
export const SystemSignaturesHeader = ({
sigCount,
lazyDeleteValue,
onLazyDeleteChange,
pendingCount,
undoCountdown,
onUndoClick,
onSettingsClick,
}: HeaderProps) => {
const {
data: { selectedSystems },
} = useMapRootState();
const [systemId] = selectedSystems;
const isNotSelectedSystem = selectedSystems.length !== 1;
const containerRef = useRef<HTMLDivElement>(null);
const isCompact = useMaxWidth(containerRef, COMPACT_MAX_WIDTH);
return (
<div ref={containerRef} className="w-full">
<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 deletions (${pendingCount})${undoCountdown && undoCountdown > 0 ? `${undoCountdown}s left` : ''}`,
}}
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>
</div>
);
};

View File

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

View File

@@ -1,79 +0,0 @@
.verticalTabsContainer {
display: flex;
width: 100%;
min-height: 300px;
:global {
.p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
}
.p-tabview-nav-container {
border-right: none;
height: 100%;
}
.p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition:
background-color 200ms,
border-right-color 200ms;
&:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.p-tabview-nav-link {
transition: color 200ms;
justify-content: flex-end;
padding: 10px;
//background-color: var(--surface-card);
background-color: initial;
border: none;
color: var(--gray-400);
border-radius: initial;
font-weight: 400;
margin: 0;
}
&.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
.p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
&:hover {
//background-color: var(--surface-hover);
border-right: 4px solid var(--primary-color);
}
}
}
}
.p-tabview-panel {
flex-grow: 1;
}
}
}

View File

@@ -2,22 +2,18 @@ import { Dialog } from 'primereact/dialog';
import { useCallback, useState } from 'react';
import { Button } from 'primereact/button';
import { TabPanel, TabView } from 'primereact/tabview';
import styles from './SystemSignatureSettingsDialog.module.scss';
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
export type Setting = { key: string; name: string; value: boolean; isFilter?: boolean };
export const COSMIC_SIGNATURE = 'Cosmic Signature';
export const COSMIC_ANOMALY = 'Cosmic Anomaly';
export const DEPLOYABLE = 'Deployable';
export const STRUCTURE = 'Structure';
export const STARBASE = 'Starbase';
export const SHIP = 'Ship';
export const DRONE = 'Drone';
import { Dropdown } from 'primereact/dropdown';
import {
Setting,
SettingsTypes,
SIGNATURE_SETTINGS,
SignatureSettingsType,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
interface SystemSignatureSettingsDialogProps {
settings: Setting[];
onSave: (settings: Setting[]) => void;
settings: SignatureSettingsType;
onSave: (settings: SignatureSettingsType) => void;
onCancel: () => void;
}
@@ -27,59 +23,72 @@ export const SystemSignatureSettingsDialog = ({
onCancel,
}: SystemSignatureSettingsDialogProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const [settings, setSettings] = useState<Setting[]>(defaultSettings);
const [settings, setSettings] = useState<SignatureSettingsType>(defaultSettings);
const filterSettings = settings.filter(setting => setting.isFilter);
const userSettings = settings.filter(setting => !setting.isFilter);
const handleSettingsChange = (key: string) => {
setSettings(prevState => prevState.map(item => (item.key === key ? { ...item, value: !item.value } : item)));
const handleSettingsChange = ({ key, type }: Setting, value?: unknown) => {
setSettings(prev => {
switch (type) {
case SettingsTypes.dropdown:
return { ...prev, [key]: value };
case SettingsTypes.flag:
return { ...prev, [key]: !prev[key] };
}
return prev;
});
};
const handleSave = useCallback(() => {
onSave(settings);
}, [onSave, settings]);
const renderSetting = (setting: Setting) => {
const val = settings[setting.key];
if (setting.options) {
return (
<div key={setting.key} className="flex items-center justify-between gap-2 mb-2">
<label className="text-[#b8b8b8] text-[13px] select-none">{setting.name}</label>
<Dropdown
value={val}
options={setting.options}
onChange={e => handleSettingsChange(setting, e.value)}
className="w-40"
/>
</div>
);
}
return (
<PrettySwitchbox
key={setting.key}
label={setting.name}
checked={!!val}
setChecked={() => handleSettingsChange(setting)}
/>
);
};
return (
<Dialog header="System Signatures Settings" visible={true} onHide={onCancel} className="w-full max-w-lg h-[500px]">
<div className="flex flex-col gap-3 justify-between h-full">
<div className="flex flex-col gap-2">
<div className={styles.verticalTabsContainer}>
<TabView
activeIndex={activeIndex}
onTabChange={e => setActiveIndex(e.index)}
className={styles.verticalTabView}
>
<TabPanel header="Filters" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{filterSettings.map(setting => {
return (
<PrettySwitchbox
key={setting.key}
label={setting.name}
checked={setting.value}
setChecked={() => handleSettingsChange(setting.key)}
/>
);
})}
</div>
</TabPanel>
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{userSettings.map(setting => {
return (
<PrettySwitchbox
key={setting.key}
label={setting.name}
checked={setting.value}
setChecked={() => handleSettingsChange(setting.key)}
/>
);
})}
</div>
</TabPanel>
</TabView>
</div>
<TabView
activeIndex={activeIndex}
onTabChange={e => setActiveIndex(e.index)}
className="vertical-tabs-container"
>
<TabPanel header="Filters">
<div className="w-full h-full flex flex-col gap-1">
{SIGNATURE_SETTINGS.filterFlags.map(renderSetting)}
</div>
</TabPanel>
<TabPanel header="User Interface">
<div className="w-full h-full flex flex-col gap-1">
{SIGNATURE_SETTINGS.uiFlags.map(renderSetting)}
<div className="my-2 border-t border-stone-700/50"></div>
{SIGNATURE_SETTINGS.uiOther.map(renderSetting)}
</div>
</TabPanel>
</TabView>
</div>
<div className="flex gap-2 justify-end">

View File

@@ -1,180 +1,195 @@
import { useCallback, useState, useEffect, useRef, useMemo } from 'react';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import {
InfoDrawer,
LayoutEventBlocker,
SystemView,
TooltipPosition,
WdCheckbox,
WdImgButton,
} from '@/hooks/Mapper/components/ui-kit';
import { SystemSignaturesContent } from './SystemSignaturesContent';
import {
COSMIC_ANOMALY,
COSMIC_SIGNATURE,
DEPLOYABLE,
DRONE,
Setting,
SHIP,
STARBASE,
STRUCTURE,
SystemSignatureSettingsDialog,
} from './SystemSignatureSettingsDialog';
import { SignatureGroup } from '@/hooks/Mapper/types';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { PrimeIcons } from 'primereact/api';
import { SystemSignatureSettingsDialog } from './SystemSignatureSettingsDialog';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { CheckboxChangeEvent } from 'primereact/checkbox';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { SystemSignaturesHeader } from './SystemSignatureHeader';
import useLocalStorageState from 'use-local-storage-state';
import { useHotkey } from '@/hooks/Mapper/hooks/useHotkey';
import {
SETTINGS_KEYS,
SETTINGS_VALUES,
SIGNATURE_SETTING_STORE_KEY,
SIGNATURE_WINDOW_ID,
SignatureSettingsType,
SIGNATURES_DELETION_TIMING,
SIGNATURE_DELETION_TIMEOUTS,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers';
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v5_2';
export const SHOW_DESCRIPTION_COLUMN_SETTING = 'show_description_column_setting';
export const SHOW_UPDATED_COLUMN_SETTING = 'SHOW_UPDATED_COLUMN_SETTING';
export const LAZY_DELETE_SIGNATURES_SETTING = 'LAZY_DELETE_SIGNATURES_SETTING';
export const KEEP_LAZY_DELETE_SETTING = 'KEEP_LAZY_DELETE_ENABLED_SETTING';
/**
* Custom hook for managing pending signature deletions and undo countdown.
*/
function useSignatureUndo(
systemId: string | undefined,
settings: SignatureSettingsType,
outCommand: OutCommandHandler,
) {
const [countdown, setCountdown] = useState<number>(0);
const [pendingIds, setPendingIds] = useState<Set<string>>(new Set());
const intervalRef = useRef<number | null>(null);
const settings: Setting[] = [
{ key: SHOW_UPDATED_COLUMN_SETTING, name: 'Show Updated Column', value: false, isFilter: false },
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: false, isFilter: false },
{ key: LAZY_DELETE_SIGNATURES_SETTING, name: 'Lazy Delete Signatures', value: false, isFilter: false },
{ key: KEEP_LAZY_DELETE_SETTING, name: 'Keep "Lazy Delete" Enabled', value: false, isFilter: false },
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true, isFilter: true },
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true, isFilter: true },
{ key: DEPLOYABLE, name: 'Show Deployables', value: true, isFilter: true },
{ key: STRUCTURE, name: 'Show Structures', value: true, isFilter: true },
{ key: STARBASE, name: 'Show Starbase', value: true, isFilter: true },
{ key: SHIP, name: 'Show Ships', value: true, isFilter: true },
{ key: DRONE, name: 'Show Drones And Charges', value: true, isFilter: true },
{ key: SignatureGroup.Wormhole, name: 'Show Wormholes', value: true, isFilter: true },
{ key: SignatureGroup.RelicSite, name: 'Show Relic Sites', value: true, isFilter: true },
{ key: SignatureGroup.DataSite, name: 'Show Data Sites', value: true, isFilter: true },
{ key: SignatureGroup.OreSite, name: 'Show Ore Sites', value: true, isFilter: true },
{ key: SignatureGroup.GasSite, name: 'Show Gas Sites', value: true, isFilter: true },
{ key: SignatureGroup.CombatSite, name: 'Show Combat Sites', value: true, isFilter: true },
];
const defaultSettings = () => {
return [...settings];
};
export const SystemSignatures = () => {
const {
data: { selectedSystems },
} = useMapRootState();
const [visible, setVisible] = useState(false);
const [settings, setSettings] = useState<Setting[]>(defaultSettings);
const [systemId] = selectedSystems;
const isNotSelectedSystem = selectedSystems.length !== 1;
const lazyDeleteValue = useMemo(() => {
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!.value;
}, [settings]);
const handleSettingsChange = useCallback((settings: Setting[]) => {
setSettings(settings);
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
setVisible(false);
}, []);
const handleLazyDeleteChange = useCallback((value: boolean) => {
setSettings(settings => {
const lazyDelete = settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!;
lazyDelete.value = value;
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
return [...settings];
const addDeleted = useCallback((ids: string[]) => {
setPendingIds(prev => {
const next = new Set(prev);
ids.forEach(id => next.add(id));
return next;
});
}, []);
// kick off or clear countdown whenever pendingIds changes
useEffect(() => {
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
if (restoredSettings) {
setSettings(JSON.parse(restoredSettings));
// clear any existing timer
if (intervalRef.current != null) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
if (pendingIds.size === 0) {
setCountdown(0);
return;
}
// determine timeout from settings
const timingKey = Number(settings[SETTINGS_KEYS.DELETION_TIMING] ?? SIGNATURES_DELETION_TIMING.DEFAULT);
const timeoutMs =
Number(SIGNATURE_DELETION_TIMEOUTS[timingKey as keyof typeof SIGNATURE_DELETION_TIMEOUTS]) || 10000;
setCountdown(Math.ceil(timeoutMs / 1000));
// start new interval
intervalRef.current = window.setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
clearInterval(intervalRef.current!);
intervalRef.current = null;
setPendingIds(new Set());
return 0;
}
return prev - 1;
});
}, 1000);
return () => {
if (intervalRef.current != null) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, [pendingIds, settings[SETTINGS_KEYS.DELETION_TIMING]]);
// undo handler
const handleUndo = useCallback(async () => {
if (!systemId || pendingIds.size === 0) return;
await outCommand({
type: OutCommand.undoDeleteSignatures,
data: { system_id: systemId, eve_ids: Array.from(pendingIds) },
});
setPendingIds(new Set());
setCountdown(0);
if (intervalRef.current != null) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}, [systemId, pendingIds, outCommand]);
return {
pendingIds,
countdown,
addDeleted,
handleUndo,
};
}
export const SystemSignatures = () => {
const [visible, setVisible] = useState(false);
const [sigCount, setSigCount] = useState(0);
const {
data: { selectedSystems },
outCommand,
} = useMapRootState();
const [currentSettings, setCurrentSettings] = useLocalStorageState<SignatureSettingsType>(
SIGNATURE_SETTING_STORE_KEY,
{
defaultValue: SETTINGS_VALUES,
},
);
const [systemId] = selectedSystems;
const isSystemSelected = useMemo(() => selectedSystems.length === 1, [selectedSystems.length]);
const { pendingIds, countdown, addDeleted, handleUndo } = useSignatureUndo(systemId, currentSettings, outCommand);
useHotkey(true, ['z', 'Z'], (event: KeyboardEvent) => {
if (pendingIds.size > 0 && countdown > 0) {
event.preventDefault();
event.stopPropagation();
handleUndo();
}
});
const handleCountChange = useCallback((count: number) => {
setSigCount(count);
}, []);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 260);
const handleSettingsSave = useCallback(
(newSettings: SignatureSettingsType) => {
setCurrentSettings(newSettings);
setVisible(false);
},
[setCurrentSettings],
);
const handleLazyDeleteToggle = useCallback(
(value: boolean) => {
setCurrentSettings(prev => ({
...prev,
[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES]: value,
}));
},
[setCurrentSettings],
);
const openSettings = useCallback(() => setVisible(true), []);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
<div className="flex justify-between items-center gap-1">
{!compact && (
<div className="flex whitespace-nowrap text-ellipsis overflow-hidden text-stone-400">
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={compact ? '' : 'Lazy delete'}
value={lazyDeleteValue}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300 whitespace-nowrap text-ellipsis overflow-hidden"
onChange={(event: CheckboxChangeEvent) => handleLazyDeleteChange(!!event.checked)}
/>
</WdTooltipWrapper>
<WdImgButton
className={PrimeIcons.QUESTION_CIRCLE}
tooltip={{
position: TooltipPosition.left,
// @ts-ignore
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 select one or more signatures <br /> in 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 select any signature need click on that, <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>}>
For delete any signature first of all you need select before
<br /> and then use <b className="text-sky-500">Del</b>
</InfoDrawer>
</div>
) as React.ReactNode,
}}
/>
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setVisible(true)} />
</LayoutEventBlocker>
</div>
<SystemSignaturesHeader
sigCount={sigCount}
lazyDeleteValue={currentSettings[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES] as boolean}
pendingCount={pendingIds.size}
undoCountdown={countdown}
onLazyDeleteChange={handleLazyDeleteToggle}
onUndoClick={handleUndo}
onSettingsClick={openSettings}
/>
}
windowId={SIGNATURE_WINDOW_ID}
>
{isNotSelectedSystem ? (
{!isSystemSelected ? (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
System is not selected
</div>
) : (
<SystemSignaturesContent systemId={systemId} settings={settings} onLazyDeleteChange={handleLazyDeleteChange} />
<SystemSignaturesContent
systemId={systemId}
settings={currentSettings}
onLazyDeleteChange={handleLazyDeleteToggle}
onCountChange={handleCountChange}
onSignatureDeleted={addDeleted}
/>
)}
{visible && (
<SystemSignatureSettingsDialog
settings={settings}
settings={currentSettings}
onCancel={() => setVisible(false)}
onSave={handleSettingsChange}
onSave={handleSettingsSave}
/>
)}
</Widget>
);
};
export default SystemSignatures;

View File

@@ -1,10 +0,0 @@
.TableRowCompact {
height: 8px;
max-height: 8px;
font-size: 12px !important;
line-height: 8px;
}
.Table {
}

View File

@@ -1,29 +1,30 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { parseSignatures } from '@/hooks/Mapper/helpers';
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import {
getGroupIdByRawGroup,
GROUPS_LIST,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
import { PrimeIcons } from 'primereact/api';
import { Column } from 'primereact/column';
import {
DataTable,
DataTableRowClickEvent,
DataTableRowMouseEvent,
DataTableStateEvent,
SortOrder,
} from 'primereact/datatable';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import useRefState from 'react-usestateref';
import { Setting } from '../SystemSignatureSettingsDialog';
import { useHotkey } from '@/hooks/Mapper/hooks';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
import useLocalStorageState from 'use-local-storage-state';
import classes from './SystemSignaturesContent.module.scss';
import clsx from 'clsx';
import { SystemSignature } from '@/hooks/Mapper/types';
import { SignatureView } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SignatureView';
import {
getActualSigs,
getRowColorByTimeLeft,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
COMPACT_MAX_WIDTH,
getGroupIdByRawGroup,
GROUPS_LIST,
MEDIUM_MAX_WIDTH,
OTHER_COLUMNS_WIDTH,
SETTINGS_KEYS,
SIGNATURE_WINDOW_ID,
SignatureSettingsType,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants';
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
import { TooltipPosition, WdTooltip, WdTooltipHandlers, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { ExtendedSystemSignature, SignatureGroup, SignatureKind, SystemSignature } from '@/hooks/Mapper/types';
import {
renderAddedTimeLeft,
renderDescription,
@@ -31,18 +32,13 @@ import {
renderInfoColumn,
renderUpdatedTimeLeft,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
import useLocalStorageState from 'use-local-storage-state';
import { PrimeIcons } from 'primereact/api';
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
import { useMapEventListener } from '@/hooks/Mapper/events';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
import {
SHOW_DESCRIPTION_COLUMN_SETTING,
SHOW_UPDATED_COLUMN_SETTING,
LAZY_DELETE_SIGNATURES_SETTING,
KEEP_LAZY_DELETE_SETTING,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures';
import { useClipboard, useHotkey } from '@/hooks/Mapper/hooks';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth';
import { getSignatureRowClass } from '../helpers/rowStyles';
import { useSystemSignaturesData } from '../hooks/useSystemSignaturesData';
const renderColIcon = (sig: SystemSignature) => renderIcon(sig);
type SystemSignaturesSortSettings = {
sortField: string;
sortOrder: SortOrder;
@@ -55,12 +51,16 @@ const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
interface SystemSignaturesContentProps {
systemId: string;
settings: Setting[];
settings: SignatureSettingsType;
hideLinkedSignatures?: boolean;
selectable?: boolean;
onSelect?: (signature: SystemSignature) => void;
onLazyDeleteChange?: (value: boolean) => void;
onCountChange?: (count: number) => void;
filterSignature?: (signature: SystemSignature) => boolean;
onSignatureDeleted?: (deletedIds: string[]) => void;
}
export const SystemSignaturesContent = ({
systemId,
settings,
@@ -68,378 +68,319 @@ export const SystemSignaturesContent = ({
selectable,
onSelect,
onLazyDeleteChange,
onCountChange,
filterSignature,
onSignatureDeleted,
}: SystemSignaturesContentProps) => {
const { outCommand } = useMapRootState();
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
const [selectedSignatureForDialog, setSelectedSignatureForDialog] = useState<SystemSignature | null>(null);
const [showSignatureSettings, setShowSignatureSettings] = useState(false);
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
const [sortSettings, setSortSettings] = useLocalStorageState<SystemSignaturesSortSettings>('window:signatures:sort', {
defaultValue: SORT_DEFAULT_VALUES,
});
const [hoveredSignature, setHoveredSignature] = useState<SystemSignature | null>(null);
const tableRef = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(tableRef, 260);
const medium = useMaxWidth(tableRef, 380);
const refData = useRef({ selectable });
refData.current = { selectable };
const tooltipRef = useRef<WdTooltipHandlers>(null);
const isCompact = useMaxWidth(tableRef, COMPACT_MAX_WIDTH);
const isMedium = useMaxWidth(tableRef, MEDIUM_MAX_WIDTH);
const { clipboardContent, setClipboardContent } = useClipboard();
const lazyDeleteValue = useMemo(() => {
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)?.value ?? false;
}, [settings]);
const keepLazyDeleteValue = useMemo(() => {
return settings.find(setting => setting.key === KEEP_LAZY_DELETE_SETTING)?.value ?? false;
}, [settings]);
const handleResize = useCallback(() => {
if (tableRef.current) {
const tableWidth = tableRef.current.offsetWidth;
const otherColumnsWidth = 276;
const availableWidth = tableWidth - otherColumnsWidth;
setNameColumnWidth(`${availableWidth}px`);
}
}, []);
const groupSettings = useMemo(() => settings.filter(s => (GROUPS_LIST as string[]).includes(s.key)), [settings]);
const showDescriptionColumn = useMemo(
() => settings.find(s => s.key === SHOW_DESCRIPTION_COLUMN_SETTING)?.value,
[settings],
const [sortSettings, setSortSettings] = useLocalStorageState<{ sortField: string; sortOrder: SortOrder }>(
'window:signatures:sort',
{ defaultValue: SORT_DEFAULT_VALUES },
);
const showUpdatedColumn = useMemo(() => settings.find(s => s.key === SHOW_UPDATED_COLUMN_SETTING)?.value, [settings]);
const filteredSignatures = useMemo(() => {
return signatures
.filter(x => {
if (hideLinkedSignatures && !!x.linked_system) {
return false;
}
const isCosmicSignature = x.kind === COSMIC_SIGNATURE;
const preparedGroup = getGroupIdByRawGroup(x.group);
if (isCosmicSignature) {
const showCosmicSignatures = settings.find(y => y.key === COSMIC_SIGNATURE)?.value;
if (showCosmicSignatures) {
return !x.group || groupSettings.find(y => y.key === preparedGroup)?.value;
} else {
return !!x.group && groupSettings.find(y => y.key === preparedGroup)?.value;
}
}
return settings.find(y => y.key === x.kind)?.value;
})
.sort((a, b) => {
return new Date(b.updated_at || 0).getTime() - new Date(a.updated_at || 0).getTime();
});
}, [signatures, settings, groupSettings, hideLinkedSignatures]);
const handleGetSignatures = useCallback(async () => {
const { signatures } = await outCommand({
type: OutCommand.getSignatures,
data: { system_id: systemId },
});
setSignatures(signatures);
}, [outCommand, systemId]);
const handleUpdateSignatures = useCallback(
async (newSignatures: SystemSignature[], updateOnly: boolean, skipUpdateUntouched?: boolean) => {
const { added, updated, removed } = getActualSigs(
signaturesRef.current,
newSignatures,
updateOnly,
skipUpdateUntouched,
);
const { signatures: updatedSignatures } = await outCommand({
type: OutCommand.updateSignatures,
data: {
system_id: systemId,
added,
updated,
removed,
},
});
setSignatures(() => updatedSignatures);
setSelectedSignatures([]);
},
[outCommand, systemId],
);
const handleDeleteSelected = useCallback(
async (e: KeyboardEvent) => {
if (selectable) {
return;
}
if (selectedSignatures.length === 0) {
return;
}
e.preventDefault();
e.stopPropagation();
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
await handleUpdateSignatures(
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
false,
true,
);
},
[handleUpdateSignatures, selectable, signatures, selectedSignatures],
);
const handleSelectAll = useCallback(() => {
setSelectedSignatures(signatures);
}, [signatures]);
const handleSelectSignatures = useCallback(
// TODO still will be good to define types if we use typescript
// @ts-ignore
e => {
if (selectable) {
onSelect?.(e.value);
} else {
setSelectedSignatures(e.value);
}
},
[onSelect, selectable],
);
const handlePaste = async (clipboardContent: string) => {
const newSignatures = parseSignatures(
clipboardContent,
settings.map(x => x.key),
);
handleUpdateSignatures(newSignatures, !lazyDeleteValue);
if (lazyDeleteValue && !keepLazyDeleteValue) {
onLazyDeleteChange?.(false);
}
};
const handleEnterRow = useCallback(
(e: DataTableRowMouseEvent) => {
setHoveredSig(filteredSignatures[e.index]);
tooltipRef.current?.show(e.originalEvent);
},
[filteredSignatures],
);
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
tooltipRef.current?.hide(e.originalEvent);
setHoveredSig(null);
}, []);
useEffect(() => {
if (refData.current.selectable) {
return;
}
if (!clipboardContent?.text) {
return;
}
handlePaste(clipboardContent.text);
setClipboardContent(null);
}, [clipboardContent, selectable, lazyDeleteValue, keepLazyDeleteValue]);
useHotkey(true, ['a'], handleSelectAll);
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
useEffect(() => {
if (!systemId) {
setSignatures([]);
return;
}
handleGetSignatures();
}, [systemId]);
useMapEventListener(event => {
switch (event.name) {
case Commands.signaturesUpdated:
if (event.data?.toString() !== systemId.toString()) {
return;
}
handleGetSignatures();
return true;
}
const {
signatures,
selectedSignatures,
setSelectedSignatures,
handleDeleteSelected,
handleSelectAll,
handlePaste,
hasUnsupportedLanguage,
} = useSystemSignaturesData({
systemId,
settings,
onCountChange,
onLazyDeleteChange,
onSignatureDeleted,
});
useEffect(() => {
const observer = new ResizeObserver(handleResize);
if (tableRef.current) {
observer.observe(tableRef.current);
if (selectable) return;
if (!clipboardContent?.text) return;
handlePaste(clipboardContent.text);
setClipboardContent(null);
}, [selectable, clipboardContent, handlePaste, setClipboardContent]);
useHotkey(true, ['a'], handleSelectAll);
useHotkey(false, ['Backspace', 'Delete'], (event: KeyboardEvent) => {
const targetWindow = (event.target as HTMLHtmlElement)?.closest(`[data-window-id="${SIGNATURE_WINDOW_ID}"]`);
if (!targetWindow) {
return;
}
handleResize(); // Call on mount to set initial width
event.preventDefault();
event.stopPropagation();
if (onSignatureDeleted && selectedSignatures.length > 0) {
const deletedIds = selectedSignatures.map(s => s.eve_id);
onSignatureDeleted(deletedIds);
}
handleDeleteSelected();
});
return () => {
if (tableRef.current) {
observer.unobserve(tableRef.current);
}
};
const handleResize = useCallback(() => {
if (!tableRef.current) return;
setNameColumnWidth(`${tableRef.current.offsetWidth - OTHER_COLUMNS_WIDTH}px`);
}, []);
const renderToolbar = (/*row: SystemSignature*/) => {
return (
<div className="flex justify-end items-center gap-2 mr-[4px]">
<WdTooltipWrapper content="To Edit Signature do double click">
<span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span>
</WdTooltipWrapper>
</div>
);
};
useEffect(() => {
if (!tableRef.current) return;
const [showSignatureSettings, setShowSignatureSettings] = useState(false);
const observer = new ResizeObserver(handleResize);
observer.observe(tableRef.current);
handleResize();
const handleRowClick = (e: DataTableRowClickEvent) => {
setSelectedSignature(e.data as SystemSignature);
return () => {
observer.disconnect();
};
}, [handleResize]);
const handleRowClick = useCallback((e: DataTableRowClickEvent) => {
setSelectedSignatureForDialog(e.data as SystemSignature);
setShowSignatureSettings(true);
};
}, []);
const handleSelectSignatures = useCallback(
(e: { value: SystemSignature[] }) => {
selectable ? onSelect?.(e.value[0]) : setSelectedSignatures(e.value as ExtendedSystemSignature[]);
},
[onSelect, selectable, setSelectedSignatures],
);
const { showDescriptionColumn, showUpdatedColumn, showCharacterColumn, showCharacterPortrait } = useMemo(
() => ({
showDescriptionColumn: settings[SETTINGS_KEYS.SHOW_DESCRIPTION_COLUMN] as boolean,
showUpdatedColumn: settings[SETTINGS_KEYS.SHOW_UPDATED_COLUMN] as boolean,
showCharacterColumn: settings[SETTINGS_KEYS.SHOW_CHARACTER_COLUMN] as boolean,
showCharacterPortrait: settings[SETTINGS_KEYS.SHOW_CHARACTER_PORTRAIT] as boolean,
}),
[settings],
);
const filteredSignatures = useMemo<ExtendedSystemSignature[]>(() => {
return signatures.filter(sig => {
if (filterSignature && !filterSignature(sig)) {
return false;
}
if (hideLinkedSignatures && sig.linked_system) {
return false;
}
if (sig.kind === SignatureKind.CosmicSignature) {
if (!settings[SETTINGS_KEYS.COSMIC_SIGNATURE]) {
return false;
}
if (sig.group) {
const enabledGroups = Object.keys(settings).filter(
x => GROUPS_LIST.includes(x as SignatureGroup) && settings[x as SETTINGS_KEYS],
);
const mappedGroup = getGroupIdByRawGroup(sig.group);
if (!mappedGroup) {
return true; // If we can't determine the group, still show it
}
return enabledGroups.includes(mappedGroup);
}
return true;
}
return settings[sig.kind];
});
}, [signatures, hideLinkedSignatures, settings, filterSignature]);
const onRowMouseEnter = useCallback((e: DataTableRowMouseEvent) => {
setHoveredSignature(e.data as SystemSignature);
tooltipRef.current?.show(e.originalEvent);
}, []);
const onRowMouseLeave = useCallback(() => {
setHoveredSignature(null);
tooltipRef.current?.hide();
}, []);
const refVars = useRef({ settings, selectedSignatures, setSortSettings });
refVars.current = { settings, selectedSignatures, setSortSettings };
// @ts-ignore
const getRowClassName = useCallback(rowData => {
if (!rowData) {
return null;
}
return getSignatureRowClass(
rowData as ExtendedSystemSignature,
refVars.current.selectedSignatures,
refVars.current.settings[SETTINGS_KEYS.COLOR_BY_TYPE] as boolean,
);
}, []);
const handleSortSettings = useCallback(
(e: DataTableStateEvent) => refVars.current.setSortSettings({ sortField: e.sortField, sortOrder: e.sortOrder }),
[],
);
return (
<>
<div ref={tableRef} className={'h-full '}>
{filteredSignatures.length === 0 ? (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
No signatures
</div>
) : (
<>
{/* @ts-ignore */}
<DataTable
className={classes.Table}
value={filteredSignatures}
size="small"
selectionMode={selectable ? 'single' : 'multiple'}
selection={selectedSignatures}
metaKeySelection
onSelectionChange={handleSelectSignatures}
dataKey="eve_id"
tableClassName="w-full select-none"
resizableColumns={false}
onRowDoubleClick={handleRowClick}
rowHover
selectAll
sortField={sortSettings.sortField}
sortOrder={sortSettings.sortOrder}
onSort={event => setSortSettings(() => ({ sortField: event.sortField, sortOrder: event.sortOrder }))}
onRowMouseEnter={compact || medium ? handleEnterRow : undefined}
onRowMouseLeave={compact || medium ? handleLeaveRow : undefined}
rowClassName={row => {
if (selectedSignatures.some(x => x.eve_id === row.eve_id)) {
return clsx(classes.TableRowCompact, 'bg-amber-500/50 hover:bg-amber-500/70 transition duration-200');
}
const dateClass = getRowColorByTimeLeft(row.inserted_at ? new Date(row.inserted_at) : undefined);
if (!dateClass) {
return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200');
}
return clsx(classes.TableRowCompact, dateClass);
}}
>
<div ref={tableRef} className="h-full">
{filteredSignatures.length === 0 ? (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
No signatures
</div>
) : (
<>
{hasUnsupportedLanguage && (
<div className="w-full flex justify-center items-center text-amber-500 text-xs p-1 bg-amber-950/20 border-b border-amber-800/30">
<i className={PrimeIcons.EXCLAMATION_TRIANGLE + ' mr-1'} />
Non-English signatures detected. Some signatures may not display correctly. Double-click to edit signature details.
</div>
)}
<DataTable
value={filteredSignatures}
size="small"
selectionMode="multiple"
selection={selectedSignatures}
metaKeySelection
onSelectionChange={handleSelectSignatures}
dataKey="eve_id"
className="w-full select-none"
resizableColumns={false}
rowHover
selectAll
onRowDoubleClick={handleRowClick}
sortField={sortSettings.sortField}
sortOrder={sortSettings.sortOrder}
onSort={handleSortSettings}
onRowMouseEnter={onRowMouseEnter}
onRowMouseLeave={onRowMouseLeave}
// @ts-ignore
rowClassName={getRowClassName}
>
<Column
field="icon"
header=""
body={renderColIcon}
bodyClassName="p-0 px-1"
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
/>
<Column
field="eve_id"
header="Id"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
style={{ maxWidth: 72, minWidth: 72, width: 72 }}
sortable
/>
<Column
field="group"
header="Group"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
style={{ maxWidth: 110, minWidth: 110, width: 110 }}
body={sig => sig.group ?? ''}
hidden={isCompact}
sortable
/>
<Column
field="info"
header="Info"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
style={{ maxWidth: nameColumnWidth }}
hidden={isCompact || isMedium}
body={renderInfoColumn}
/>
{showDescriptionColumn && (
<Column
bodyClassName="p-0 px-1"
field="group"
body={x => renderIcon(x)}
style={{ maxWidth: 26, minWidth: 26, width: 26, height: 25 }}
></Column>
<Column
field="eve_id"
header="Id"
field="description"
header="Description"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
style={{ maxWidth: 72, minWidth: 72, width: 72 }}
hidden={isCompact}
body={renderDescription}
sortable
></Column>
/>
)}
<Column
field="inserted_at"
header="Added"
dataType="date"
body={renderAddedTimeLeft}
style={{ minWidth: 70, maxWidth: 80 }}
bodyClassName="ssc-header text-ellipsis overflow-hidden whitespace-nowrap"
sortable
/>
{showUpdatedColumn && (
<Column
field="group"
header="Group"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
hidden={compact}
style={{ maxWidth: 110, minWidth: 110, width: 110 }}
sortable
></Column>
<Column
field="info"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderInfoColumn}
style={{ maxWidth: nameColumnWidth }}
hidden={compact || medium}
></Column>
{showDescriptionColumn && (
<Column
field="description"
header="Description"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderDescription}
hidden={compact}
sortable
></Column>
)}
<Column
field="inserted_at"
header="Added"
field="updated_at"
header="Updated"
dataType="date"
body={renderUpdatedTimeLeft}
style={{ minWidth: 70, maxWidth: 80 }}
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
sortable
/>
)}
{showCharacterColumn && (
<Column
field="character_name"
header="Character"
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
body={renderAddedTimeLeft}
sortable
></Column>
)}
{showUpdatedColumn && (
<Column
field="updated_at"
header="Updated"
dataType="date"
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
body={renderUpdatedTimeLeft}
sortable
></Column>
)}
{!selectable && (
<Column
header=""
body={() => (
<div className="flex justify-end items-center gap-2 mr-[4px]">
<WdTooltipWrapper content="Double-click a row to edit signature">
<span className={PrimeIcons.PENCIL + ' text-[10px]'} />
</WdTooltipWrapper>
</div>
)}
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
bodyClassName="p-0 pl-1 pr-2"
/>
)}
</DataTable>
</>
)}
{!selectable && (
<Column
bodyClassName="p-0 pl-1 pr-2"
field="group"
body={renderToolbar}
// headerClassName={headerClasses}
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
></Column>
)}
</DataTable>
</>
)}
<WdTooltip
className="bg-stone-900/95 text-slate-50"
ref={tooltipRef}
content={hoveredSig ? <SignatureView {...hoveredSig} /> : null}
<WdTooltip
className="bg-stone-900/95 text-slate-50"
ref={tooltipRef}
position={TooltipPosition.top}
content={
hoveredSignature ? (
<SignatureView signature={hoveredSignature} showCharacterPortrait={showCharacterPortrait} />
) : null
}
/>
{showSignatureSettings && (
<SignatureSettings
systemId={systemId}
show
onHide={() => setShowSignatureSettings(false)}
signatureData={selectedSignatureForDialog || undefined}
/>
{showSignatureSettings && (
<SignatureSettings
systemId={systemId}
show
onHide={() => setShowSignatureSettings(false)}
signatureData={selectedSignature}
/>
)}
</div>
</>
)}
</div>
);
};

View File

@@ -1,15 +1,26 @@
import {
GroupType,
SignatureGroup,
SignatureGroupDE,
SignatureGroupENG,
SignatureGroupFR,
SignatureGroupRU,
SignatureKind,
SignatureKindDE,
SignatureKindENG,
SignatureKindFR,
SignatureKindRU,
} from '@/hooks/Mapper/types';
export const TIME_ONE_MINUTE = 1000 * 60;
export const TIME_TEN_MINUTES = 1000 * 60 * 10;
export const TIME_TEN_MINUTES = TIME_ONE_MINUTE * 10;
export const TIME_ONE_DAY = 24 * 60 * TIME_ONE_MINUTE;
export const TIME_ONE_WEEK = 7 * TIME_ONE_DAY;
export const FINAL_DURATION_MS = 10000;
export const COMPACT_MAX_WIDTH = 260;
export const MEDIUM_MAX_WIDTH = 380;
export const OTHER_COLUMNS_WIDTH = 276;
export const GROUPS_LIST = [
SignatureGroup.GasSite,
@@ -33,42 +44,232 @@ export const GROUPS: Record<SignatureGroup, GroupType> = {
[SignatureGroup.CosmicSignature]: { id: SignatureGroup.CosmicSignature, icon: '/icons/x_close14.png', w: 9, h: 9 },
};
export const MAPPING_GROUP_TO_ENG = {
// ENGLISH
[SignatureGroupENG.GasSite]: SignatureGroup.GasSite,
[SignatureGroupENG.RelicSite]: SignatureGroup.RelicSite,
[SignatureGroupENG.DataSite]: SignatureGroup.DataSite,
[SignatureGroupENG.OreSite]: SignatureGroup.OreSite,
[SignatureGroupENG.CombatSite]: SignatureGroup.CombatSite,
[SignatureGroupENG.Wormhole]: SignatureGroup.Wormhole,
[SignatureGroupENG.CosmicSignature]: SignatureGroup.CosmicSignature,
// RUSSIAN
[SignatureGroupRU.GasSite]: SignatureGroup.GasSite,
[SignatureGroupRU.RelicSite]: SignatureGroup.RelicSite,
[SignatureGroupRU.DataSite]: SignatureGroup.DataSite,
[SignatureGroupRU.OreSite]: SignatureGroup.OreSite,
[SignatureGroupRU.CombatSite]: SignatureGroup.CombatSite,
[SignatureGroupRU.Wormhole]: SignatureGroup.Wormhole,
[SignatureGroupRU.CosmicSignature]: SignatureGroup.CosmicSignature,
export const LANGUAGE_GROUP_MAPPINGS = {
EN: {
[SignatureGroupENG.GasSite]: SignatureGroup.GasSite,
[SignatureGroupENG.RelicSite]: SignatureGroup.RelicSite,
[SignatureGroupENG.DataSite]: SignatureGroup.DataSite,
[SignatureGroupENG.OreSite]: SignatureGroup.OreSite,
[SignatureGroupENG.CombatSite]: SignatureGroup.CombatSite,
[SignatureGroupENG.Wormhole]: SignatureGroup.Wormhole,
[SignatureGroupENG.CosmicSignature]: SignatureGroup.CosmicSignature,
},
RU: {
[SignatureGroupRU.GasSite]: SignatureGroup.GasSite,
[SignatureGroupRU.RelicSite]: SignatureGroup.RelicSite,
[SignatureGroupRU.DataSite]: SignatureGroup.DataSite,
[SignatureGroupRU.OreSite]: SignatureGroup.OreSite,
[SignatureGroupRU.CombatSite]: SignatureGroup.CombatSite,
[SignatureGroupRU.Wormhole]: SignatureGroup.Wormhole,
[SignatureGroupRU.CosmicSignature]: SignatureGroup.CosmicSignature,
},
FR: {
[SignatureGroupFR.GasSite]: SignatureGroup.GasSite,
[SignatureGroupFR.RelicSite]: SignatureGroup.RelicSite,
[SignatureGroupFR.DataSite]: SignatureGroup.DataSite,
[SignatureGroupFR.OreSite]: SignatureGroup.OreSite,
[SignatureGroupFR.CombatSite]: SignatureGroup.CombatSite,
[SignatureGroupFR.Wormhole]: SignatureGroup.Wormhole,
[SignatureGroupFR.CosmicSignature]: SignatureGroup.CosmicSignature,
},
DE: {
[SignatureGroupDE.GasSite]: SignatureGroup.GasSite,
[SignatureGroupDE.RelicSite]: SignatureGroup.RelicSite,
[SignatureGroupDE.DataSite]: SignatureGroup.DataSite,
[SignatureGroupDE.OreSite]: SignatureGroup.OreSite,
[SignatureGroupDE.CombatSite]: SignatureGroup.CombatSite,
[SignatureGroupDE.Wormhole]: SignatureGroup.Wormhole,
[SignatureGroupDE.CosmicSignature]: SignatureGroup.CosmicSignature,
},
};
export const MAPPING_TYPE_TO_ENG = {
// ENGLISH
[SignatureKindENG.CosmicSignature]: SignatureKind.CosmicSignature,
[SignatureKindENG.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
[SignatureKindENG.Structure]: SignatureKind.Structure,
[SignatureKindENG.Ship]: SignatureKind.Ship,
[SignatureKindENG.Deployable]: SignatureKind.Deployable,
[SignatureKindENG.Drone]: SignatureKind.Drone,
// Flatten the structure for backward compatibility
export const MAPPING_GROUP_TO_ENG: Record<string, SignatureGroup> = (() => {
const flattened: Record<string, SignatureGroup> = {};
for (const [, mappings] of Object.entries(LANGUAGE_GROUP_MAPPINGS)) {
Object.assign(flattened, mappings);
}
return flattened;
})();
// RUSSIAN
[SignatureKindRU.CosmicSignature]: SignatureKind.CosmicSignature,
[SignatureKindRU.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
[SignatureKindRU.Structure]: SignatureKind.Structure,
[SignatureKindRU.Ship]: SignatureKind.Ship,
[SignatureKindRU.Deployable]: SignatureKind.Deployable,
[SignatureKindRU.Drone]: SignatureKind.Drone,
export const getGroupIdByRawGroup = (val: string): SignatureGroup | undefined => {
return MAPPING_GROUP_TO_ENG[val] || undefined;
};
export const getGroupIdByRawGroup = (val: string) => MAPPING_GROUP_TO_ENG[val as SignatureGroup];
export const SIGNATURE_WINDOW_ID = 'system_signatures_window';
export const SIGNATURE_SETTING_STORE_KEY = 'wanderer_system_signature_settings_v6_5';
export enum SETTINGS_KEYS {
SHOW_DESCRIPTION_COLUMN = 'show_description_column',
SHOW_UPDATED_COLUMN = 'show_updated_column',
SHOW_CHARACTER_COLUMN = 'show_character_column',
LAZY_DELETE_SIGNATURES = 'lazy_delete_signatures',
KEEP_LAZY_DELETE = 'keep_lazy_delete_enabled',
DELETION_TIMING = 'deletion_timing',
COLOR_BY_TYPE = 'color_by_type',
SHOW_CHARACTER_PORTRAIT = 'show_character_portrait',
// From SignatureKind
COSMIC_ANOMALY = SignatureKind.CosmicAnomaly,
COSMIC_SIGNATURE = SignatureKind.CosmicSignature,
DEPLOYABLE = SignatureKind.Deployable,
STRUCTURE = SignatureKind.Structure,
STARBASE = SignatureKind.Starbase,
SHIP = SignatureKind.Ship,
DRONE = SignatureKind.Drone,
// From SignatureGroup
WORMHOLE = SignatureGroup.Wormhole,
RELIC_SITE = SignatureGroup.RelicSite,
DATA_SITE = SignatureGroup.DataSite,
ORE_SITE = SignatureGroup.OreSite,
GAS_SITE = SignatureGroup.GasSite,
COMBAT_SITE = SignatureGroup.CombatSite,
}
export enum SettingsTypes {
flag,
dropdown,
}
export type SignatureSettingsType = { [key in SETTINGS_KEYS]?: unknown };
export type Setting = {
key: SETTINGS_KEYS;
name: string;
type: SettingsTypes;
isSeparator?: boolean;
options?: { label: string; value: number | string | boolean }[];
};
export enum SIGNATURES_DELETION_TIMING {
IMMEDIATE,
DEFAULT,
EXTENDED,
}
export type SignatureDeletionTimingType = { [key in SIGNATURES_DELETION_TIMING]?: unknown };
export const SIGNATURE_SETTINGS = {
filterFlags: [
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.COSMIC_ANOMALY, name: 'Show Anomalies' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.COSMIC_SIGNATURE, name: 'Show Cosmic Signatures' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.DEPLOYABLE, name: 'Show Deployables' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.STRUCTURE, name: 'Show Structures' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.STARBASE, name: 'Show Starbase' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.SHIP, name: 'Show Ships' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.DRONE, name: 'Show Drones And Charges' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.WORMHOLE, name: 'Show Wormholes' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.RELIC_SITE, name: 'Show Relic Sites' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.DATA_SITE, name: 'Show Data Sites' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.ORE_SITE, name: 'Show Ore Sites' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.GAS_SITE, name: 'Show Gas Sites' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.COMBAT_SITE, name: 'Show Combat Sites' },
],
uiFlags: [
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.SHOW_UPDATED_COLUMN, name: 'Show Updated Column' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.SHOW_DESCRIPTION_COLUMN, name: 'Show Description Column' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.SHOW_CHARACTER_COLUMN, name: 'Show Character Column' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.LAZY_DELETE_SIGNATURES, name: 'Lazy Delete Signatures' },
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.KEEP_LAZY_DELETE, name: 'Keep "Lazy Delete" Enabled' },
{
type: SettingsTypes.flag,
key: SETTINGS_KEYS.SHOW_CHARACTER_PORTRAIT,
name: 'Show Character Portrait in Tooltip',
},
{ type: SettingsTypes.flag, key: SETTINGS_KEYS.COLOR_BY_TYPE, name: 'Color Signatures by Type' },
],
uiOther: [
{
type: SettingsTypes.dropdown,
key: SETTINGS_KEYS.DELETION_TIMING,
name: 'Deletion Timing',
options: [
{ value: SIGNATURES_DELETION_TIMING.IMMEDIATE, label: '0s' },
{ value: SIGNATURES_DELETION_TIMING.DEFAULT, label: '10s' },
{ value: SIGNATURES_DELETION_TIMING.EXTENDED, label: '30s' },
],
},
],
};
export const SETTINGS_VALUES: SignatureSettingsType = {
[SETTINGS_KEYS.SHOW_UPDATED_COLUMN]: true,
[SETTINGS_KEYS.SHOW_DESCRIPTION_COLUMN]: true,
[SETTINGS_KEYS.SHOW_CHARACTER_COLUMN]: true,
[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES]: true,
[SETTINGS_KEYS.KEEP_LAZY_DELETE]: false,
[SETTINGS_KEYS.DELETION_TIMING]: SIGNATURES_DELETION_TIMING.DEFAULT,
[SETTINGS_KEYS.COLOR_BY_TYPE]: true,
[SETTINGS_KEYS.SHOW_CHARACTER_PORTRAIT]: true,
[SETTINGS_KEYS.COSMIC_ANOMALY]: true,
[SETTINGS_KEYS.COSMIC_SIGNATURE]: true,
[SETTINGS_KEYS.DEPLOYABLE]: true,
[SETTINGS_KEYS.STRUCTURE]: true,
[SETTINGS_KEYS.STARBASE]: true,
[SETTINGS_KEYS.SHIP]: true,
[SETTINGS_KEYS.DRONE]: true,
[SETTINGS_KEYS.WORMHOLE]: true,
[SETTINGS_KEYS.RELIC_SITE]: true,
[SETTINGS_KEYS.DATA_SITE]: true,
[SETTINGS_KEYS.ORE_SITE]: true,
[SETTINGS_KEYS.GAS_SITE]: true,
[SETTINGS_KEYS.COMBAT_SITE]: true,
};
export const SIGNATURE_DELETION_TIMEOUTS: SignatureDeletionTimingType = {
[SIGNATURES_DELETION_TIMING.DEFAULT]: 10_000,
[SIGNATURES_DELETION_TIMING.IMMEDIATE]: 0,
[SIGNATURES_DELETION_TIMING.EXTENDED]: 30_000,
};
// Replace the flat structure with a nested structure by language
export const LANGUAGE_TYPE_MAPPINGS = {
EN: {
[SignatureKindENG.CosmicSignature]: SignatureKind.CosmicSignature,
[SignatureKindENG.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
[SignatureKindENG.Structure]: SignatureKind.Structure,
[SignatureKindENG.Ship]: SignatureKind.Ship,
[SignatureKindENG.Deployable]: SignatureKind.Deployable,
[SignatureKindENG.Drone]: SignatureKind.Drone,
[SignatureKindENG.Starbase]: SignatureKind.Starbase,
},
RU: {
[SignatureKindRU.CosmicSignature]: SignatureKind.CosmicSignature,
[SignatureKindRU.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
[SignatureKindRU.Structure]: SignatureKind.Structure,
[SignatureKindRU.Ship]: SignatureKind.Ship,
[SignatureKindRU.Deployable]: SignatureKind.Deployable,
[SignatureKindRU.Drone]: SignatureKind.Drone,
[SignatureKindRU.Starbase]: SignatureKind.Starbase,
},
FR: {
[SignatureKindFR.CosmicSignature]: SignatureKind.CosmicSignature,
[SignatureKindFR.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
[SignatureKindFR.Structure]: SignatureKind.Structure,
[SignatureKindFR.Ship]: SignatureKind.Ship,
[SignatureKindFR.Deployable]: SignatureKind.Deployable,
[SignatureKindFR.Drone]: SignatureKind.Drone,
[SignatureKindFR.Starbase]: SignatureKind.Starbase,
},
DE: {
[SignatureKindDE.CosmicSignature]: SignatureKind.CosmicSignature,
[SignatureKindDE.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
[SignatureKindDE.Structure]: SignatureKind.Structure,
[SignatureKindDE.Ship]: SignatureKind.Ship,
[SignatureKindDE.Deployable]: SignatureKind.Deployable,
[SignatureKindDE.Drone]: SignatureKind.Drone,
[SignatureKindDE.Starbase]: SignatureKind.Starbase,
},
};
// Flatten the structure for backward compatibility
export const MAPPING_TYPE_TO_ENG: Record<string, SignatureKind> = (() => {
const flattened: Record<string, SignatureKind> = {};
for (const [, mappings] of Object.entries(LANGUAGE_TYPE_MAPPINGS)) {
Object.assign(flattened, mappings);
}
return flattened;
})();

View File

@@ -0,0 +1,104 @@
import { ExtendedSystemSignature, SystemSignature } from '@/hooks/Mapper/types';
import { FINAL_DURATION_MS } from '../constants';
export function prepareUpdatePayload(
systemId: string,
added: ExtendedSystemSignature[],
updated: ExtendedSystemSignature[],
removed: ExtendedSystemSignature[],
) {
return {
system_id: systemId,
added: added.map(s => ({ ...s })),
updated: updated.map(s => ({ ...s })),
removed: removed.map(s => ({ ...s })),
};
}
export function schedulePendingAdditionForSig(
sig: ExtendedSystemSignature,
finalDuration: number,
setSignatures: React.Dispatch<React.SetStateAction<ExtendedSystemSignature[]>>,
pendingAdditionMapRef: React.MutableRefObject<Record<string, { finalUntil: number; finalTimeoutId: number }>>,
setPendingUndoAdditions: React.Dispatch<React.SetStateAction<ExtendedSystemSignature[]>>,
) {
setPendingUndoAdditions(prev => [...prev, sig]);
const now = Date.now();
const finalTimeoutId = window.setTimeout(() => {
setSignatures(prev =>
prev.map(x => (x.eve_id === sig.eve_id ? { ...x, pendingAddition: false, pendingUntil: undefined } : x)),
);
const clone = { ...pendingAdditionMapRef.current };
delete clone[sig.eve_id];
pendingAdditionMapRef.current = clone;
setPendingUndoAdditions(prev => prev.filter(x => x.eve_id !== sig.eve_id));
}, finalDuration);
pendingAdditionMapRef.current = {
...pendingAdditionMapRef.current,
[sig.eve_id]: {
finalUntil: now + finalDuration,
finalTimeoutId,
},
};
setSignatures(prev =>
prev.map(x => (x.eve_id === sig.eve_id ? { ...x, pendingAddition: true, pendingUntil: now + finalDuration } : x)),
);
}
export function mergeLocalPending(
pendingMapRef: React.MutableRefObject<Record<string, ExtendedSystemSignature>>,
serverSigs: ExtendedSystemSignature[],
): ExtendedSystemSignature[] {
const now = Date.now();
const pendingDeletions = Object.values(pendingMapRef.current).filter(
sig => sig.pendingDeletion && sig.pendingUntil && sig.pendingUntil > now,
);
const mergedMap = new Map<string, ExtendedSystemSignature>();
serverSigs.forEach(sig => mergedMap.set(sig.eve_id, sig));
pendingDeletions.forEach(sig => {
if (mergedMap.has(sig.eve_id)) {
mergedMap.set(sig.eve_id, sig);
}
});
return Array.from(mergedMap.values());
}
export function scheduleLazyTimers(
signatures: ExtendedSystemSignature[],
pendingMapRef: React.MutableRefObject<Record<string, ExtendedSystemSignature>>,
finalizeFn: (sig: ExtendedSystemSignature) => Promise<void>,
finalDuration = FINAL_DURATION_MS,
) {
signatures.forEach(sig => {
const finalTimeoutId = window.setTimeout(async () => {
await finalizeFn(sig);
}, finalDuration);
pendingMapRef.current = {
...pendingMapRef.current,
[sig.eve_id]: {
...sig,
finalTimeoutId,
},
};
});
}
export const calculateTimeRemaining = (pendingSigs: SystemSignature[]) => {
const now = Date.now();
let minTime: number | undefined = undefined;
pendingSigs.forEach(sig => {
const extendedSig = sig as unknown as { pendingUntil?: number };
if (extendedSig.pendingUntil && (minTime === undefined || extendedSig.pendingUntil - now < minTime)) {
minTime = extendedSig.pendingUntil - now;
}
});
return minTime && minTime > 0 ? minTime : undefined;
};

View File

@@ -1,25 +1,68 @@
import { GROUPS_LIST } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants';
import { SystemSignature } from '@/hooks/Mapper/types';
import { GROUPS_LIST } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { getState } from './getState.ts';
import { getState } from './getState';
/**
* Compare two lists of signatures and return which are added, updated, or removed.
*
* @param oldSignatures existing signatures (in memory or from server)
* @param newSignatures newly parsed or incoming signatures from user input
* @param updateOnly if true, do NOT remove old signatures not found in newSignatures
* @param skipUpdateUntouched if true, do NOT push unmodified signatures into the `updated` array
*/
export const getActualSigs = (
oldSignatures: SystemSignature[],
newSignatures: SystemSignature[],
updateOnly: boolean,
updateOnly?: boolean,
skipUpdateUntouched?: boolean,
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
const updated: SystemSignature[] = [];
const removed: SystemSignature[] = [];
const added: SystemSignature[] = [];
oldSignatures.forEach(oldSig => {
// if old sigs is not contains in newSigs we need mark it as removed
// otherwise we check
const newSig = newSignatures.find(s => s.eve_id === oldSig.eve_id);
if (newSig) {
// we take new sig and now we need check that sig has been updated
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
if (isNeedUpgrade) {
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
const needUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
const mergedSig = { ...oldSig };
let changed = false;
if (needUpgrade) {
mergedSig.group = newSig.group;
mergedSig.name = newSig.name;
changed = true;
}
if (newSig.description && newSig.description !== oldSig.description) {
mergedSig.description = newSig.description;
changed = true;
}
try {
const oldInfo = JSON.parse(oldSig.custom_info || '{}');
const newInfo = JSON.parse(newSig.custom_info || '{}');
let infoChanged = false;
for (const key in newInfo) {
if (oldInfo[key] !== newInfo[key]) {
oldInfo[key] = newInfo[key];
infoChanged = true;
}
}
if (infoChanged) {
mergedSig.custom_info = JSON.stringify(oldInfo);
changed = true;
}
} catch (e) {
console.error(`getActualSigs: Error merging custom_info for ${oldSig.eve_id}`, e);
}
if (newSig.updated_at !== oldSig.updated_at) {
mergedSig.updated_at = newSig.updated_at;
changed = true;
}
if (changed) {
updated.push(mergedSig);
} else if (!skipUpdateUntouched) {
updated.push({ ...oldSig });
}
@@ -30,8 +73,12 @@ export const getActualSigs = (
}
});
const oldSignaturesIds = oldSignatures.map(x => x.eve_id);
const added = newSignatures.filter(s => !oldSignaturesIds.includes(s.eve_id));
const oldIds = new Set(oldSignatures.map(x => x.eve_id));
newSignatures.forEach(s => {
if (!oldIds.has(s.eve_id)) {
added.push(s);
}
});
return { added, updated, removed };
};

View File

@@ -3,19 +3,21 @@ import {
TIME_TEN_MINUTES,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
export const getRowColorByTimeLeft = (date: Date | undefined) => {
export const getRowBackgroundColor = (date: Date | undefined): string => {
if (!date) {
return null;
return '';
}
const currentDate = new Date();
const diff = currentDate.getTime() + currentDate.getTimezoneOffset() * TIME_ONE_MINUTE - date.getTime();
if (diff < TIME_ONE_MINUTE) {
return 'bg-lime-600/50 transition hover:bg-lime-600/60';
return '[&_.ssc-header]:text-amber-300 [&_.ssc-header]:hover:text-amber-200 [&_.ssc-header]:font-bold';
}
if (diff < TIME_TEN_MINUTES) {
return 'bg-lime-700/40 transition hover:bg-lime-700/50';
return '[&_.ssc-header]:text-amber-500 [&_.ssc-header]:hover:text-amber-500 [&_.ssc-header]:font-bold';
}
return '';
};

View File

@@ -1,16 +1,15 @@
import { SystemSignature } from '@/hooks/Mapper/types';
import { UNKNOWN_SIGNATURE_NAME } from '@/hooks/Mapper/helpers';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
// also we need detect changes, we need understand that sigs has states
// first state when kind is Cosmic Signature or Cosmic Anomaly and group is empty
// and we should detect it for ungrade sigs
export const getState = (_: string[], newSig: SystemSignature) => {
let state = -1;
if (!newSig.group || newSig.group === '') {
if (!newSig.group || newSig.group === SignatureGroup.CosmicSignature) {
state = 0;
} else if (!newSig.name || newSig.name === '') {
} else if (!newSig.name || newSig.name === '' || newSig.name === UNKNOWN_SIGNATURE_NAME) {
state = 1;
} else if (newSig.name !== '') {
state = 2;
}
return state;
};

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