Compare commits

...

275 Commits

Author SHA1 Message Date
achichenkov
b549189644 fix(Map): fixed U210, K346 for C4 shattered systems 2024-12-16 17:13:33 +03:00
achichenkov
35279d17b4 fix(Map): fixed U210, K346 for shattered systems. Fixed mass of mediums chains. Fixed size of some capital chains from 3M to 3.3M. Based on https://whtype.info/ data. 2024-12-16 16:48:53 +03:00
achichenkov
bb403aa0c5 fix(Map): removed unnecessary log 2024-12-16 12:45:51 +03:00
achichenkov
04327c288b fix(Map): Uncomment what should not be commented 2024-12-16 12:43:26 +03:00
achichenkov
94d60e40d0 feat(Map): Fixed incorrect wrapping labels of checkboxes in System Signatures, Local and Routes. Also changed dotlan links for k-spacem now it leads to region map before, for wh all stay as it was. Added ability to chane to softer background and remove dots on background of map. Also some small design issues. #2 2024-12-16 12:42:24 +03:00
achichenkov
8505fcb6b7 feat(Map): Fixed incorrect wrapping labels of checkboxes in System Signatures, Local and Routes. Also changed dotlan links for k-spacem now it leads to region map before, for wh all stay as it was. Added ability to chane to softer background and remove dots on background of map. Also some small design issues. 2024-12-16 12:42:03 +03:00
CI
9aec57166d chore: release version v1.29.5
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-14 22:32:16 +00:00
Dmitry Popov
a3739f2950 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-14 23:31:47 +01:00
Dmitry Popov
3d3b152758 fix(Core): Fix character trackers cleanup 2024-12-14 23:31:42 +01:00
CI
0e03730543 chore: release version v1.29.4
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-10 11:01:09 +00:00
Dmitry Popov
97e07a6511 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-10 12:00:38 +01:00
Dmitry Popov
a77a51ba15 fix(Core): Small fixes 2024-12-10 12:00:34 +01:00
CI
42e706e1c2 chore: release version v1.29.3
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-07 18:02:05 +00:00
Dmitry Popov
025dd06053 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-07 19:01:36 +01:00
Dmitry Popov
bcb421d879 fix(Core): Increased eve DB data download timeout 2024-12-07 19:01:32 +01:00
CI
66056ab54b chore: release version v1.29.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-07 08:32:58 +00:00
Dmitry Popov
bb92f76ceb Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-07 09:32:28 +01:00
Dmitry Popov
84076b340b fix(Core): Fix unpkg CDN issues, fix Abyssals sites adding as systems on map 2024-12-07 09:32:24 +01:00
CI
48caae5c0e chore: release version v1.29.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-05 10:54:41 +00:00
Dmitry Popov
77dd23795a Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-05 11:54:05 +01:00
Dmitry Popov
2771d6304e chore: release version v1.28.1 2024-12-05 11:54:01 +01:00
CI
9946edffa4 chore: release version v1.29.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-05 08:52:42 +00:00
Dmitry Popov
50bf2fd9d3 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-05 09:52:13 +01:00
Dmitry Popov
bdcde168aa feat(Signatures): Show 'Unsplashed' signatures on the map (optionally) 2024-12-05 09:52:07 +01:00
CI
5807142e20 chore: release version v1.28.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-04 20:14:44 +00:00
Dmitry Popov
ec2d9565b9 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-04 21:14:18 +01:00
Dmitry Popov
a18a71c73d chore: release version v1.27.1 2024-12-04 21:14:14 +01:00
CI
93a6bd1156 chore: release version v1.28.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-04 17:02:43 +00:00
Dmitry Popov
581a410aef Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-04 18:02:08 +01:00
Dmitry Popov
ab02fe988c feat(Map): Added an option to show 'Offline characters' to map admins & managers only
- updated UI layout for map settings modal
2024-12-04 18:01:50 +01:00
CI
b8d20fb21b chore: release version v1.27.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-04 08:14:30 +00:00
Dmitry Popov
12fa1a0be8 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-04 09:13:57 +01:00
Dmitry Popov
85a84f7507 fix(Map): Fix 'On the map' visibility 2024-12-04 09:13:54 +01:00
CI
2385313013 chore: release version v1.27.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-03 21:04:59 +00:00
Dmitry Popov
c7ce727571 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-03 22:04:26 +01:00
Dmitry Popov
8b165ff478 feat(Map): Hide 'On the map' list for 'Viewer' role 2024-12-03 22:04:20 +01:00
CI
6d7d0cc72d chore: release version v1.26.1 2024-12-03 20:05:05 +00:00
Dmitry Popov
f7eba5d4fd Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-03 21:04:32 +01:00
Dmitry Popov
73ef6dae73 fix(Signatures): Fix error on splash wh 2024-12-03 21:04:26 +01:00
CI
7fa6df1e5e chore: release version v1.26.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-12-03 16:54:46 +00:00
Dmitry Popov
e1a2ffb151 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-03 17:54:18 +01:00
Dmitry Popov
6d7727a32d feat(Signatures): Keep 'Lazy delete' enabled setting 2024-12-03 17:54:13 +01:00
CI
6d7a94bd5a chore: release version v1.25.2
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-12-01 07:26:29 +00:00
Dmitry Popov
ecc3fb17e1 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-12-01 08:25:59 +01:00
Dmitry Popov
209e2bf0a5 fix(Signatures): Fix lazy delete on system switch 2024-12-01 08:25:10 +01:00
CI
b1947e57a4 chore: release version v1.25.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-28 23:20:10 +00:00
Dmitry Popov
74507501a5 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-29 00:19:43 +01:00
Dmitry Popov
c73481fd58 fix(Signatures): Fix colors & add 'Backspace' hotkey to delete signatures 2024-11-29 00:19:26 +01:00
CI
7795ad0b0c chore: release version v1.25.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-28 10:09:57 +00:00
Dmitry Popov
aff768f413 feat(Signatures): Automatically remove signature if linked system removed 2024-11-28 11:09:24 +01:00
CI
310b60f5b6 chore: release version v1.24.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-27 23:42:04 +00:00
Dmitry Popov
100f0be86a fix(Signatures): Fix paste signatures 2024-11-28 00:41:01 +01:00
CI
87e115e40d chore: release version v1.24.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-27 15:37:38 +00:00
Dmitry Popov
ef5f36e4c4 Signatures k162 types (#77)
* feat(Signatures): Added ability to specify K162 type (actually next types supported Hi/Low/Null/C1-C6/Thera/Pochven)
2024-11-27 19:37:10 +04:00
CI
099650420d chore: release version v1.24.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-27 13:28:03 +00:00
Dmitry Popov
8ccf7fffa5 feat(Signatures): Added "Lazy delete" option & got rid of update popup 2024-11-27 14:27:27 +01:00
CI
b97a055bf7 chore: release version v1.23.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-26 21:05:58 +00:00
Dmitry Popov
663fee6699 feat(Map): Lock systems available to manager/admin roles only (#75)
* feat(Map): Lock systems available to manager/admin roles only

* feat(Map): Fix add system & add acl member select behaviour
2024-11-27 01:05:26 +04:00
CI
33d5f3938b chore: release version v1.22.0 2024-11-26 19:03:01 +00:00
Aleksei Chichenkov
ef6b45d7a1 feat(Map): Rework design of checkboxes in Signatures settings dialog. Rework design of checkboxes in Routes settings dialog. Now signature will deleteing by Delete hotkey was Backspace. Fixed size of group column in signatures list. Instead Updated column will be Added, updated may be turn on in settings. (#76)
Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
2024-11-26 23:02:30 +04:00
CI
c1ecd3690e chore: release version v1.21.0
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-24 15:55:38 +00:00
Aleksei Chichenkov
3250fe1ec6 Merge pull request #74 from wanderer-industries/dessign-issues-2
feat(Map): add new gate design, change EOL placement
2024-11-24 18:55:12 +03:00
achichenkov
48e8cd93b9 feat(Map): add new gate design, change EOL placement 2024-11-24 18:30:40 +03:00
CI
afacbb16b6 chore: release version v1.20.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-22 12:42:02 +00:00
Dmitry Popov
dfad127f32 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-22 13:41:35 +01:00
Dmitry Popov
300c1b5a18 chore: release version v1.19.3 2024-11-22 13:41:30 +01:00
CI
bb38e1710b chore: release version v1.20.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-22 12:27:53 +00:00
Dmitry Popov
0857a82de5 feat(Core): Add connection type for Gates, add new Update logic
fixes #73
2024-11-22 13:27:17 +01:00
CI
da5afcc91c chore: release version v1.19.3
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-20 14:09:27 +00:00
Dmitry Popov
0002979fda fix(Core): Fix adding systems on splash (#71)
* fix(Core): Fix adding systems on splash
2024-11-20 18:08:59 +04:00
CI
080af16d41 chore: release version v1.19.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-19 21:27:05 +00:00
Dmitry Popov
d03a0b7083 chore: release version v1.19.1 2024-11-19 22:26:22 +01:00
CI
5ba21f5386 chore: release version v1.19.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-19 16:07:12 +00:00
Dmitry Popov
10eeae5295 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-19 17:06:39 +01:00
Dmitry Popov
a5bead15d0 chore: release version v1.18.1 2024-11-19 17:06:35 +01:00
CI
0de674adde chore: release version v1.19.0 2024-11-19 14:22:53 +00:00
Dmitry Popov
1db65965d0 feat(Signatures): Add user setting to show Inserted time in a separate column 2024-11-19 15:22:16 +01:00
CI
bbed17f631 chore: release version v1.18.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-17 11:19:49 +00:00
Dmitry Popov
0af4a3a731 chore: release version v1.18.0 2024-11-17 12:19:22 +01:00
CI
49d503705a chore: release version v1.18.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-16 15:05:38 +00:00
Aleksei Chichenkov
c55dd7b8d9 Merge pull request #64 from wanderer-industries/design-issues
feat(Map): a lot of design issues
2024-11-16 18:05:08 +03:00
CI
7ddcab3537 chore: release version v1.17.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-15 22:35:12 +00:00
Dmitry Popov
040b46c345 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-15 23:34:38 +01:00
Dmitry Popov
cd11ab6775 feat(Signatures): Add user setting to show Description in a separate column 2024-11-15 23:34:33 +01:00
achichenkov
a5d776f3b1 feat(Map): a lot of design issues 2024-11-15 19:42:46 +03:00
CI
e02caf341d chore: release version v1.16.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-15 16:13:05 +00:00
Dmitry Popov
3c04caa67c Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-15 17:12:37 +01:00
Dmitry Popov
e76b564cbf fix(Signatures): Fix signature stored filters 2024-11-15 17:12:33 +01:00
CI
5b2de88c3d chore: release version v1.16.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-15 07:59:04 +00:00
Dmitry Popov
82080b232f feat(Signatures): Add additional filters support to signature list, show description icon 2024-11-15 08:58:25 +01:00
CI
666bc7af43 chore: release version v1.15.5
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-14 12:57:21 +00:00
Dmitry Popov
dc077d5a5b chore: release version v1.15.4 2024-11-14 13:56:52 +01:00
CI
29c840c64a chore: release version v1.15.4
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-14 08:28:05 +00:00
Dmitry Popov
65e0f89f33 fix(Core): Untracked characters still tracked on map (#63)
fixes #60
2024-11-14 12:27:37 +04:00
CI
d3b9b36332 chore: release version v1.15.3
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-13 07:47:06 +00:00
Dmitry Popov
90bbf29ea1 Db unix socket (#62)
* feat(Core): Add support for Unix sockets for DB connection

* chore: release version v1.15.2

* chore: release version v1.15.2
2024-11-13 11:46:39 +04:00
CI
57f73684e8 chore: release version v1.15.2
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-07 21:45:12 +00:00
Dmitry Popov
7833cdebb2 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-07 22:44:22 +01:00
Dmitry Popov
67a5ae2985 chore: release version v1.15.0 2024-11-07 22:44:19 +01:00
CI
f58c52d26b chore: release version v1.15.1 2024-11-07 21:42:31 +00:00
Dmitry Popov
41e7739461 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-07 22:41:52 +01:00
Dmitry Popov
332152b677 fix(Dev): Update .devcontainer instructions 2024-11-07 22:41:43 +01:00
CI
85b49fe1f0 chore: release version v1.15.0 2024-11-07 21:40:09 +00:00
Dmitry Popov
e7924532be feat(Connections): Add connection mark EOL time (#56) 2024-11-08 01:39:44 +04:00
CI
475d950ad6 chore: release version v1.14.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-06 14:13:14 +00:00
Dmitry Popov
e6cfb29c6f fix(Core): Fix character tracking permissions 2024-11-06 15:12:44 +01:00
CI
dee6e86db1 chore: release version v1.14.0
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-05 07:23:19 +00:00
Dmitry Popov
72f088331f Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-05 08:22:50 +01:00
Dmitry Popov
bbf536d10e feat(ACL): Add an ability to assign member role without DnD 2024-11-05 08:22:46 +01:00
CI
149ac98297 chore: release version v1.13.12
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-04 07:24:03 +00:00
Dmitry Popov
b90a2910c9 fix(Map): Fix system revert issues 2024-11-04 08:23:26 +01:00
CI
c4da8a3a8d chore: release version v1.13.11 2024-11-02 21:31:48 +00:00
Dmitry Popov
3ca75583d2 fix(Map): Fix system revert issues 2024-11-02 22:31:20 +01:00
CI
5f4607ae6f chore: release version v1.13.10 2024-11-01 14:54:49 +00:00
Dmitry Popov
d880c6873f Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/wanderer_app/map/map_server_impl.ex
2024-11-01 15:53:05 +01:00
Dmitry Popov
937649b2ed fix(Map): Fix system revert issues 2024-11-01 15:51:15 +01:00
CI
78e912c886 chore: release version v1.13.9 2024-11-01 10:55:05 +00:00
Dmitry Popov
696c7d2cd1 Map events refactoring (#41)
* refactor(Map): Map event handling refactoring
2024-11-01 14:54:34 +04:00
CI
49c0cb026b chore: release version v1.13.8 2024-10-28 16:21:55 +00:00
Dmitry Popov
7091341b4b chore: release version v1.13.7 2024-10-28 17:21:26 +01:00
CI
8795ce6af3 chore: release version v1.13.7 2024-10-28 16:01:28 +00:00
Dmitry Popov
239b34d15a chore: release version v1.13.6 2024-10-28 17:00:53 +01:00
CI
729a5ad1a9 chore: release version v1.13.6 2024-10-28 15:44:15 +00:00
Dmitry Popov
8febc2476b Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 16:43:45 +01:00
Dmitry Popov
84b1317927 chore: release version v1.12.11 2024-10-28 16:43:42 +01:00
CI
bfb504e5db chore: release version v1.13.5 2024-10-28 14:31:58 +00:00
Dmitry Popov
9975deacfb Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 15:31:28 +01:00
Dmitry Popov
f77f071003 chore: release version v1.12.11 2024-10-28 15:31:25 +01:00
CI
4a8d55e83d chore: release version v1.13.4 2024-10-28 13:10:01 +00:00
Dmitry Popov
9a99f40e2a Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 14:09:34 +01:00
Dmitry Popov
428fa8035c chore: release version v1.12.11 2024-10-28 14:09:31 +01:00
CI
745f3dee17 chore: release version v1.13.3 2024-10-28 10:59:00 +00:00
Dmitry Popov
9907cc1875 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 11:58:33 +01:00
Dmitry Popov
130cd780a2 chore: release version v1.12.11 2024-10-28 11:58:30 +01:00
CI
a808e5d1a5 chore: release version v1.13.2 2024-10-28 10:52:13 +00:00
Dmitry Popov
b926117e26 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 11:51:45 +01:00
Dmitry Popov
fdf238accf chore: release version v1.12.11 2024-10-28 11:51:42 +01:00
CI
4e1c27e8a3 chore: release version v1.13.1 2024-10-28 10:35:24 +00:00
Dmitry Popov
a3e51a0ac5 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 11:34:55 +01:00
Dmitry Popov
d27bb0d54f chore: release version v1.12.11 2024-10-28 11:34:52 +01:00
CI
f6a750f06b chore: release version v1.13.0 2024-10-28 10:18:27 +00:00
Dmitry Popov
c4e2f63e69 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-28 11:17:52 +01:00
Dmitry Popov
675ffc8f42 feat(Core): Use ESI /characters/affiliation API
Use ESI /characters/affiliation to fetch characters corporation changes
(1H cached instead of 7D)
2024-10-28 11:17:49 +01:00
CI
cdc4221175 chore: release version v1.12.11 2024-10-25 12:17:07 +00:00
Dmitry Popov
843f3363fd Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-25 14:16:32 +02:00
Dmitry Popov
17653a6374 chore: release version v1.12.6 2024-10-25 14:16:28 +02:00
CI
7d860533a0 chore: release version v1.12.10 2024-10-24 20:01:03 +00:00
Dmitry Popov
0c751b3ced Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-24 22:00:36 +02:00
Dmitry Popov
80a522ab06 chore: release version v1.12.6 2024-10-24 22:00:33 +02:00
CI
0718d76e00 chore: release version v1.12.9 2024-10-24 19:49:25 +00:00
Dmitry Popov
a4887c5358 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-24 21:48:52 +02:00
Dmitry Popov
2ad5e122ff chore: release version v1.12.6 2024-10-24 21:48:50 +02:00
CI
832d874a0e chore: release version v1.12.8 2024-10-24 19:18:49 +00:00
Dmitry Popov
6a133d4dbd Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-24 21:18:07 +02:00
Dmitry Popov
d96dfa63c9 chore: release version v1.12.6 2024-10-24 21:18:03 +02:00
CI
d5c8c05426 chore: release version v1.12.7 2024-10-24 19:12:47 +00:00
Dmitry Popov
b6bb4647c7 chore: release version v1.12.6 2024-10-24 21:12:13 +02:00
CI
a81f06b743 chore: release version v1.12.6 2024-10-24 09:10:46 +00:00
Dmitry Popov
cb41a33546 Custom signatures (#37)
* feat(signatures): Add custom info to system signatures

* feat(connections): Add custom info to system connections

* feat(Map): Add system signature type

* feat(Map): Update wormhole types info

* feat(Map): Add undo action for removed systems

* feat(Map): Delete systems on Backspace hotkey

* feat(Map): Update k-space systems background & styles

* feat(Map): Update systems status background styles

* feat(Map): add support for new wh type data. add signatures settings modal menu; reworked signatures widget - was added info of wormhole;

---------

Co-authored-by: achichenkov <aleksei.chichenkov@telleqt.ai>
2024-10-24 13:10:17 +04:00
CI
005068de9c chore: release version v1.12.5 2024-10-22 08:18:22 +00:00
Dmitry Popov
d8c7b1e070 Auto delete connections (#38)
* feat(Map): Auto delete linked connections

* feat(Map): Auto delete linked connections
2024-10-22 12:17:53 +04:00
CI
4835dfcc42 chore: release version v1.12.4 2024-10-21 11:11:42 +00:00
Dmitry Popov
15bceb09a2 chore: release version v1.12.3 2024-10-21 13:11:13 +02:00
Dmitry Popov
13e818abfd fix(Map): Fix systems cleanup 2024-10-21 13:02:42 +02:00
CI
9c5f6049b5 chore: release version v1.12.3 2024-10-18 07:10:35 +00:00
Dmitry Popov
2095b619a4 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-18 11:10:06 +04:00
Dmitry Popov
df155cbc1b fix(Map): Fix regression issues 2024-10-18 11:10:02 +04:00
CI
3781729fd1 chore: release version v1.12.2 2024-10-16 21:12:27 +00:00
Dmitry Popov
d03c634ec0 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-17 01:11:41 +04:00
Dmitry Popov
93c979c218 chore: release version v1.12.0 2024-10-17 01:11:37 +04:00
CI
90fef94583 chore: release version v1.12.1 2024-10-16 20:11:16 +00:00
Dmitry Popov
0b8eec2263 fix(Map): Fix system add error after map page refresh 2024-10-17 00:10:36 +04:00
CI
9511af4e6d chore: release version v1.12.0 2024-10-16 14:12:30 +00:00
Aleksei Chichenkov
7deaf1fd9f Merge pull request #36 from wanderer-industries/refactor-settings
feat(Map): Prettify user settings
2024-10-16 17:12:04 +03:00
achichenkov
43cc5bd520 feat(Map): Prettify user settings
Fixes #35
2024-10-16 17:04:50 +03:00
CI
68b58aa520 chore: release version v1.11.5 2024-10-16 12:46:16 +00:00
Dmitry Popov
dbadd09af3 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-16 16:45:48 +04:00
Dmitry Popov
fcbe2c754f chore: release version v1.11.1 2024-10-16 16:45:44 +04:00
CI
ad4580677b chore: release version v1.11.4 2024-10-16 11:48:48 +00:00
Dmitry Popov
01a6cc7d92 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-16 15:48:20 +04:00
Dmitry Popov
95ce95a187 chore: release version v1.11.1 2024-10-16 15:48:16 +04:00
CI
ce8e6fbfb0 chore: release version v1.11.3 2024-10-16 05:51:27 +00:00
Dmitry Popov
a20eaed76b Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-16 09:50:49 +04:00
Dmitry Popov
419af72028 chore: release version v1.11.1 2024-10-16 09:50:45 +04:00
CI
8e499522f6 chore: release version v1.11.2 2024-10-15 22:13:53 +00:00
Dmitry Popov
84321b847e chore: release version v1.11.1 2024-10-16 02:13:18 +04:00
CI
c969a4d465 chore: release version v1.11.1 2024-10-14 14:31:41 +00:00
Dmitry Popov
0e12c850b6 chore: release version v1.11.0 2024-10-14 18:31:12 +04:00
CI
442835dd9b chore: release version v1.11.0 2024-10-14 14:24:17 +00:00
Dmitry Popov
b4ff99cb2e Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-14 18:23:43 +04:00
Dmitry Popov
aa0ecbc998 feat(Map): Add map level option to store custom labels 2024-10-14 18:23:39 +04:00
CI
cc412e93c0 chore: release version v1.10.0 2024-10-13 10:18:23 +00:00
Dmitry Popov
1d36fadbfa Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-13 14:17:43 +04:00
Dmitry Popov
56182bd87d feat(Map): Link signature on splash 2024-10-13 14:17:40 +04:00
CI
d290ff92b3 chore: release version v1.9.0 2024-10-13 10:10:38 +00:00
Dmitry Popov
298c5fd3b8 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-13 14:09:56 +04:00
Dmitry Popov
e365c43781 feat(Map): Link signature on splash 2024-10-13 14:09:53 +04:00
CI
23a9f22ef4 chore: release version v1.8.0 2024-10-13 10:04:22 +00:00
Dmitry Popov
242f437237 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-13 14:03:53 +04:00
Dmitry Popov
2eae8cffdb feat(Map): Link signature on splash 2024-10-13 14:03:48 +04:00
CI
68ab3d4f72 chore: release version v1.7.0 2024-10-13 09:40:06 +00:00
Dmitry Popov
1ea805aff0 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-13 13:39:39 +04:00
Dmitry Popov
6ce45349dc feat(Map): Link signature on splash 2024-10-13 13:39:35 +04:00
CI
8f20cd9863 chore: release version v1.6.0 2024-10-13 08:58:05 +00:00
Dmitry Popov
4ed0e85680 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-13 12:57:37 +04:00
Dmitry Popov
8ce9eb9955 feat(Map): Link signature on splash 2024-10-13 12:57:33 +04:00
CI
363330f3d1 chore: release version v1.5.0 2024-10-11 09:06:20 +00:00
Dmitry Popov
fbf9c5ddd6 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-11 13:05:52 +04:00
Dmitry Popov
fbf2ee314c feat(Map): Follow Character on Map and auto select their current system
fixes #34
2024-10-11 13:05:48 +04:00
CI
c9f83fb419 chore: release version v1.4.0 2024-10-11 08:12:17 +00:00
Dmitry Popov
9737d91e16 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-11 12:11:38 +04:00
Dmitry Popov
2f672ae970 feat(Map): Follow Character on Map and auto select their current system
fixes #34
2024-10-11 12:11:33 +04:00
CI
25339546c6 chore: release version v1.3.6 2024-10-09 21:44:05 +00:00
Dmitry Popov
912cad42ac Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-10 01:43:36 +04:00
Dmitry Popov
b3752c8d8f fix(Signatures): Signatures update fixes
fixes #25
2024-10-10 01:43:32 +04:00
CI
e8a11333f2 chore: release version v1.3.5 2024-10-09 13:41:38 +00:00
Dmitry Popov
8bb6d09e6e fix(Signatures): Signatures update fixes
fixes #25
2024-10-09 17:41:02 +04:00
CI
56bf955297 chore: release version v1.3.4 2024-10-09 09:24:17 +00:00
Dmitry Popov
ef6c08dfe8 Refactoring (#27)
* chore: release version v1.3.1

* fix(Core): Add system "true security" correction

* chore: release version v1.3.1
2024-10-09 13:23:35 +04:00
CI
495c3e1cd7 chore: release version v1.3.3 2024-10-08 07:30:12 +00:00
Dmitry Popov
9a5fe3d744 chore: release version v1.3.2 2024-10-08 11:29:36 +04:00
CI
72607cae4d chore: release version v1.3.2 2024-10-07 19:34:37 +00:00
Dmitry Popov
4891cdb04d 19 add map custom options (#24)
* feat(Core): Support map default layout option
2024-10-07 23:33:52 +04:00
CI
d214881720 chore: release version v1.3.1 2024-10-07 09:58:06 +00:00
Dmitry Popov
e66c125dbf chore: release version v1.3.0 2024-10-07 13:57:34 +04:00
CI
9862bcfa05 chore: release version v1.3.0 2024-10-07 07:54:23 +00:00
Aleksei Chichenkov
0ac5451bef Merge pull request #23 from wanderer-industries/fix-signatures-sort
Fix signatures sort
2024-10-07 10:53:45 +03:00
CI
669479b815 chore: release version v1.2.10 2024-10-07 07:51:56 +00:00
Ryan Olds
2721130566 Added DATABASE_SSL_VERIFY_NONE env var (#21) 2024-10-07 11:51:26 +04:00
achichenkov
6e33ad943f feat(Map): Fix default sort
Fixes #22
2024-10-07 10:24:54 +03:00
achichenkov
f4b7357802 feat(Map): Remove resizible and fix styles of column sorting
Fixes #22
2024-10-07 09:31:01 +03:00
achichenkov
7a404a7e6a Merge branch 'refs/heads/main' into fix-signatures-sort
# Conflicts:
#	assets/js/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent/SystemSignaturesContent.tsx
2024-10-07 09:13:35 +03:00
CI
5158700a79 chore: release version v1.2.9 2024-10-07 06:11:35 +00:00
Aleksei Chichenkov
41d10c1b47 Merge pull request #20 from ryanrolds/sig_sort_local_storage
Persist the signature sort between sessions and header improvements
2024-10-07 09:11:09 +03:00
achichenkov
3aaac91f07 feat(Map): Revision of sorting from also adding ability to sort all columns
Fixes #2
2024-10-07 09:08:54 +03:00
Ryan R. Olds
ea7ff080b8 Undid some formatting changes 2024-10-06 15:26:42 -07:00
Ryan R. Olds
b5270958eb Undid some formatting changes 2024-10-06 15:26:21 -07:00
Ryan R. Olds
b0a38eab8c Signature header improvements 2024-10-06 15:19:58 -07:00
Ryan R. Olds
0a478e82ba Persist the signature sort between sessions 2024-10-06 14:46:04 -07:00
CI
02d97a009c chore: release version v1.2.8 2024-10-06 13:13:12 +00:00
Ryan Olds
33940cdb9b Sortable signatures (#18)
feat(Signatures): Make signatures sortable
2024-10-06 17:12:47 +04:00
CI
7a63f9ee6b chore: release version v1.2.7 2024-10-05 07:55:07 +00:00
Dmitry Popov
89b41fff59 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-05 11:54:38 +04:00
Dmitry Popov
7a15f71528 chore: release version v1.2.5 2024-10-05 11:54:35 +04:00
CI
cdce2f8761 chore: release version v1.2.6 2024-10-05 07:39:43 +00:00
Dmitry Popov
a2470bbe47 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-05 11:39:17 +04:00
Dmitry Popov
dbdf1ddce0 fix(Core): Stability & performance improvements 2024-10-05 11:39:13 +04:00
CI
f767e42e6f chore: release version v1.2.5 2024-10-04 21:56:47 +00:00
Dmitry Popov
3051eb6369 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-05 01:56:19 +04:00
Dmitry Popov
a41faddca3 fix(Core): Add system "true security" correction 2024-10-05 01:56:16 +04:00
CI
469038730e chore: release version v1.2.4 2024-10-03 09:27:53 +00:00
Dmitry Popov
b1fe5d2453 fix(Map): Remove duplicate connections 2024-10-03 13:27:21 +04:00
CI
f43e717da0 chore: release version v1.2.3 2024-10-02 17:52:44 +00:00
Dmitry Popov
95c8d4eef8 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-02 21:52:08 +04:00
Dmitry Popov
747ca0ee82 fix(Map): Fix map loading after select a different map. 2024-10-02 21:52:04 +04:00
CI
35a0184ec3 chore: release version v1.2.2 2024-10-02 12:50:16 +00:00
Dmitry Popov
96e1e5328c Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-02 16:49:27 +04:00
Dmitry Popov
7f98d6a0d8 chore: release version v1.2.0 2024-10-02 16:49:23 +04:00
CI
0194e25696 chore: release version v1.2.1 2024-10-02 12:48:06 +00:00
Dmitry Popov
189442e50f Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-10-02 16:47:34 +04:00
Dmitry Popov
d9bed070ec fix(ACL): Fix allowing to save map/access list with empty owner set 2024-10-02 16:47:29 +04:00
CI
a6193da8b5 chore: release version v1.2.0 2024-09-29 19:01:16 +00:00
Aleksei Chichenkov
50d35b207d Merge pull request #14 from wanderer-industries/feat-wnd-13
feat(Map): Add ability to open jump planner from routes
2024-09-29 22:00:44 +03:00
achichenkov
19eb45bfa1 feat(Map): Add ability to open jump planner from routes
Fixes #13
2024-09-29 21:47:35 +03:00
CI
01e0b24d9d chore: release version v1.1.0 2024-09-29 15:07:13 +00:00
Aleksei Chichenkov
3c8024b16c Merge pull request #12 from wanderer-industries/feat-wnd-11
feat(Map): Add highlighting for imperial space systems depends on fac…
2024-09-29 18:06:41 +03:00
achichenkov
4c0ad0dd66 feat(Map): Add highlighting for imperial space systems depends on faction
Fixes #11
2024-09-29 17:57:07 +03:00
CI
501840086b chore: release version v1.0.23 2024-09-25 09:52:47 +00:00
Dmitry Popov
240b180857 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-09-25 13:52:14 +04:00
Dmitry Popov
2bc5d0aaea fix(Map): Main map doesn't load back after refreshing/switching pages
fixes #8
2024-09-25 13:52:10 +04:00
CI
df66aa79b8 chore: release version v1.0.22 2024-09-25 08:24:50 +00:00
Dmitry Popov
6ea6a59ce3 fix(Map): Main map doesn't load back after refreshing/switching pages
fixes #8
2024-09-25 12:24:14 +04:00
CI
f3afa4d9d2 chore: release version v1.0.21 2024-09-24 20:21:54 +00:00
Dmitry Popov
e33d81eda1 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-09-25 00:21:17 +04:00
Dmitry Popov
0fc4863dc4 fix(Map): Main map doesn't load back after refreshing/switching pages
fixes #8
2024-09-25 00:21:14 +04:00
278 changed files with 18270 additions and 9246 deletions

View File

@@ -1,12 +1,7 @@
FROM elixir:1.16-otp-25
FROM elixir:1.17-otp-27
RUN apt update -yq
RUN apt install -yq curl gnupg mc inotify-tools
RUN apt install -yq curl gnupg
RUN apt --fix-broken install
RUN apt remove -y nodejs nodejs-doc
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
RUN apt install -y nodejs
RUN npm install --global yarn
RUN mix local.hex --force

View File

@@ -2,7 +2,7 @@ version: "0.1"
services:
db:
image: postgres:14.3
image: postgres:13-alpine
restart: always
environment:
POSTGRES_USER: postgres
@@ -10,13 +10,13 @@ services:
ports:
- "5432:5432"
volumes:
- db:/var/lib/postgresql/data
- db-new:/var/lib/postgresql/data
wanderer:
environment:
PORT: 8000
DB_HOST: db
WEB_APP_URL: "http://localhost:4444"
WEB_APP_URL: "http://localhost:8000"
ERL_AFLAGS: "-kernel shell_history enabled"
build:
context: .
@@ -33,4 +33,4 @@ services:
volumes:
elixir-artifacts: {}
db: {}
db-new: {}

View File

@@ -58,6 +58,8 @@ jobs:
otp: ["27"]
elixir: ["1.17"]
node-version: ["18.x"]
outputs:
commit_hash: ${{ steps.generate-changelog.outputs.commit_hash }}
steps:
- name: Prepare
run: |
@@ -108,16 +110,17 @@ jobs:
run: mix compile
- name: Generate Changelog & Update Tag Version
id: generate-changelog
run: |
git config --global user.name 'CI'
git config --global user.email 'ci@users.noreply.github.com'
mix git_ops.release --force-patch --yes
git push --follow-tags
echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
docker:
name: 🛠 Build Docker Images
needs:
- build
needs: build
runs-on: ubuntu-22.04
permissions:
checks: write
@@ -141,6 +144,7 @@ jobs:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
with:
ref: ${{ needs.build.outputs.commit_hash }}
fetch-depth: 0
- name: Prepare Changelog
@@ -189,6 +193,30 @@ jobs:
- name: Image digest
run: echo ${{ steps.build.outputs.digest }}
- uses: markpatterson27/markdown-to-output@v1
id: extract-changelog
with:
filepath: CHANGELOG.md
- name: Get content
uses: 2428392/gh-truncate-string-action@v1.3.0
id: get-content
with:
stringToTruncate: |
📣 Wanderer new release available 🎉
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
${{ steps.extract-changelog.outputs.body }}
maxLength: 500
truncationSymbol: "…"
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ steps.get-content.outputs.string }}
create-release:
name: 🏷 Create Release
runs-on: ubuntu-22.04

View File

@@ -18,4 +18,4 @@ jobs:
key: ${{ secrets.SSH_KEY }}
port: ${{ secrets.PORT }}
script: |
/app/release/linux/deploy.sh ${{ github.event.release.tag_name }}
/home/wanderer/app/deploy.sh ${{ github.event.release.tag_name }}

2
.gitignore vendored
View File

@@ -12,6 +12,8 @@
# Ignore assets that are produced by build tools.
/priv/static/assets/
/priv/static/icons/
/priv/static/images/
/priv/static/*.js
/priv/static/*.css

View File

@@ -1,3 +1,3 @@
erlang 25.3
elixir 1.16-otp-25
erlang 26.2.5.5
elixir 1.17.3-otp-26
nodejs 18.0.0

View File

@@ -2,44 +2,567 @@
<!-- changelog -->
## [v1.0.20](https://github.com/wanderer-industries/wanderer/compare/v1.0.19...v1.0.20) (2024-09-23)
## [v1.29.5](https://github.com/wanderer-industries/wanderer/compare/v1.29.4...v1.29.5) (2024-12-14)
### Bug Fixes:
* Core: Fix character trackers cleanup
## [v1.29.4](https://github.com/wanderer-industries/wanderer/compare/v1.29.3...v1.29.4) (2024-12-10)
### Bug Fixes:
* Core: Small fixes
## [v1.29.3](https://github.com/wanderer-industries/wanderer/compare/v1.29.2...v1.29.3) (2024-12-07)
### Bug Fixes:
* Core: Increased eve DB data download timeout
## [v1.29.2](https://github.com/wanderer-industries/wanderer/compare/v1.29.1...v1.29.2) (2024-12-07)
### Bug Fixes:
* Core: Fix unpkg CDN issues, fix Abyssals sites adding as systems on map
## [v1.29.1](https://github.com/wanderer-industries/wanderer/compare/v1.29.0...v1.29.1) (2024-12-05)
## [v1.29.0](https://github.com/wanderer-industries/wanderer/compare/v1.28.1...v1.29.0) (2024-12-05)
### Features:
* Signatures: Show 'Unsplashed' signatures on the map (optionally)
## [v1.28.1](https://github.com/wanderer-industries/wanderer/compare/v1.28.0...v1.28.1) (2024-12-04)
## [v1.28.0](https://github.com/wanderer-industries/wanderer/compare/v1.27.1...v1.28.0) (2024-12-04)
### Features:
* Map: Added an option to show 'Offline characters' to map admins & managers only
## [v1.27.1](https://github.com/wanderer-industries/wanderer/compare/v1.27.0...v1.27.1) (2024-12-04)
### Bug Fixes:
* Map: Fix 'On the map' visibility
## [v1.27.0](https://github.com/wanderer-industries/wanderer/compare/v1.26.1...v1.27.0) (2024-12-03)
### Features:
* Map: Hide 'On the map' list for 'Viewer' role
## [v1.26.1](https://github.com/wanderer-industries/wanderer/compare/v1.26.0...v1.26.1) (2024-12-03)
### Bug Fixes:
* Signatures: Fix error on splash wh
## [v1.26.0](https://github.com/wanderer-industries/wanderer/compare/v1.25.2...v1.26.0) (2024-12-03)
### Features:
* Signatures: Keep 'Lazy delete' enabled setting
## [v1.25.2](https://github.com/wanderer-industries/wanderer/compare/v1.25.1...v1.25.2) (2024-12-01)
### Bug Fixes:
* Signatures: Fix lazy delete on system switch
## [v1.25.1](https://github.com/wanderer-industries/wanderer/compare/v1.25.0...v1.25.1) (2024-11-28)
### Bug Fixes:
* Signatures: Fix colors & add 'Backspace' hotkey to delete signatures
## [v1.25.0](https://github.com/wanderer-industries/wanderer/compare/v1.24.2...v1.25.0) (2024-11-28)
### Features:
* Signatures: Automatically remove signature if linked system removed
## [v1.24.2](https://github.com/wanderer-industries/wanderer/compare/v1.24.1...v1.24.2) (2024-11-27)
### Bug Fixes:
* Signatures: Fix paste signatures
## [v1.24.1](https://github.com/wanderer-industries/wanderer/compare/v1.24.0...v1.24.1) (2024-11-27)
## [v1.24.0](https://github.com/wanderer-industries/wanderer/compare/v1.23.0...v1.24.0) (2024-11-27)
### Features:
* Signatures: Added "Lazy delete" option & got rid of update popup
## [v1.23.0](https://github.com/wanderer-industries/wanderer/compare/v1.22.0...v1.23.0) (2024-11-26)
### Features:
* Map: Lock systems available to manager/admin roles only (#75)
* Map: Lock systems available to manager/admin roles only
* Map: Fix add system & add acl member select behaviour
## [v1.22.0](https://github.com/wanderer-industries/wanderer/compare/v1.21.0...v1.22.0) (2024-11-26)
### Features:
* Map: Rework design of checkboxes in Signatures settings dialog. Rework design of checkboxes in Routes settings dialog. Now signature will deleteing by Delete hotkey was Backspace. Fixed size of group column in signatures list. Instead Updated column will be Added, updated may be turn on in settings. (#76)
## [v1.21.0](https://github.com/wanderer-industries/wanderer/compare/v1.20.1...v1.21.0) (2024-11-24)
### Features:
* Map: add new gate design, change EOL placement
## [v1.20.1](https://github.com/wanderer-industries/wanderer/compare/v1.20.0...v1.20.1) (2024-11-22)
## [v1.20.0](https://github.com/wanderer-industries/wanderer/compare/v1.19.3...v1.20.0) (2024-11-22)
### Features:
* Core: Add connection type for Gates, add new Update logic
## [v1.19.3](https://github.com/wanderer-industries/wanderer/compare/v1.19.2...v1.19.3) (2024-11-20)
### Bug Fixes:
* Core: Fix adding systems on splash (#71)
* Core: Fix adding systems on splash
## [v1.19.2](https://github.com/wanderer-industries/wanderer/compare/v1.19.1...v1.19.2) (2024-11-19)
## [v1.19.1](https://github.com/wanderer-industries/wanderer/compare/v1.19.0...v1.19.1) (2024-11-19)
## [v1.19.0](https://github.com/wanderer-industries/wanderer/compare/v1.18.1...v1.19.0) (2024-11-19)
### Features:
* Signatures: Add user setting to show Inserted time in a separate column
## [v1.18.1](https://github.com/wanderer-industries/wanderer/compare/v1.18.0...v1.18.1) (2024-11-17)
## [v1.18.0](https://github.com/wanderer-industries/wanderer/compare/v1.17.0...v1.18.0) (2024-11-16)
### Features:
* Map: a lot of design issues
## [v1.17.0](https://github.com/wanderer-industries/wanderer/compare/v1.16.1...v1.17.0) (2024-11-15)
### Features:
* Signatures: Add user setting to show Description in a separate column
## [v1.16.1](https://github.com/wanderer-industries/wanderer/compare/v1.16.0...v1.16.1) (2024-11-15)
### Bug Fixes:
* Signatures: Fix signature stored filters
## [v1.16.0](https://github.com/wanderer-industries/wanderer/compare/v1.15.5...v1.16.0) (2024-11-15)
### Features:
* Signatures: Add additional filters support to signature list, show description icon
## [v1.15.5](https://github.com/wanderer-industries/wanderer/compare/v1.15.4...v1.15.5) (2024-11-14)
## [v1.15.4](https://github.com/wanderer-industries/wanderer/compare/v1.15.3...v1.15.4) (2024-11-14)
### Bug Fixes:
* Core: Untracked characters still tracked on map (#63)
## [v1.15.3](https://github.com/wanderer-industries/wanderer/compare/v1.15.2...v1.15.3) (2024-11-13)
## [v1.15.2](https://github.com/wanderer-industries/wanderer/compare/v1.15.1...v1.15.2) (2024-11-07)
## [v1.15.1](https://github.com/wanderer-industries/wanderer/compare/v1.15.0...v1.15.1) (2024-11-07)
### Bug Fixes:
* Dev: Update .devcontainer instructions
## [v1.15.0](https://github.com/wanderer-industries/wanderer/compare/v1.14.1...v1.15.0) (2024-11-07)
### Features:
* Connections: Add connection mark EOL time (#56)
## [v1.14.1](https://github.com/wanderer-industries/wanderer/compare/v1.14.0...v1.14.1) (2024-11-06)
### Bug Fixes:
* Core: Fix character tracking permissions
## [v1.14.0](https://github.com/wanderer-industries/wanderer/compare/v1.13.12...v1.14.0) (2024-11-05)
### Features:
* ACL: Add an ability to assign member role without DnD
## [v1.13.12](https://github.com/wanderer-industries/wanderer/compare/v1.13.11...v1.13.12) (2024-11-04)
### Bug Fixes:
* Map: Fix system revert issues
## [v1.13.11](https://github.com/wanderer-industries/wanderer/compare/v1.13.10...v1.13.11) (2024-11-02)
### Bug Fixes:
* Map: Fix system revert issues
## [v1.13.10](https://github.com/wanderer-industries/wanderer/compare/v1.13.9...v1.13.10) (2024-11-01)
### Bug Fixes:
* Map: Fix system revert issues
## [v1.13.9](https://github.com/wanderer-industries/wanderer/compare/v1.13.8...v1.13.9) (2024-11-01)
## [v1.13.8](https://github.com/wanderer-industries/wanderer/compare/v1.13.7...v1.13.8) (2024-10-28)
## [v1.13.0](https://github.com/wanderer-industries/wanderer/compare/v1.12.11...v1.13.0) (2024-10-28)
### Features:
* Core: Use ESI /characters/affiliation API
## [v1.12.4](https://github.com/wanderer-industries/wanderer/compare/v1.12.3...v1.12.4) (2024-10-21)
### Bug Fixes:
* Map: Fix systems cleanup
## [v1.12.3](https://github.com/wanderer-industries/wanderer/compare/v1.12.2...v1.12.3) (2024-10-18)
### Bug Fixes:
* Map: Fix regression issues
## [v1.12.1](https://github.com/wanderer-industries/wanderer/compare/v1.12.0...v1.12.1) (2024-10-16)
### Bug Fixes:
* Map: Fix system add error after map page refresh
## [v1.12.0](https://github.com/wanderer-industries/wanderer/compare/v1.11.5...v1.12.0) (2024-10-16)
### Features:
* Map: Prettify user settings
## [v1.11.0](https://github.com/wanderer-industries/wanderer/compare/v1.10.0...v1.11.0) (2024-10-14)
### Features:
* Map: Add map level option to store custom labels
## [v1.10.0](https://github.com/wanderer-industries/wanderer/compare/v1.9.0...v1.10.0) (2024-10-13)
### Features:
* Map: Link signature on splash
## [v1.5.0](https://github.com/wanderer-industries/wanderer/compare/v1.4.0...v1.5.0) (2024-10-11)
### Features:
* Map: Follow Character on Map and auto select their current system
## [v1.3.6](https://github.com/wanderer-industries/wanderer/compare/v1.3.5...v1.3.6) (2024-10-09)
### Bug Fixes:
* Signatures: Signatures update fixes
## [v1.3.0](https://github.com/wanderer-industries/wanderer/compare/v1.2.10...v1.3.0) (2024-10-07)
### Features:
* Map: Fix default sort
* Map: Remove resizible and fix styles of column sorting
* Map: Revision of sorting from also adding ability to sort all columns
## [v1.2.6](https://github.com/wanderer-industries/wanderer/compare/v1.2.5...v1.2.6) (2024-10-05)
### Bug Fixes:
* Core: Stability & performance improvements
## [v1.2.5](https://github.com/wanderer-industries/wanderer/compare/v1.2.4...v1.2.5) (2024-10-04)
### Bug Fixes:
* Core: Add system "true security" correction
## [v1.2.4](https://github.com/wanderer-industries/wanderer/compare/v1.2.3...v1.2.4) (2024-10-03)
### Bug Fixes:
* Map: Remove duplicate connections
## [v1.2.3](https://github.com/wanderer-industries/wanderer/compare/v1.2.2...v1.2.3) (2024-10-02)
### Bug Fixes:
* Map: Fix map loading after select a different map.
## [v1.2.1](https://github.com/wanderer-industries/wanderer/compare/v1.2.0...v1.2.1) (2024-10-02)
### Bug Fixes:
* ACL: Fix allowing to save map/access list with empty owner set
## [v1.2.0](https://github.com/wanderer-industries/wanderer/compare/v1.1.0...v1.2.0) (2024-09-29)
### Features:
* Map: Add ability to open jump planner from routes
## [v1.1.0](https://github.com/wanderer-industries/wanderer/compare/v1.0.23...v1.1.0) (2024-09-29)
### Features:
* Map: Add highlighting for imperial space systems depends on faction
## [v1.0.23](https://github.com/wanderer-industries/wanderer/compare/v1.0.22...v1.0.23) (2024-09-25)
### Bug Fixes:
* Map: Main map doesn't load back after refreshing/switching pages
## [v1.0.22](https://github.com/wanderer-industries/wanderer/compare/v1.0.21...v1.0.22) (2024-09-25)
### Bug Fixes
* Map: Main map doesn't load back after refreshing/switching pages
## [v1.0.21](https://github.com/wanderer-industries/wanderer/compare/v1.0.20...v1.0.21) (2024-09-24)
### Bug Fixes
* Map: Main map doesn't load back after refreshing/switching pages
## [v1.0.20](https://github.com/wanderer-industries/wanderer/compare/v1.0.19...v1.0.20) (2024-09-23)
### Bug Fixes
* core: Small fixes & improvements
## [v1.0.19](https://github.com/wanderer-industries/wanderer/compare/v1.0.18...v1.0.19) (2024-09-23)
### Bug Fixes:
### Bug Fixes
* ACL: Fix adding empty members list
## [v1.0.18](https://github.com/wanderer-industries/wanderer/compare/v1.0.17...v1.0.18) (2024-09-22)
### Bug Fixes:
### Bug Fixes
* ACL: Cant delete ACL list after map deletion #5
## [v1.0.17](https://github.com/wanderer-industries/wanderer/compare/v1.0.16...v1.0.17) (2024-09-21)
## [v1.0.16](https://github.com/wanderer-industries/wanderer/compare/v1.0.15...v1.0.16) (2024-09-21)
### Bug Fixes:
### Bug Fixes
* Map: commented console log
@@ -49,103 +572,48 @@
## [v1.0.15](https://github.com/wanderer-industries/wanderer/compare/v1.0.14...v1.0.15) (2024-09-21)
### Bug Fixes:
### Bug Fixes
* map: Show a proper user notification if map was deleted/archived
## [v1.0.14](https://github.com/wanderer-industries/wanderer/compare/v1.0.13...v1.0.14) (2024-09-21)
## [v1.0.13](https://github.com/wanderer-industries/wanderer/compare/v1.0.12...v1.0.13) (2024-09-21)
### Bug Fixes:
### Bug Fixes
* tracking: Ensure user has at least one character tracked to work with map
## [v1.0.12](https://github.com/wanderer-industries/wanderer/compare/v1.0.11...v1.0.12) (2024-09-20)
### Bug Fixes:
### Bug Fixes
* audit: Hide character for non-character map activities
## [v1.0.11](https://github.com/wanderer-industries/wanderer/compare/v1.0.10...v1.0.11) (2024-09-20)
## [v1.0.10](https://github.com/wanderer-industries/wanderer/compare/v1.0.9...v1.0.10) (2024-09-19)
### Bug Fixes:
### Bug Fixes
* signatures: Fix update signatures error if no character tracked on map
## [v1.0.9](https://github.com/wanderer-industries/wanderer/compare/v1.0.8...v1.0.9) (2024-09-19)
### Bug Fixes:
### Bug Fixes
* core: Fix system add error if it's already added on map
## [v1.0.8](https://github.com/wanderer-industries/wanderer/compare/v1.0.7...v1.0.8) (2024-09-19)
### Bug Fixes:
### Bug Fixes
* docker: Fix DB connection in docker-compose internal network
## [v1.0.7](https://github.com/wanderer-industries/wanderer/compare/v1.0.6...v1.0.7) (2024-09-19)
## [v1.0.6](https://github.com/wanderer-industries/wanderer/compare/v1.0.5...v1.0.6) (2024-09-18)
## [v1.0.5](https://github.com/wanderer-industries/wanderer/compare/v1.0.4...v1.0.5) (2024-09-18)
## [v1.0.4](https://github.com/wanderer-industries/wanderer/compare/v1.0.3...v1.0.4) (2024-09-18)
### Bug Fixes:
### Bug Fixes
* core: skip search results for failed character info request
## [v1.0.3](https://github.com/wanderer-industries/wanderer/compare/v1.0.2...v1.0.3) (2024-09-18)
## [v1.0.2](https://github.com/wanderer-industries/wanderer/compare/v1.0.1...v1.0.2) (2024-09-18)
## [v1.0.1](https://github.com/wanderer-industries/wanderer/compare/v1.0.0...v1.0.1) (2024-09-18)

View File

@@ -20,11 +20,11 @@ Interested to learn more? [Check more on our website](https://wanderer.ltd/news)
Wanderer is open source project and we have a free as in beer and self-hosted solution called [Wanderer Community Edition (CE)](https://wanderer.ltd/news/community-edition). Here are the differences between Wanderer and Wanderer CE:
| | Wanderer Cloud | Wanderer Community Edition |
| ------------- | ------------- | ------------- |
| **Infrastructure management** | Easy and convenient. It takes 2 minutes to register your character and create a map. We manage everything so you dont have to worry about anything and can focus on gameplay. | You do it all yourself. You need to get a server and you need to manage your infrastructure. You are responsible for installation, maintenance, upgrades, server capacity, uptime, backup, security, stability, consistency, loading time and so on.|
| **Release schedule** | Continuously developed and improved with new features and updates multiple times per week. | Latest features and improvements won't be immediately available.|
| **Server location** | All visitor data is exclusively processed on EU-owned cloud infrastructure. We keep your site data on a secure, encrypted and green energy powered server in Germany. This ensures that your site data is protected by the strict European Union data privacy laws and ensures compliance with GDPR. Your website data never leaves the EU. | You have full control and can host your instance on any server in any country that you wish. Host it on a server in your basement or host it with any cloud provider wherever you want, even those that are not GDPR compliant.|
| | Wanderer Cloud | Wanderer Community Edition |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Infrastructure management** | Easy and convenient. It takes 2 minutes to register your character and create a map. We manage everything so you dont have to worry about anything and can focus on gameplay. | You do it all yourself. You need to get a server and you need to manage your infrastructure. You are responsible for installation, maintenance, upgrades, server capacity, uptime, backup, security, stability, consistency, loading time and so on. |
| **Release schedule** | Continuously developed and improved with new features and updates multiple times per week. | Latest features and improvements won't be immediately available. |
| **Server location** | All visitor data is exclusively processed on EU-owned cloud infrastructure. We keep your site data on a secure, encrypted and green energy powered server in Germany. This ensures that your site data is protected by the strict European Union data privacy laws and ensures compliance with GDPR. Your website data never leaves the EU. | You have full control and can host your instance on any server in any country that you wish. Host it on a server in your basement or host it with any cloud provider wherever you want, even those that are not GDPR compliant. |
Interested in self-hosting Wanderer CE on your server? Take a look at our [Wanderer CE installation instructions](https://github.com/wanderer-industries/community-edition/).
@@ -54,7 +54,13 @@ Now you can visit [`localhost:8000`](http://localhost:8000) from your browser.
#### Using .devcontainer
- Run devcontainer
- See how to start server in #setup section
- Install additional dependencies inside Dev container
- `root@0d0a785313b6:/app# apt update`
- `root@0d0a785313b6:/app# curl -sL https://deb.nodesource.com/setup_18.x | bash -`
- `root@0d0a785313b6:/app# apt-get install nodejs inotify-tools -y`
- `root@0d0a785313b6:/app# mix setup`
- See how to run server in #Run section
#### Using nix flakes

View File

@@ -28,6 +28,12 @@ body {
font-weight: 500;
}
#bg-canvas {
position: absolute;
width: 100vw;
height: 100vh;
}
.ccp-font {
font-family: 'Shentox', 'Rogan', sans-serif !important;
}
@@ -460,3 +466,467 @@ body {
transform: rotate(-360deg);
}
}
/* Map refresh */
.socket {
scale: 0.5;
width: 150px;
height: 150px;
left: 50%;
/* margin-left: -75px; */
top: 50%;
/* margin-top: -50px; */
}
.hex-brick {
background: #000;
width: 30px;
height: 17px;
position: absolute;
top: 5px;
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
}
.hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
.h2 {
transform: rotate(60deg);
-webkit-transform: rotate(60deg);
}
.h3 {
transform: rotate(-60deg);
-webkit-transform: rotate(-60deg);
}
.gel {
height: 30px;
width: 30px;
transition: all 0.3s;
-webkit-transition: all 0.3s;
position: absolute;
top: 50%;
left: 50%;
}
.center-gel {
margin-left: -15px;
margin-top: -15px;
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
}
.c1 {
margin-left: -47px;
margin-top: -15px;
}
.c2 {
margin-left: -31px;
margin-top: -43px;
}
.c3 {
margin-left: 1px;
margin-top: -43px;
}
.c4 {
margin-left: 17px;
margin-top: -15px;
}
.c5 {
margin-left: -31px;
margin-top: 13px;
}
.c6 {
margin-left: 1px;
margin-top: 13px;
}
.c7 {
margin-left: -63px;
margin-top: -43px;
}
.c8 {
margin-left: 33px;
margin-top: -43px;
}
.c9 {
margin-left: -15px;
margin-top: 41px;
}
.c10 {
margin-left: -63px;
margin-top: 13px;
}
.c11 {
margin-left: 33px;
margin-top: 13px;
}
.c12 {
margin-left: -15px;
margin-top: -71px;
}
.c13 {
margin-left: -47px;
margin-top: -71px;
}
.c14 {
margin-left: 17px;
margin-top: -71px;
}
.c15 {
margin-left: -47px;
margin-top: 41px;
}
.c16 {
margin-left: 17px;
margin-top: 41px;
}
.c17 {
margin-left: -79px;
margin-top: -15px;
}
.c18 {
margin-left: 49px;
margin-top: -15px;
}
.c19 {
margin-left: -63px;
margin-top: -99px;
}
.c20 {
margin-left: 33px;
margin-top: -99px;
}
.c21 {
margin-left: 1px;
margin-top: -99px;
}
.c22 {
margin-left: -31px;
margin-top: -99px;
}
.c23 {
margin-left: -63px;
margin-top: 69px;
}
.c24 {
margin-left: 33px;
margin-top: 69px;
}
.c25 {
margin-left: 1px;
margin-top: 69px;
}
.c26 {
margin-left: -31px;
margin-top: 69px;
}
.c27 {
margin-left: -79px;
margin-top: -15px;
}
.c28 {
margin-left: -95px;
margin-top: -43px;
}
.c29 {
margin-left: -95px;
margin-top: 13px;
}
.c30 {
margin-left: 49px;
margin-top: 41px;
}
.c31 {
margin-left: -79px;
margin-top: -71px;
}
.c32 {
margin-left: -111px;
margin-top: -15px;
}
.c33 {
margin-left: 65px;
margin-top: -43px;
}
.c34 {
margin-left: 65px;
margin-top: 13px;
}
.c35 {
margin-left: -79px;
margin-top: 41px;
}
.c36 {
margin-left: 49px;
margin-top: -71px;
}
.c37 {
margin-left: 81px;
margin-top: -15px;
}
.r1 {
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.2s;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.2s;
}
.r2 {
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.4s;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.4s;
}
.r3 {
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.6s;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.6s;
}
.r1 > .hex-brick {
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.2s;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.2s;
}
.r1 > .hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
.r2 > .hex-brick {
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.4s;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.4s;
}
.r2 > .hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
.r3 > .hex-brick {
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.6s;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.6s;
}
.r3 > .hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
@keyframes pulse-version {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(0.01);
transform: scale(0.01);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@keyframes fade {
0% {
background: #09d0e2;
}
50% {
background: #8ae6ee;
}
100% {
background: #09d0e2;
}
}
@keyframes fade-active {
0% {
background: #ff52d9;
}
50% {
background: #ff52d9;
}
100% {
background: #ff52d9;
}
}
@-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(0.01);
transform: scale(0.01);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@-webkit-keyframes fade {
0% {
background: #abf8ff;
}
50% {
background: #389ca6;
}
100% {
background: #abf8ff;
}
}
/* Map refresh END */
.inputContainer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
}
.inputContainer > span:nth-child(1),
.inputContainer > label:nth-child(1) {
color: var(--gray-200);
font-size: 13px;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.inputContainer > :nth-child(2) {
border-bottom: 2px dotted #3f3f3f;
height: 1px;
margin: 0 12px;
}
.smallInputSwitch {
height: 100%;
display: flex;
align-items: center;
}
.smallInputSwitch .p-inputswitch {
height: 1rem;
width: 2rem;
}
.smallInputSwitch .p-inputswitch.p-inputswitch-checked .p-inputswitch-slider::before {
transform: translateX(1rem);
}
.smallInputSwitch .p-inputswitch.p-highlight .p-inputswitch-slider:before {
transform: translateX(1rem);
}
.smallInputSwitch .p-inputswitch .p-inputswitch-slider::before {
width: 0.8rem;
height: 0.8rem;
margin-top: -0.4rem;
margin-left: -3px;
}
.checkboxRoot.sizeXS {
width: 14px;
height: 14px;
}
.checkboxRoot.sizeXS .p-checkbox-box,
.checkboxRoot.sizeXS .p-checkbox-input {
width: 14px;
height: 14px;
}
.checkboxRoot.sizeM {
width: 16px;
height: 16px;
}
.checkboxRoot.sizeM .p-checkbox-box,
.checkboxRoot.sizeM .p-checkbox-input {
width: 16px;
height: 16px;
}

View File

@@ -15,11 +15,10 @@ const ErrorFallback = () => {
};
export default function MapRoot({ hooks }) {
const mapRef = useRef<MapHandlers>(null);
const providerRef = useRef<MapHandlers>(null);
const hooksRef = useRef<any>(hooks);
const mapperHandlerRefs = useRef([mapRef, providerRef]);
const mapperHandlerRefs = useRef([providerRef]);
const { handleCommand, handleMapEvent, handleMapEvents } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
@@ -41,7 +40,7 @@ export default function MapRoot({ hooks }) {
return (
<PrimeReactProvider>
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand} mapRef={mapRef}>
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand}>
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
<ReactFlowProvider>
<MapRootContent />

View File

@@ -67,3 +67,44 @@
}
}
.p-sortable-column {
font-size: 12px;
font-weight: bold;
padding: 3px 4px;
}
.p-selectable-row td {
padding: 4px 4px;
}
.p-sortable-column > .p-column-header-content > span:last-child {
transform: scale(0.7);
& > svg {
margin-left: 4px;
}
}
.p-dropdown-label, .p-inputtext {
padding: 0.25rem 0.75rem;
font-size: 14px;
}
.p-dropdown-item {
padding: 0.25rem 0.5rem;
font-size: 14px;
}
.p-dropdown-item-group {
padding: 0.25rem 0.75rem;
font-size: 14px;
}
.p-dropdown-trigger {
width: 14px;
margin: 0 12px;
}
.p-dropdown-empty-message {
padding: 0.25rem 0.5rem;
}

View File

@@ -1,5 +1,5 @@
/* Основной класс диалога */
.p-dialog {
body .p-dialog {
display: flex;
flex-direction: column;
//position: absolute;
@@ -7,11 +7,26 @@
left: 0;
//visibility: hidden;
overflow: hidden;
border-radius: 6px;
border-radius: 2px;
box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2);
transition: box-shadow 0.3s;
background: #fff;
z-index: 1000;
border: 1px solid #212121;
background: var(--surface-h);
color: var(--text-color);
.p-dialog-header {
background: #171717 !important;
color: var(--text-color);
.p-dialog-header-icon:focus-visible {
box-shadow: none !important;
}
}
.p-dialog-footer {
border-top: 1px solid var(--surface-d);
}
}
/* Стиль видимого диалога */
@@ -45,12 +60,12 @@
justify-content: space-between;
padding: 1rem;
background: #f4f4f4;
border-bottom: 1px solid #ddd;
//border-bottom: 1px solid #ddd;
}
/* Содержимое диалога */
.p-dialog-content {
padding: 1rem;
padding: 0.5rem;
overflow-y: auto;
flex: 1;
}
@@ -78,23 +93,3 @@
.p-dialog-header-close .pi {
font-size: 1.25rem;
}
/* Тема Saga Blue (пример) */
body .p-dialog {
background: var(--surface-a);
color: var(--text-color);
}
body .p-dialog .p-dialog-header,
body .p-dialog .p-dialog-footer {
background: var(--surface-b);
color: var(--text-color);
}
body .p-dialog .p-dialog-header {
border-bottom: 1px solid var(--surface-d);
}
body .p-dialog .p-dialog-footer {
border-top: 1px solid var(--surface-d);
}

View File

@@ -9,6 +9,7 @@
--surface-d: #3f4b5b;
--surface-e: #2a323d;
--surface-f: #2a323d;
--surface-h: #171717;
--text-color: rgba(255, 255, 255, 0.87);
--text-color-secondary: rgba(255, 255, 255, 0.6);
--primary-color: #8dd0ff;

View File

@@ -1,20 +1,19 @@
import { useCallback } from 'react';
import clsx from 'clsx';
import { useAutoAnimate } from '@formkit/auto-animate/react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
const [parent] = useAutoAnimate();
const { mapRef } = useMapRootState();
const handleSelect = useCallback(
(character: CharacterTypeRaw) => {
mapRef.current?.command(Commands.selectSystem, character?.location?.solar_system_id?.toString());
},
[mapRef],
);
const handleSelect = useCallback((character: CharacterTypeRaw) => {
emitMapEvent({
name: Commands.centerSystem,
data: character?.location?.solar_system_id?.toString(),
});
}, []);
const items = data.map(character => (
<li

View File

@@ -46,7 +46,7 @@ export const useLabelsMenu = (
}
// const labels = getLabels(system.labels);
const hasLabels = labels.list.length > 0;
const hasLabels = labels?.list?.length > 0;
const statusList = hasLabels ? LABELS_ORDER : LABELS_ORDER.slice(1);
return [

View File

@@ -18,7 +18,7 @@ export const useTagMenu = (
ref.current = { onSystemTag, systems, systemId };
return useCallback(() => {
const { onSystemTag, systemId , systems} = ref.current;
const { onSystemTag, systemId, systems } = ref.current;
const system = systemId ? getSystemById(systems, systemId) : undefined;
const isSelectedLetters = AVAILABLE_LETTERS.includes(system?.tag ?? '');

View File

@@ -4,6 +4,7 @@ import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.
import { SolarSystemRawType } from '@/hooks/Mapper/types';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
interface UseContextMenuSystemHandlersProps {
hubs: string[];
@@ -16,8 +17,10 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
const [system, setSystem] = useState<string>();
const ref = useRef({ hubs, system, systems, outCommand });
ref.current = { hubs, system, systems, outCommand };
const { deleteSystems } = useDeleteSystems();
const ref = useRef({ hubs, system, systems, outCommand, deleteSystems });
ref.current = { hubs, system, systems, outCommand, deleteSystems };
const open = useCallback((ev: any, systemId: string) => {
setSystem(systemId);
@@ -27,12 +30,12 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
}, []);
const onDeleteSystem = useCallback(() => {
const { system, outCommand } = ref.current;
const { system, deleteSystems } = ref.current;
if (!system) {
return;
}
outCommand({ type: OutCommand.deleteSystems, data: [system] });
deleteSystems([system]);
setSystem(undefined);
}, []);

View File

@@ -6,6 +6,9 @@ import { PrimeIcons } from 'primereact/api';
import { ContextMenuSystemProps } from '@/hooks/Mapper/components/contexts';
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export const useContextMenuSystemItems = ({
onDeleteSystem,
@@ -25,6 +28,7 @@ export const useContextMenuSystemItems = ({
const getStatus = useStatusMenu(systems, systemId, onSystemStatus);
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
const getWaypointMenu = useWaypointMenu(onWaypointSet);
const canLockSystem = useMapCheckPermissions([UserPermission.LOCK_SYSTEM]);
return useMemo(() => {
const system = systemId ? getSystemById(systems, systemId) : undefined;
@@ -41,6 +45,8 @@ export const useContextMenuSystemItems = ({
<FastSystemActions
systemId={systemId}
systemName={system.system_static_info.solar_system_name}
regionName={system.system_static_info.region_name}
isWH={isWormholeSpace(system.system_static_info.system_class)}
showEdit
onOpenSettings={onOpenSettings}
/>
@@ -58,19 +64,25 @@ export const useContextMenuSystemItems = ({
command: onHubToggle,
},
...(system.locked
? [
{
label: 'Unlock',
icon: PrimeIcons.LOCK_OPEN,
command: onLockToggle,
},
]
? canLockSystem
? [
{
label: 'Unlock',
icon: PrimeIcons.LOCK_OPEN,
command: onLockToggle,
},
]
: []
: [
{
label: 'Lock',
icon: PrimeIcons.LOCK,
command: onLockToggle,
},
...(canLockSystem
? [
{
label: 'Lock',
icon: PrimeIcons.LOCK,
command: onLockToggle,
},
]
: []),
{ separator: true },
{
label: 'Delete',
@@ -80,6 +92,7 @@ export const useContextMenuSystemItems = ({
]),
];
}, [
canLockSystem,
systems,
systemId,
getTags,

View File

@@ -8,17 +8,22 @@ import { getSystemById } from '@/hooks/Mapper/helpers';
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { Route } from '@/hooks/Mapper/types/routes.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export interface ContextMenuSystemInfoProps {
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
hubs: string[];
contextMenuRef: RefObject<ContextMenu>;
systemId: string | undefined;
systemIdFrom?: string | undefined;
systems: SolarSystemRawType[];
onOpenSettings(): void;
onHubToggle(): void;
onAddSystem(): void;
onWaypointSet: WaypointSetContextHandler;
routes: Route[];
}
export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
@@ -30,9 +35,12 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
onAddSystem,
onWaypointSet,
systemId,
systemIdFrom,
hubs,
routes,
}) => {
const getWaypointMenu = useWaypointMenu(onWaypointSet);
const getJumpPlannerMenu = useJumpPlannerMenu(systems, systemIdFrom);
const items: MenuItem[] = useMemo(() => {
const system = systemId ? systemStatics.get(parseInt(systemId)) : undefined;
@@ -41,7 +49,6 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
if (!systemId || !system) {
return [];
}
return [
{
className: classes.FastActions,
@@ -50,12 +57,16 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
<FastSystemActions
systemId={systemId}
systemName={system.solar_system_name}
regionName={system.region_name}
isWH={isWormholeSpace(system.system_class)}
onOpenSettings={onOpenSettings}
/>
);
},
},
{ separator: true },
...getJumpPlannerMenu(system, routes),
...getWaypointMenu(systemId, system.system_class),
{
label: !hubs.includes(systemId) ? 'Add in Routes' : 'Remove from Routes',
@@ -72,7 +83,17 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
]
: []),
];
}, [systemId, systemStatics, systems, getWaypointMenu, hubs, onHubToggle, onAddSystem, onOpenSettings]);
}, [
systemId,
systemStatics,
systems,
getJumpPlannerMenu,
getWaypointMenu,
hubs,
onHubToggle,
onAddSystem,
onOpenSettings,
]);
return (
<>

View File

@@ -1,30 +1,36 @@
import { RefObject, useCallback, useRef, useState } from 'react';
import * as React from 'react';
import { useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { Commands, MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { Commands, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import * as React from 'react';
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
interface UseContextMenuSystemHandlersProps {
hubs: string[];
outCommand: OutCommandHandler;
mapRef: RefObject<MapHandlers>;
}
export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: UseContextMenuSystemHandlersProps) => {
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, mapRef });
ref.current = { hubs, system, outCommand, mapRef };
const ref = useRef({ hubs, system, outCommand });
ref.current = { hubs, system, outCommand };
const open = useCallback((ev: React.SyntheticEvent, systemId: string) => {
setSystem(systemId);
ev.preventDefault();
ctxManager.next('ctxSysInfo', contextMenuRef.current);
contextMenuRef.current?.show(ev);
}, []);
const open = useCallback(
(ev: React.SyntheticEvent, systemId: string, route: (SolarSystemStaticInfoRaw | undefined)[]) => {
setSystem(systemId);
routeRef.current = route;
ev.preventDefault();
ctxManager.next('ctxSysInfo', contextMenuRef.current);
contextMenuRef.current?.show(ev);
},
[],
);
const onHubToggle = useCallback(() => {
const { hubs, system, outCommand } = ref.current;
@@ -42,19 +48,23 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: U
}, []);
const onAddSystem = useCallback(() => {
const { system, outCommand, mapRef } = ref.current;
if (!system) {
const { system: solarSystemId, outCommand } = ref.current;
if (!solarSystemId) {
return;
}
outCommand({
type: OutCommand.addSystem,
data: {
system_id: system,
system_id: solarSystemId,
},
});
setTimeout(() => {
mapRef.current?.command(Commands.selectSystem, system);
emitMapEvent({
name: Commands.centerSystem,
data: solarSystemId,
});
setSystem(undefined);
}, 200);
}, []);

View File

@@ -1,17 +1,17 @@
import { Node } from 'reactflow';
import { useRef, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { SolarSystemRawType } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
export const useContextMenuSystemMultipleHandlers = () => {
const contextMenuRef = useRef<ContextMenu | null>(null);
const { outCommand } = useMapRootState();
const [systems, setSystems] = useState<Node<SolarSystemRawType>[]>();
const { deleteSystems } = useDeleteSystems();
const handleSystemMultipleContext: NodeSelectionMouseHandler = (ev, systems_) => {
setSystems(systems_);
ev.preventDefault();
@@ -19,7 +19,7 @@ export const useContextMenuSystemMultipleHandlers = () => {
contextMenuRef.current?.show(ev);
};
const onDeleteSystems = () => {
const onDeleteSystems = useCallback(() => {
if (!systems) {
return;
}
@@ -29,12 +29,11 @@ export const useContextMenuSystemMultipleHandlers = () => {
return;
}
outCommand({ type: OutCommand.deleteSystems, data: sysToDel });
};
deleteSystems(sysToDel);
}, [deleteSystems, systems]);
return {
handleSystemMultipleContext,
contextMenuRef,
onDeleteSystems,
};

View File

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

View File

@@ -1 +1,3 @@
export * from './useWaypointMenu';
export * from './useJumpPlannerMenu';
export * from './useDeleteSystems';

View File

@@ -0,0 +1,18 @@
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export const useDeleteSystems = () => {
const { outCommand } = useMapRootState();
const deleteSystems = (systemIds: string[]) => {
if (!systemIds || !systemIds.length) {
return;
}
outCommand({ type: OutCommand.deleteSystems, data: systemIds });
};
return {
deleteSystems,
};
};

View File

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

View File

@@ -0,0 +1,129 @@
import { MenuItem } from 'primereact/menuitem';
import { PrimeIcons } from 'primereact/api';
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';
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];
enum JUMP_SHIP_TYPE {
BLACK_OPS = 'Marshal',
JUMP_FREIGHTER = 'Anshar',
RORQUAL = 'Rorqual',
CAPITAL = 'Thanatos',
SUPER_CAPITAL = 'Avatar',
}
export const openJumpPlan = (jumpShipType: JUMP_SHIP_TYPE, from: string, to: string) => {
return window.open(`https://evemaps.dotlan.net/jump/${jumpShipType},544/${from}:${to}`, '_blank');
};
const BRACKET_ICONS = {
npcsuperCarrier_32: '/icons/brackets/npcsuperCarrier_32.png',
carrier_32: '/icons/brackets/carrier_32.png',
battleship_32: '/icons/brackets/battleship_32.png',
freighter_32: '/icons/brackets/freighter_32.png',
};
const renderIcon = (icon: string) => {
return (
<div className="flex justify-center items-center mr-1.5 pt-px">
<img src={icon} style={{ width: 20, height: 20 }} />
</div>
);
};
export const useJumpPlannerMenu = (
systems: SolarSystemRawType[],
systemIdFrom?: string | undefined,
): ((systemId: SolarSystemStaticInfoRaw, routes: Route[]) => MenuItem[]) => {
return useCallback(
(destination: SolarSystemStaticInfoRaw) => {
if (!destination || !systemIdFrom) {
return [];
}
const origin = getSystemById(systems, systemIdFrom)?.system_static_info;
if (!origin) {
return [];
}
const isShowBOorJumpFreighter =
isPossibleSpace(imperialSpace, origin.system_class) && isPossibleSpace(criminalSpace, destination.system_class);
const isShowCapital =
isPossibleSpace(criminalSpace, origin.system_class) && isPossibleSpace(criminalSpace, destination.system_class);
if (!isShowBOorJumpFreighter && !isShowCapital) {
return [];
}
return [
{
label: 'In Jump Planner',
icon: PrimeIcons.SEND,
items: [
...(isShowBOorJumpFreighter
? [
{
label: 'Black Ops',
icon: renderIcon(BRACKET_ICONS.battleship_32),
command: () => {
openJumpPlan(JUMP_SHIP_TYPE.BLACK_OPS, origin.solar_system_name, destination.solar_system_name);
},
},
{
label: 'Jump Freighter',
icon: renderIcon(BRACKET_ICONS.freighter_32),
command: () => {
openJumpPlan(
JUMP_SHIP_TYPE.JUMP_FREIGHTER,
origin.solar_system_name,
destination.solar_system_name,
);
},
},
{
label: 'Rorqual',
icon: renderIcon(BRACKET_ICONS.freighter_32),
command: () => {
openJumpPlan(JUMP_SHIP_TYPE.RORQUAL, origin.solar_system_name, destination.solar_system_name);
},
},
]
: []),
...(isShowCapital
? [
{
label: 'Capital',
icon: renderIcon(BRACKET_ICONS.carrier_32),
command: () => {
openJumpPlan(JUMP_SHIP_TYPE.CAPITAL, origin.solar_system_name, destination.solar_system_name);
},
},
{
label: 'Super Capital',
icon: renderIcon(BRACKET_ICONS.npcsuperCarrier_32),
command: () => {
openJumpPlan(
JUMP_SHIP_TYPE.SUPER_CAPITAL,
origin.solar_system_name,
destination.solar_system_name,
);
},
},
]
: []),
],
},
];
},
[systems, systemIdFrom],
);
};

View File

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

View File

@@ -0,0 +1,36 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMemo } from 'react';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
interface UseSystemInfoProps {
systemId: string;
}
export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
const {
data: { systems, connections },
} = useMapRootState();
const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] });
// eslint-disable-next-line no-console
console.log('JOipP', `systemStatics`, systemStatics);
return useMemo(() => {
const staticInfo = systemStatics.get(parseInt(systemId));
const dynamicInfo = getSystemById(systems, systemId);
if (!staticInfo || !dynamicInfo) {
throw new Error(`Error on getting system ${systemId}`);
}
const leadsTo = connections
.filter(x => [x.source, x.target].includes(systemId))
.map(x => [x.source, x.target])
.flat()
.filter(x => x !== systemId);
return { dynamicInfo, staticInfo, leadsTo };
}, [systemStatics, systemId, systems, connections]);
};

View File

@@ -2,3 +2,7 @@
width: 100%;
height: 100%;
}
.BackgroundAlternateColor {
}

View File

@@ -1,25 +1,25 @@
import React, { ForwardedRef, forwardRef, MouseEvent, useCallback } from 'react';
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect } from 'react';
import ReactFlow, {
Background,
ConnectionMode,
Edge,
MiniMap,
Node,
NodeChange,
NodeDragHandler,
OnConnect,
OnMoveEnd,
OnSelectionChangeFunc,
SelectionDragHandler,
SelectionMode,
useEdgesState,
useNodesState,
useReactFlow,
} from 'reactflow';
import 'reactflow/dist/style.css';
import classes from './Map.module.scss';
import './styles/neon-theme.scss';
import './styles/eve-common.scss';
import { MapProvider, useMapState } from './MapProvider';
import { useMapHandlers, useUpdateNodes } from './hooks';
import { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import {
ContextMenuConnection,
@@ -34,6 +34,7 @@ import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import clsx from 'clsx';
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
@@ -89,11 +90,16 @@ interface MapCompProps {
refn: ForwardedRef<MapHandlers>;
onCommand: OutCommandHandler;
onSelectionChange: OnMapSelectionChange;
onManualDelete(systems: string[]): void;
onConnectionInfoClick?(e: SolarSystemConnection): void;
onSelectionContextMenu?: NodeSelectionMouseHandler;
minimapClasses?: string;
isShowMinimap?: boolean;
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
showKSpaceBG?: boolean;
isThickConnections?: boolean;
isShowBackgroundPattern?: boolean;
isSoftBackground?: boolean;
}
const MapComp = ({
@@ -104,16 +110,21 @@ const MapComp = ({
onSystemContextMenu,
onConnectionInfoClick,
onSelectionContextMenu,
onManualDelete,
isShowMinimap,
showKSpaceBG,
isThickConnections,
isShowBackgroundPattern,
isSoftBackground,
}: MapCompProps) => {
const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges);
const { getNode } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
useMapHandlers(refn, onSelectionChange);
useUpdateNodes(nodes);
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
const { update } = useMapState();
const onConnect: OnConnect = useCallback(
@@ -169,13 +180,52 @@ const MapComp = ({
localStorage.setItem(SESSION_KEY.viewPort, JSON.stringify(viewport));
};
const handleNodesChange = useCallback(
(changes: NodeChange[]) => {
const systemsIdsToRemove: string[] = [];
const nextChanges = changes.reduce((acc, change) => {
if (change.type !== 'remove') {
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, onManualDelete, onNodesChange],
);
useEffect(() => {
update(x => ({
...x,
showKSpaceBG: showKSpaceBG,
isThickConnections: isThickConnections,
}));
}, [showKSpaceBG, isThickConnections, update]);
return (
<>
<div className={classes.MapRoot}>
<div className={clsx(classes.MapRoot, { ['bg-neutral-900']: isSoftBackground })}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onNodesChange={handleNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
// TODO we need save into session all of this
@@ -191,6 +241,7 @@ const MapComp = ({
onConnectStart={() => update({ isConnecting: true })}
onConnectEnd={() => update({ isConnecting: false })}
onNodeMouseEnter={(_, node) => update({ hoverNodeId: node.id })}
// onKeyUp=
onNodeMouseLeave={() => update({ hoverNodeId: null })}
onEdgeClick={(_, t) => {
onConnectionInfoClick?.(t.data);
@@ -210,14 +261,14 @@ const MapComp = ({
minZoom={0.2}
maxZoom={1.5}
elevateNodesOnSelect
deleteKeyCode={['Delete']}
// TODO need create clear example with problem with that flag
// if system is not visible edge not drawing (and any render in Custom node is not happening)
// onlyRenderVisibleElements
deleteKeyCode={null}
selectionMode={SelectionMode.Partial}
>
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
<Background />
{isShowBackgroundPattern && <Background />}
</ReactFlow>
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
Test // DON NOT REMOVE

View File

@@ -7,6 +7,8 @@ export type MapData = MapUnionTypes & {
isConnecting: boolean;
hoverNodeId: string | null;
visibleNodes: Set<string>;
showKSpaceBG: boolean;
isThickConnections: boolean;
};
interface MapProviderProps {
@@ -16,6 +18,7 @@ interface MapProviderProps {
const INITIAL_DATA: MapData = {
wormholesData: {},
wormholes: [],
effects: {},
characters: [],
userCharacters: [],
@@ -27,6 +30,9 @@ const INITIAL_DATA: MapData = {
connections: [],
hoverNodeId: null,
visibleNodes: new Set(),
showKSpaceBG: false,
isThickConnections: false,
userPermissions: {},
};
export interface MapContextProps {
@@ -38,6 +44,7 @@ export interface MapContextProps {
const MapContext = createContext<MapContextProps>({
update: () => {},
data: { ...INITIAL_DATA },
// @ts-ignore
outCommand: async () => void 0,
});

View File

@@ -3,7 +3,7 @@ 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 { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import clsx from 'clsx';
import classes from './ContextMenuConnection.module.scss';
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
@@ -35,36 +35,49 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
}
const isFrigateSize = edge.data?.ship_size_type === ShipSizeStatus.small;
const isWormhole = edge.data?.type !== ConnectionType.gate;
return [
{
label: `EOL`,
className: clsx({
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
}),
icon: PrimeIcons.CLOCK,
command: onChangeTimeState,
},
{
label: `Frigate`,
className: clsx({
[classes.ConnectionFrigate]: isFrigateSize,
}),
icon: PrimeIcons.CLOUD,
command: () =>
onChangeShipSizeStatus(
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
),
},
{
label: `Save mass`,
className: clsx({
[classes.ConnectionSave]: edge.data?.locked,
}),
icon: PrimeIcons.LOCK,
command: () => onToggleMassSave(!edge.data?.locked),
},
...(!isFrigateSize
...(isWormhole
? [
{
label: `EOL`,
className: clsx({
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
}),
icon: PrimeIcons.CLOCK,
command: onChangeTimeState,
},
]
: []),
...(isWormhole
? [
{
label: `Frigate`,
className: clsx({
[classes.ConnectionFrigate]: isFrigateSize,
}),
icon: PrimeIcons.CLOUD,
command: () =>
onChangeShipSizeStatus(
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
),
},
]
: []),
...(isWormhole
? [
{
label: `Save mass`,
className: clsx({
[classes.ConnectionSave]: edge.data?.locked,
}),
icon: PrimeIcons.LOCK,
command: () => onToggleMassSave(!edge.data?.locked),
},
]
: []),
...(isWormhole && !isFrigateSize
? [
{
label: `Mass status`,

View File

@@ -4,7 +4,7 @@ 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 { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
export const useContextMenuConnectionHandlers = () => {
@@ -47,6 +47,23 @@ export const useContextMenuConnectionHandlers = () => {
setEdge(undefined);
};
const onChangeType = useCallback((type: ConnectionType) => {
const { edge, outCommand } = ref.current;
if (!edge) {
return;
}
outCommand({
type: OutCommand.updateConnectionType,
data: {
source: edge.source,
target: edge.target,
value: type,
},
});
}, []);
const onChangeMassState = useCallback((status: MassState) => {
const { edge, outCommand } = ref.current;
@@ -118,6 +135,7 @@ export const useContextMenuConnectionHandlers = () => {
contextMenuRef,
onDeleteConnection,
onChangeTimeState,
onChangeType,
onChangeMassState,
onChangeShipSizeStatus,
onToggleMassSave,

View File

@@ -21,7 +21,11 @@
}
&.Frigate {
stroke: #4e62c9;
stroke: #d4f0ff;
}
&.Gate {
stroke: #1c1e15;
}
&.Hovered {
@@ -37,9 +41,16 @@
}
&.Frigate {
stroke: #41acd7;
stroke: #d4f0ff;
}
}
&.Tick {
stroke-width: 3px;
&.Hovered {
stroke-width: 3px;
}
}
}
@@ -61,6 +72,19 @@
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
}
}
.ClickPath {
@@ -93,5 +117,14 @@
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
&.Right {
margin-left: 0px;
}
}
}

View File

@@ -1,39 +1,64 @@
import { useCallback, useMemo, useState } from 'react';
import classes from './SolarSystemEdge.module.scss';
import { EdgeLabelRenderer, EdgeProps, getBezierPath, Position, useStore } from 'reactflow';
import { EdgeLabelRenderer, EdgeProps, getBezierPath, getSmoothStepPath, Position, useStore } from 'reactflow';
import { getEdgeParams } from '@/hooks/Mapper/components/map/utils.ts';
import clsx from 'clsx';
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { PrimeIcons } from 'primereact/api';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
const MAP_TRANSLATES: Record<string, string> = {
[Position.Top]: 'translate(-50%, 0%)',
[Position.Top]: 'translate(-48%, 0%)',
[Position.Bottom]: 'translate(-50%, -100%)',
[Position.Left]: 'translate(0%, -50%)',
[Position.Right]: 'translate(-100%, -50%)',
};
const MAP_OFFSETS_TICK: Record<string, { x: number; y: number }> = {
[Position.Top]: { x: 0, y: 3 },
[Position.Bottom]: { x: 0, y: -3 },
[Position.Left]: { x: 3, y: 0 },
[Position.Right]: { x: -3, y: 0 },
};
const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
[Position.Top]: { x: 0, y: 0 },
[Position.Bottom]: { x: 0, y: 0 },
[Position.Left]: { x: 0, y: 0 },
[Position.Right]: { x: 0, y: 0 },
};
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
const isWormhole = data?.type !== ConnectionType.gate;
const {
data: { isThickConnections },
} = useMapState();
const [hovered, setHovered] = useState(false);
const [path, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos] = useMemo(() => {
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
const [edgePath, labelX, labelY] = getBezierPath({
sourceX: sx,
sourceY: sy,
const offset = isThickConnections ? MAP_OFFSETS_TICK[targetPos] : MAP_OFFSETS[targetPos];
const method = isWormhole ? getBezierPath : getSmoothStepPath;
const [edgePath, labelX, labelY] = method({
sourceX: sx - offset.x,
sourceY: sy - offset.y,
sourcePosition: sourcePos,
targetPosition: targetPos,
targetX: tx,
targetY: ty,
targetX: tx + offset.x,
targetY: ty + offset.y,
});
return [edgePath, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos];
}, [sourceNode, targetNode]);
}, [isThickConnections, sourceNode, targetNode, isWormhole]);
if (!sourceNode || !targetNode || !data) {
return null;
@@ -44,8 +69,10 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<path
id={`back_${id}`}
className={clsx(classes.EdgePathBack, {
[classes.TimeCrit]: data.time_status === TimeStatus.eol,
[classes.Tick]: isThickConnections,
[classes.TimeCrit]: isWormhole && data.time_status === TimeStatus.eol,
[classes.Hovered]: hovered,
[classes.Gate]: !isWormhole,
})}
d={path}
markerEnd={markerEnd}
@@ -54,10 +81,12 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<path
id={`front_${id}`}
className={clsx(classes.EdgePathFront, {
[classes.Tick]: isThickConnections,
[classes.Hovered]: hovered,
[classes.MassVerge]: data.mass_status === MassState.verge,
[classes.MassHalf]: data.mass_status === MassState.half,
[classes.Frigate]: data.ship_size_type === ShipSizeStatus.small,
[classes.MassVerge]: isWormhole && data.mass_status === MassState.verge,
[classes.MassHalf]: isWormhole && data.mass_status === MassState.half,
[classes.Frigate]: isWormhole && data.ship_size_type === ShipSizeStatus.small,
[classes.Gate]: !isWormhole,
})}
d={path}
markerEnd={markerEnd}
@@ -75,11 +104,19 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<EdgeLabelRenderer>
<div
className={clsx(classes.Handle, 'react-flow__handle absolute nodrag pointer-events-none')}
className={clsx(
classes.Handle,
{ [classes.Tick]: isThickConnections, [classes.Right]: Position.Right === sourcePos },
'react-flow__handle absolute nodrag pointer-events-none',
)}
style={{ transform: `${MAP_TRANSLATES[sourcePos]} translate(${sx}px,${sy}px)` }}
/>
<div
className={clsx(classes.Handle, 'react-flow__handle absolute nodrag pointer-events-none')}
className={clsx(
classes.Handle,
{ [classes.Tick]: isThickConnections },
'react-flow__handle absolute nodrag pointer-events-none',
)}
style={{ transform: `${MAP_TRANSLATES[targetPos]} translate(${tx}px,${ty}px)` }}
/>
@@ -89,7 +126,7 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
>
{data.locked && (
{isWormhole && data.locked && (
<WdTooltipWrapper
content="Save mass"
className={clsx(

View File

@@ -1,4 +1,4 @@
@import "@/hooks/Mapper/components/map/styles/eve-common-variables";
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
@@ -6,7 +6,7 @@ $pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020; // Темный фон для подсказок
$tooltip-bg: #202020; // Dark background for tooltips
.RootCustomNode {
display: flex;
@@ -23,6 +23,62 @@ $tooltip-bg: #202020; // Темный фон для подсказок
border-radius: 5px;
position: relative;
z-index: 1;
overflow: hidden;
&.Mataria,
&.Amarria,
&.Gallente,
&.Caldaria {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: cover;
background-position: 50% 50%;
z-index: -1;
background-repeat: no-repeat;
border-radius: 3px;
}
}
&.Mataria {
&::before {
background-image: url('/images/mataria-180.png');
opacity: 0.6;
background-position-x: 1px;
background-position-y: -14px;
}
}
&.Caldaria {
&::before {
background-image: url('/images/caldaria-180.png');
opacity: 0.6;
background-position-x: 1px;
background-position-y: -10px;
}
}
&.Amarria {
&::before {
opacity: 0.45;
background-image: url('/images/amarr-180.png');
background-position-x: 0;
background-position-y: -13px;
}
}
&.Gallente {
&::before {
opacity: 0.5;
background-image: url('/images/gallente-180.png');
background-position-x: 1px;
background-position-y: 0;
}
}
&.selected {
border-color: $pastel-pink;
@@ -39,7 +95,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
&.eve-system-status-home {
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
background-image: linear-gradient(45deg, $eve-solar-system-status-friendly, transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
&.selected {
border-color: $eve-solar-system-status-color-home;
@@ -48,7 +104,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
&.eve-system-status-friendly {
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
background-image: linear-gradient(45deg, darken($eve-solar-system-status-friendly, 30%), transparent);
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
&.selected {
border-color: darken($eve-solar-system-status-color-friendly, 5%);
@@ -57,7 +113,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
&.eve-system-status-lookingFor {
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
background-image: linear-gradient(45deg, #45ff8f2f, #457fff2f);
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
&.selected {
border-color: $pastel-pink;
@@ -65,23 +121,22 @@ $tooltip-bg: #202020; // Темный фон для подсказок
}
&.eve-system-status-warning {
background-image: linear-gradient(45deg, $eve-solar-system-status-warning, transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
}
&.eve-system-status-dangerous {
background-image: linear-gradient(45deg, $eve-solar-system-status-dangerous, transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
}
&.eve-system-status-target {
background-image: linear-gradient(45deg, $eve-solar-system-status-target, transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
}
}
.Bookmarks {
position: absolute;
width: 100%;
z-index: 0;
z-index: 1;
display: flex;
left: 4px;
@@ -102,7 +157,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, .3);
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
}
@@ -125,7 +180,42 @@ $tooltip-bg: #202020; // Темный фон для подсказок
font-size: 9px;
}
}
}
.Unsplashed {
position: absolute;
width: calc(50% - 4px);
z-index: -1;
display: flex;
flex-wrap: wrap;
gap: 2px;
left: 2px;
&--right {
left: calc(50% + 6px);
}
& > .Signature {
width: 13px;
height: 4px;
position: relative;
top: 3px;
border-radius: 5px;
color: #ffffff;
font-size: 8px;
text-align: center;
padding-top: 2px;
font-weight: bolder;
padding-left: 3px;
padding-right: 3px;
display: block;
background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
}
}
.icon {
@@ -158,14 +248,20 @@ $tooltip-bg: #202020; // Темный фон для подсказок
color: #ffb01d;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
font-weight: bold;
}
}
.classSystemName {
//font-weight: bold;
}
.solarSystemName {
}
}
.BottomRow {
@@ -210,6 +306,12 @@ $tooltip-bg: #202020; // Темный фон для подсказок
& > * {
line-height: 10px;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
position: relative;
top: -1px;
}
}
.Handlers {
@@ -232,11 +334,40 @@ $tooltip-bg: #202020; // Темный фон для подсказок
border-color: $pastel-pink;
}
&.HandleTop { top: -2px }
&.HandleTop {
top: -2px;
}
&.HandleRight { right: -2px }
&.HandleRight {
right: -2px;
}
&.HandleBottom { bottom: -2px }
&.HandleBottom {
bottom: -2px;
}
&.HandleLeft { left: -2px }
&.HandleLeft {
left: -2px;
}
&.Tick {
width: 7px;
height: 7px;
&.HandleTop {
top: -3px;
}
&.HandleRight {
right: -3px;
}
&.HandleBottom {
bottom: -3px;
}
&.HandleLeft {
left: -3px;
}
}
}

View File

@@ -3,6 +3,8 @@ import { Handle, Position, WrapNodeProps } from 'reactflow';
import { MapSolarSystemType } from '../../map.types';
import classes from './SolarSystemNode.module.scss';
import clsx from 'clsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import {
EFFECT_BACKGROUND_STYLES,
LABELS_INFO,
@@ -12,16 +14,25 @@ import {
} from '@/hooks/Mapper/components/map/constants.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { PrimeIcons } from 'primereact/api';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { OutCommand } from '@/hooks/Mapper/types';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: classes.Caldaria,
[Spaces.Matar]: classes.Mataria,
[Spaces.Amarr]: classes.Amarria,
[Spaces.Gallente]: classes.Gallente,
};
const sortedLabels = (labels: string[]) => {
if (labels === null) {
if (!labels) {
return [];
}
@@ -42,6 +53,9 @@ export const getActivityType = (count: number) => {
// eslint-disable-next-line react/display-name
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
const { interfaceSettings } = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const {
system_class,
security,
@@ -50,10 +64,13 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = data.system_static_info;
const signatures = data.system_signatures;
const { locked, name, tag, status, labels, id } = data || {};
const customName = solar_system_name !== name ? name : undefined;
@@ -69,6 +86,8 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
@@ -114,13 +133,32 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
signatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
})),
);
}, [isShowUnsplashedSignatures, signatures]);
return (
<>
{visible && (
<div className={classes.Bookmarks}>
{labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<div>{labelCustom}</div>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
</div>
)}
@@ -147,18 +185,24 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
</div>
)}
<div className={clsx(classes.RootCustomNode, classes[STATUS_CLASSES[status]], { [classes.selected]: selected })}>
<div
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
[classes.selected]: selected,
})}
>
{visible && (
<>
<div className={classes.HeadRow}>
<div className={clsx(classes.classTitle, classTitleColor)}>{class_title ?? '-'}</div>
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
{class_title ?? '-'}
</div>
{tag != null && tag !== '' && (
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{solar_system_name}
@@ -179,11 +223,17 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{customName && (
<div className="text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">{customName}</div>
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{customName}
</div>
)}
{!isWormhole && !customName && (
<div className="text-stone-400 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
<div
className={clsx(
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{region_name}
</div>
)}
@@ -192,10 +242,10 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem' }}></i>}
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>}
{hubs.includes(solar_system_id.toString()) && (
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem' }}></i>
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
)}
{charactersInSystem.length > 0 && (
@@ -211,31 +261,59 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
)}
</div>
{visible && isShowUnsplashedSignatures && (
<div className={classes.Unsplashed}>
{unsplashedLeft.map(x => (
<UnsplashedSignature key={x.sig_id} signature={x} />
))}
</div>
)}
{visible && isShowUnsplashedSignatures && (
<div className={clsx([classes.Unsplashed, classes['Unsplashed--right']])}>
{unsplashedRight.map(x => (
<UnsplashedSignature key={x.sig_id} signature={x} />
))}
</div>
)}
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, { [classes.selected]: selected })}
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, { [classes.selected]: selected })}
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, { [classes.selected]: selected })}
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, { [classes.selected]: selected })}
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"

View File

@@ -0,0 +1,18 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
.Signature {
position: relative;
top: 3px;
display: block;
& > .Box {
width: 13px;
height: 4px;
border-radius: 4px;
color: #ffffff;
font-size: 8px;
text-align: center;
font-weight: bolder;
display: block;
}
}

View File

@@ -0,0 +1,65 @@
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { InfoDrawer } from '@/hooks/Mapper/components/ui-kit';
import classes from './UnsplashedSignature.module.scss';
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants.ts';
import { useMemo } from 'react';
import clsx from 'clsx';
import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
import { k162Types } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
interface UnsplashedSignatureProps {
signature: SystemSignature;
}
export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) => {
const {
data: { wormholesData },
} = useMapRootState();
const whData = useMemo(() => wormholesData[signature.type], [signature.type, wormholesData]);
const whClass = useMemo(() => (whData ? WORMHOLES_ADDITIONAL_INFO[whData.dest] : null), [whData]);
const k162TypeOption = useMemo(() => {
if (!signature.custom_info) {
return null;
}
const customInfo = JSON.parse(signature.custom_info);
if (!customInfo.k162Type) {
return null;
}
return k162Types.find(x => x.value === customInfo.k162Type);
}, [signature]);
const whClassStyle = useMemo(() => {
if (signature.type === 'K162' && k162TypeOption) {
const k162Data = wormholesData[k162TypeOption.whClassName];
const k162Class = k162Data ? WORMHOLES_ADDITIONAL_INFO[k162Data.dest] : null;
return k162Class ? WORMHOLE_CLASS_STYLES[k162Class.wormholeClassID] : '';
}
return whClass ? WORMHOLE_CLASS_STYLES[whClass.wormholeClassID] : '';
}, [signature, whClass, k162TypeOption, wormholesData]);
return (
<WdTooltipWrapper
className={clsx(classes.Signature)}
content={
(
<div className="flex flex-col gap-1">
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
{renderInfoColumn(signature)}
</InfoDrawer>
</div>
) as React.ReactNode
}
>
<div className={clsx(classes.Box, whClassStyle)}>
<svg width="13" height="4" viewBox="0 0 13 4" xmlns="http://www.w3.org/2000/svg">
<rect width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
</svg>
</div>
</WdTooltipWrapper>
);
};

View File

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

View File

@@ -18,5 +18,9 @@ export const WormholeClassComp = ({ id }: WormholeClassComp) => {
}
const colorClass = WORMHOLE_CLASS_STYLES[wormholeDataAdditional.wormholeClassID.toString()];
return <div className={clsx(colorClass)}>{wormholeDataAdditional.shortName}</div>;
return (
<div className={clsx(colorClass, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
{wormholeDataAdditional.shortName}
</div>
);
};

View File

@@ -1,4 +1,4 @@
import { MassState } from '@/hooks/Mapper/types';
import { ConnectionType, MassState } from '@/hooks/Mapper/types';
export enum SOLAR_SYSTEM_CLASS_IDS {
ccp1 = -1,
@@ -30,11 +30,77 @@ export enum SOLAR_SYSTEM_CLASS_IDS {
zarzakh = 10100,
}
export enum SOLAR_SYSTEM_CLASS_GROUPS {
ccp = 'ccp',
c1 = 'c1',
c2 = 'c2',
c3 = 'c3',
c4 = 'c4',
c5 = 'c5',
c6 = 'c6',
hs = 'hs',
ls = 'ls',
ns = 'ns',
thera = 'thera',
c13 = 'c13',
drifter = 'drifter',
unknown = 'unknown',
pochven = 'pochven',
jovian = 'jovian',
}
export const SOLAR_SYSTEM_TO_CLASS_GROUPS_CLASSES = {
c1: ['c1'],
c2: ['c2'],
c3: ['c3'],
c4: ['c4'],
c5: ['c5'],
c6: ['c6'],
hs: ['hs'],
ls: ['ls'],
ns: ['ns'],
thera: ['thera'],
c13: ['c13'],
pochven: ['pochven'],
drifter: ['sentinel', 'barbican', 'vidette', 'conflux', 'redoubt'],
jove: ['jove'],
};
export const SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS = {
ccp1: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
c1: SOLAR_SYSTEM_CLASS_GROUPS.c1,
c2: SOLAR_SYSTEM_CLASS_GROUPS.c2,
c3: SOLAR_SYSTEM_CLASS_GROUPS.c3,
c4: SOLAR_SYSTEM_CLASS_GROUPS.c4,
c5: SOLAR_SYSTEM_CLASS_GROUPS.c5,
c6: SOLAR_SYSTEM_CLASS_GROUPS.c6,
hs: SOLAR_SYSTEM_CLASS_GROUPS.hs,
ls: SOLAR_SYSTEM_CLASS_GROUPS.ls,
ns: SOLAR_SYSTEM_CLASS_GROUPS.ns,
ccp2: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
ccp3: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
thera: SOLAR_SYSTEM_CLASS_GROUPS.thera,
c13: SOLAR_SYSTEM_CLASS_GROUPS.c13,
sentinel: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
baribican: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
vidette: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
conflux: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
redoubt: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
a1: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a2: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a3: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a4: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a5: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
ccp4: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
pochven: SOLAR_SYSTEM_CLASS_GROUPS.pochven,
};
type WormholesAdditionalInfoType = {
id: string;
shortName: string;
wormholeClassID: number;
title: string;
shortTitle: string;
effectPower?: number;
};
@@ -45,6 +111,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
shortName: 'CCP',
wormholeClassID: -1,
title: 'CCP System',
shortTitle: 'CCP',
},
{
id: 'c1',
@@ -52,6 +119,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 1,
effectPower: 1,
title: 'Class 1',
shortTitle: 'C1',
},
{
id: 'c2',
@@ -59,6 +127,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 2,
effectPower: 2,
title: 'Class 2',
shortTitle: 'C2',
},
{
id: 'c3',
@@ -66,6 +135,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 3,
effectPower: 3,
title: 'Class 3',
shortTitle: 'C3',
},
{
id: 'c4',
@@ -73,6 +143,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 4,
effectPower: 4,
title: 'Class 4',
shortTitle: 'C4',
},
{
id: 'c5',
@@ -80,6 +151,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 5,
effectPower: 5,
title: 'Class 5',
shortTitle: 'C5',
},
{
id: 'c6',
@@ -87,42 +159,49 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 6,
effectPower: 6,
title: 'Class 6',
shortTitle: 'C6',
},
{
id: 'hs',
shortName: 'H',
wormholeClassID: 7,
title: 'High-sec',
shortTitle: 'High-sec',
},
{
id: 'ls',
shortName: 'L',
wormholeClassID: 8,
title: 'Low-sec',
shortTitle: 'Low-sec',
},
{
id: 'ns',
shortName: 'N',
wormholeClassID: 9,
title: 'Null-sec',
shortTitle: 'Null-sec',
},
{
id: 'ccp2',
shortName: 'CCP',
wormholeClassID: 10,
title: 'CCP System',
shortTitle: 'CCP',
},
{
id: 'ccp3',
shortName: 'CCP',
wormholeClassID: 11,
title: 'CCP System',
shortTitle: 'CCP',
},
{
id: 'thera',
shortName: 'T',
wormholeClassID: 12,
title: 'Class 12 (Thera)',
shortTitle: 'Thera',
},
{
id: 'c13',
@@ -130,6 +209,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 13,
effectPower: 6,
title: 'Class 13 (Shattered Frigate)',
shortTitle: 'C13',
},
{
id: 'sentinel',
@@ -137,6 +217,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 14,
effectPower: 2,
title: 'Class 14 (Sentinel Drifter)',
shortTitle: 'Sentinel',
},
{
id: 'barbican',
@@ -144,6 +225,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 15,
effectPower: 2,
title: 'Class 15 (Barbican Drifter)',
shortTitle: 'Barbican',
},
{
id: 'vidette',
@@ -151,6 +233,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 16,
effectPower: 2,
title: 'Class 16 (Vidette Drifter)',
shortTitle: 'Vidette',
},
{
id: 'conflux',
@@ -158,6 +241,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 17,
effectPower: 2,
title: 'Class 17 (Conflux Drifter)',
shortTitle: 'Conflux',
},
{
id: 'redoubt',
@@ -165,59 +249,79 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 18,
effectPower: 2,
title: 'Class 18 (Redoubt Drifter)',
shortTitle: 'Redoubt',
},
{
id: 'a1',
shortName: 'A1',
wormholeClassID: 19,
title: '(Abyssal class 1)',
shortTitle: 'A1',
},
{
id: 'a2',
shortName: 'A2',
wormholeClassID: 20,
title: '(Abyssal class 2)',
shortTitle: 'A2',
},
{
id: 'a3',
shortName: 'A3',
wormholeClassID: 21,
title: '(Abyssal class 3)',
shortTitle: 'A3',
},
{
id: 'a4',
shortName: 'A4',
wormholeClassID: 22,
title: '(Abyssal class 4)',
shortTitle: 'A4',
},
{
id: 'a5',
shortName: 'A5',
wormholeClassID: 23,
title: '(Abyssal class 5)',
shortTitle: 'A5',
},
{
id: 'ccp4',
shortName: 'CCP',
wormholeClassID: 24,
title: 'CCP System (Penalty)',
shortTitle: 'CCP',
},
{
id: 'pochven',
shortName: 'P',
wormholeClassID: 25,
title: 'Triglavian space (Pochven)',
shortTitle: 'Pochven',
},
{
id: 'zarzakh',
shortName: 'N',
wormholeClassID: 10100,
title: 'Pirate space',
shortTitle: 'Zarzakh',
},
{
id: 'k162',
shortName: 'K162',
wormholeClassID: 10101,
title: 'Reverse',
shortTitle: 'K162',
},
];
export const WORMHOLES_ADDITIONAL_INFO: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.id]: x }), {});
export const WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.wormholeClassID]: x }), {});
// export const SOLAR_SYSTEM_CLASS_NAMES = {
// ccp1 = ,
// c1 = ,
@@ -608,6 +712,13 @@ export const STATUS_CLASSES: Record<number, string> = {
[STATUSES.dangerous]: 'eve-system-status-dangerous',
};
export const TYPE_NAMES_ORDER = [ConnectionType.wormhole, ConnectionType.gate];
export const TYPE_NAMES = {
[ConnectionType.wormhole]: 'Wormhole',
[ConnectionType.gate]: 'Gate',
};
export const MASS_STATE_NAMES_ORDER = [MassState.verge, MassState.half, MassState.normal];
export const MASS_STATE_NAMES = {

View File

@@ -3,3 +3,4 @@ export * from './convertSystem2Node';
export * from './getSystemClassStyles';
export * from './getShapeClass';
export * from './getBackgroundClass';
export * from './prepareUnsplashedChunks';

View File

@@ -11,3 +11,7 @@ export const isKnownSpace = (wormholeClassID: number) => {
return false;
};
export const isPossibleSpace = (spaces: number[], wormholeClassID: number) => {
return spaces.includes(wormholeClassID);
};

View File

@@ -0,0 +1,27 @@
// Helper function to split an array into chunks of size
const chunkArray = (array: any[], size: number) => {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
};
export const prepareUnsplashedChunks = (items: any[]) => {
// Split the items into chunks of 4
const chunks = chunkArray(items, 4);
// Get the column elements
const leftColumn: any[] = [];
const rightColumn: any[] = [];
chunks.forEach((chunk, index) => {
const column = index % 2 === 0 ? leftColumn : rightColumn;
chunk.forEach(item => {
column.push(item);
});
});
return [leftColumn, rightColumn];
};

View File

@@ -5,5 +5,6 @@ export * from './useMapRemoveSystems';
export * from './useCommandsCharacters';
export * from './useCommandsConnections';
export * from './useCommandsConnections';
export * from './useCenterSystem';
export * from './useSelectSystem';
export * from './useMapCommands';

View File

@@ -0,0 +1,18 @@
import { useReactFlow } from 'reactflow';
import { useCallback, useRef } from 'react';
import { CommandCenterSystem } from '@/hooks/Mapper/types';
export const useCenterSystem = () => {
const rf = useReactFlow();
const ref = useRef({ rf });
ref.current = { rf };
return useCallback((systemId: CommandCenterSystem) => {
const systemNode = ref.current.rf.getNodes().find(x => x.data.id === systemId);
if (!systemNode) {
return;
}
ref.current.rf.setCenter(systemNode.position.x, systemNode.position.y, { duration: 1000 });
}, []);
};

View File

@@ -2,28 +2,18 @@ import { Node, useReactFlow } from 'reactflow';
import { useCallback, useRef } from 'react';
import { CommandAddSystems } from '@/hooks/Mapper/types/mapHandlers.ts';
import { convertSystem2Node } from '../../helpers';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
export const useMapAddSystems = () => {
const rf = useReactFlow();
const {
data: { systems },
update,
} = useMapState();
const ref = useRef({ rf, systems, update });
ref.current = { update, systems, rf };
const ref = useRef({ rf });
ref.current = { rf };
return useCallback(
(systems: CommandAddSystems) => {
const nodes = rf.getNodes();
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
rf.addNodes(prepared);
return useCallback((systems: CommandAddSystems) => {
const { rf } = ref.current;
const nodes = rf.getNodes();
ref.current.update({
systems: [...ref.current.systems.filter(sys => systems.some(x => sys.id !== x.id)), ...systems],
});
},
[rf],
);
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
rf.addNodes(prepared);
}, []);
};

View File

@@ -5,24 +5,21 @@ import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts
export const useMapRemoveSystems = (onSelectionChange: OnMapSelectionChange) => {
const rf = useReactFlow();
const ref = useRef({ onSelectionChange });
ref.current = { onSelectionChange };
const ref = useRef({ onSelectionChange, rf });
ref.current = { onSelectionChange, rf };
return useCallback(
(systems: CommandRemoveSystems) => {
rf.deleteElements({ nodes: systems.map(x => ({ id: `${x}` })) });
return useCallback((systems: CommandRemoveSystems) => {
ref.current.rf.deleteElements({ nodes: systems.map(x => ({ id: `${x}` })) });
const newSelection = rf
.getNodes()
.filter(x => !systems.includes(parseInt(x.id)))
.filter(x => x.selected)
.map(x => x.id);
const newSelection = ref.current.rf
.getNodes()
.filter(x => !systems.includes(parseInt(x.id)))
.filter(x => x.selected)
.map(x => x.id);
ref.current.onSelectionChange({
systems: newSelection,
connections: [],
});
},
[rf],
);
ref.current.onSelectionChange({
systems: newSelection,
connections: [],
});
}, []);
};

View File

@@ -4,18 +4,18 @@ import { CommandSelectSystem } from '@/hooks/Mapper/types';
export const useSelectSystem = () => {
const rf = useReactFlow();
const ref = useRef({ rf });
ref.current = { rf };
return useCallback((systemId: CommandSelectSystem) => {
if (!ref.current?.rf) {
return;
}
const systemNode = ref.current.rf.getNodes().find(x => x.data.id === systemId);
if (!systemNode) {
return;
}
ref.current.rf.setCenter(systemNode.position.x, systemNode.position.y, { duration: 1000 });
ref.current.rf.setNodes(nds =>
nds.map(node => {
return {
...node,
selected: node.id === systemId,
};
}),
);
}, []);
};

View File

@@ -1,2 +1,3 @@
export * from './useMapHandlers';
export * from './useUpdateNodes';
export * from './useNodesEdgesState';

View File

@@ -1,4 +1,4 @@
import { ForwardedRef, useImperativeHandle } from 'react';
import { ForwardedRef, useImperativeHandle, useRef } from 'react';
import {
CommandAddConnections,
CommandAddSystems,
@@ -18,6 +18,7 @@ import {
CommandUpdateSystems,
MapHandlers,
} from '@/hooks/Mapper/types/mapHandlers.ts';
import {
useCommandsCharacters,
useCommandsConnections,
@@ -26,6 +27,7 @@ import {
useMapInit,
useMapRemoveSystems,
useMapUpdateSystems,
useCenterSystem,
useSelectSystem,
} from './api';
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
@@ -35,8 +37,12 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
const mapAddSystems = useMapAddSystems();
const mapUpdateSystems = useMapUpdateSystems();
const removeSystems = useMapRemoveSystems(onSelectionChange);
const centerSystem = useCenterSystem();
const selectSystem = useSelectSystem();
const selectRef = useRef({ onSelectionChange });
selectRef.current = { onSelectionChange };
const { addConnections, removeConnections, updateConnection } = useCommandsConnections();
const { mapUpdated, killsUpdated } = useMapCommands();
const { charactersUpdated, presentCharacters, characterAdded, characterRemoved, characterUpdated } =
@@ -52,16 +58,16 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
mapInit(data as CommandInit);
break;
case Commands.addSystems:
mapAddSystems(data as CommandAddSystems);
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
break;
case Commands.updateSystems:
mapUpdateSystems(data as CommandUpdateSystems);
break;
case Commands.removeSystems:
removeSystems(data as CommandRemoveSystems);
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
break;
case Commands.addConnections:
addConnections(data as CommandAddConnections);
setTimeout(() => addConnections(data as CommandAddConnections), 100);
break;
case Commands.removeConnections:
removeConnections(data as CommandRemoveConnections);
@@ -91,14 +97,36 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
killsUpdated(data as CommandKillsUpdated);
break;
case Commands.centerSystem:
setTimeout(() => {
const systemId = `${data}`;
centerSystem(systemId as CommandSelectSystem);
}, 100);
break;
case Commands.selectSystem:
selectSystem(data as CommandSelectSystem);
setTimeout(() => {
const systemId = `${data}`;
selectRef.current.onSelectionChange({
systems: [systemId],
connections: [],
});
selectSystem(systemId as CommandSelectSystem);
}, 500);
break;
case Commands.routes:
// do nothing here
break;
case Commands.signaturesUpdated:
// do nothing here
break;
case Commands.linkSignatureToSystem:
// do nothing here
break;
default:
console.warn(`Map handlers: Unknown command: ${type}`, data);
break;

View File

@@ -0,0 +1,36 @@
import { useState, useCallback, type Dispatch, type SetStateAction } from 'react';
import { applyNodeChanges, applyEdgeChanges } from '../utils/changes';
import { OnNodesChange, Edge, OnEdgesChange, Node } from 'reactflow';
/**
* Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
*
* @public
* @param initialNodes
* @returns an array [nodes, setNodes, onNodesChange]
*/
export function useNodesState<NodeType extends Node>(
initialNodes: NodeType[],
): [NodeType[], Dispatch<SetStateAction<NodeType[]>>, OnNodesChange] {
const [nodes, setNodes] = useState(initialNodes);
const onNodesChange: OnNodesChange = useCallback(changes => setNodes(nds => applyNodeChanges(changes, nds)), []);
return [nodes, setNodes, onNodesChange];
}
/**
* Hook for managing the state of edges - should only be used for prototyping / simple use cases.
*
* @public
* @param initialEdges
* @returns an array [edges, setEdges, onEdgesChange]
*/
export function useEdgesState<EdgeType extends Edge = Edge>(
initialEdges: EdgeType[],
): [EdgeType[], Dispatch<SetStateAction<EdgeType[]>>, OnEdgesChange] {
const [edges, setEdges] = useState(initialEdges);
const onEdgesChange: OnEdgesChange = useCallback(changes => setEdges(eds => applyEdgeChanges(changes, eds)), []);
return [edges, setEdges, onEdgesChange];
}

View File

@@ -0,0 +1,174 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { EdgeChange, NodeChange, Node, Edge } from 'reactflow';
// This function applies changes to nodes or edges that are triggered by React Flow internally.
// When you drag a node for example, React Flow will send a position change update.
// This function then applies the changes and returns the updated elements.
function applyChanges(changes: any[], elements: any[]): any[] {
// we need this hack to handle the setNodes and setEdges function of the useReactFlow hook for controlled flows
if (changes.some(c => c.type === 'reset')) {
return changes.filter(c => c.type === 'reset').map(c => c.item);
}
const updatedElements: any[] = [];
// By storing a map of changes for each element, we can a quick lookup as we
// iterate over the elements array!
const changesMap = new Map<any, any[]>();
const addItemChanges: any[] = [];
for (const change of changes) {
if (change.type === 'add') {
addItemChanges.push(change);
continue;
} else if (change.type === 'remove' || change.type === 'replace') {
// For a 'remove' change we can safely ignore any other changes queued for
// the same element, it's going to be removed anyway!
changesMap.set(change.id, [change]);
} else {
const elementChanges = changesMap.get(change.id);
if (elementChanges) {
// If we have some changes queued already, we can do a mutable update of
// that array and save ourselves some copying.
elementChanges.push(change);
} else {
changesMap.set(change.id, [change]);
}
}
}
for (const element of elements) {
const changes = changesMap.get(element.id);
// When there are no changes for an element we can just push it unmodified,
// no need to copy it.
if (!changes) {
updatedElements.push(element);
continue;
}
// If we have a 'remove' change queued, it'll be the only change in the array
if (changes[0].type === 'remove') {
continue;
}
if (changes[0].type === 'replace') {
updatedElements.push({ ...changes[0].item });
continue;
}
// For other types of changes, we want to start with a shallow copy of the
// object so React knows this element has changed. Sequential changes will
/// each _mutate_ this object, so there's only ever one copy.
const updatedElement = { ...element };
for (const change of changes) {
applyChange(change, updatedElement);
}
updatedElements.push(updatedElement);
}
// we need to wait for all changes to be applied before adding new items
// to be able to add them at the correct index
if (addItemChanges.length) {
addItemChanges.forEach(change => {
if (change.index !== undefined) {
updatedElements.splice(change.index, 0, { ...change.item });
} else {
updatedElements.push({ ...change.item });
}
});
}
return updatedElements;
}
// Applies a single change to an element. This is a *mutable* update.
function applyChange(change: any, element: any): any {
switch (change.type) {
case 'select': {
element.selected = change.selected;
break;
}
case 'position': {
if (typeof change.position !== 'undefined') {
element.position = change.position;
}
if (typeof change.dragging !== 'undefined') {
element.dragging = change.dragging;
}
break;
}
case 'dimensions': {
if (typeof change.dimensions !== 'undefined') {
element.measured ??= {};
element.measured.width = change.dimensions.width;
element.measured.height = change.dimensions.height;
if (change.setAttributes) {
element.width = change.dimensions.width;
element.height = change.dimensions.height;
}
}
if (typeof change.resizing === 'boolean') {
element.resizing = change.resizing;
}
break;
}
}
}
/**
* Drop in function that applies node changes to an array of nodes.
* @public
* @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way.
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
* @param changes - Array of changes to apply
* @param nodes - Array of nodes to apply the changes to
* @returns Array of updated nodes
* @example
* const onNodesChange = useCallback(
(changes) => {
setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
},
[setNodes],
);
return (
<ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
);
*/
export function applyNodeChanges<NodeType extends Node = Node>(changes: NodeChange[], nodes: NodeType[]): NodeType[] {
return applyChanges(changes, nodes) as NodeType[];
}
/**
* Drop in function that applies edge changes to an array of edges.
* @public
* @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way.
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
* @param changes - Array of changes to apply
* @param edges - Array of edge to apply the changes to
* @returns Array of updated edges
* @example
* const onEdgesChange = useCallback(
(changes) => {
setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
},
[setEdges],
);
return (
<ReactFlow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
);
*/
export function applyEdgeChanges<EdgeType extends Edge = Edge>(changes: EdgeChange[], edges: EdgeType[]): EdgeType[] {
return applyChanges(changes, edges) as EdgeType[];
}

View File

@@ -48,6 +48,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const raw = localStorage.getItem(SESSION_KEY.windows);
if (!raw) {
console.warn('No windows found in local storage!!');
return DEFAULT_WINDOWS;
}
@@ -63,7 +64,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
};
export const MapInterface = () => {
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS());
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS);
return (
<WidgetsGrid

View File

@@ -7,7 +7,7 @@ import { Button } from 'primereact/button';
import { OutCommand } from '@/hooks/Mapper/types';
import { IconField } from 'primereact/iconfield';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { WdImageSize, WdImgButton, TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
interface SystemCustomLabelDialog {
systemId: string;
@@ -79,14 +79,14 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
// @ts-ignore
const handleInput = useCallback(e => {
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '');
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
}, []);
return (
<Dialog
header="Edit label"
visible={visible}
draggable={false}
draggable={true}
style={{ width: '250px' }}
onHide={onHide}
onShow={onShow}
@@ -100,9 +100,13 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
<IconField>
{label !== '' && (
<WdImgButton
className="pi pi-trash p-input-icon"
className="pi pi-trash text-red-400"
textSize={WdImageSize.large}
tooltip={{ content: 'Reset label' }}
tooltip={{
content: 'Remove custom label',
className: 'pi p-input-icon',
position: TooltipPosition.top,
}}
onClick={handleReset}
/>
)}
@@ -111,7 +115,7 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
aria-describedby="username-help"
autoComplete="off"
value={label}
maxLength={3}
maxLength={5}
onChange={e => setLabel(e.target.value)}
// @ts-expect-error
ref={inputRef}

View File

@@ -0,0 +1,75 @@
import { useCallback, useRef } from 'react';
import { Dialog } from 'primereact/dialog';
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 {
Setting,
COSMIC_SIGNATURE,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
import { SignatureGroup } from '@/hooks/Mapper/types';
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 SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => {
const { outCommand } = useMapRootState();
const ref = useRef({ outCommand });
ref.current = { outCommand };
const handleHide = useCallback(() => {
setVisible(false);
}, [setVisible]);
const handleSelect = useCallback(
(signature: SystemSignature) => {
if (!signature) {
return;
}
const { outCommand } = ref.current;
outCommand({
type: OutCommand.linkSignatureToSystem,
data: {
...data,
signature_eve_id: signature.eve_id,
},
});
setVisible(false);
},
[data, setVisible],
);
return (
<Dialog
header="Select signature to link"
visible
draggable={false}
style={{ width: '500px' }}
onHide={handleHide}
contentClassName="!p-0"
>
<SystemSignaturesContent
systemId={`${data.solar_system_source}`}
hideLinkedSignatures
settings={signatureSettings}
onSelect={handleSelect}
selectable={true}
/>
</Dialog>
);
};

View File

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

View File

@@ -90,7 +90,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
}, []);
const handleInput = useCallback((e: any) => {
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '');
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
}, []);
return (
@@ -160,7 +160,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
aria-describedby="label"
autoComplete="off"
value={label}
maxLength={3}
maxLength={5}
onChange={e => setLabel(e.target.value)}
onInput={handleInput}
/>

View File

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

View File

@@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react';
import { useCallback, useMemo, useRef } from 'react';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
@@ -8,6 +8,10 @@ import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { CharacterCard, LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
import useLocalStorageState from 'use-local-storage-state';
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
type CharItemProps = {
compact: boolean;
@@ -62,6 +66,14 @@ export const LocalCharacters = () => {
const [systemId] = selectedSystems;
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
const showOffline = useMemo(
() => !restrictOfflineShowing || isAdminOrManager,
[isAdminOrManager, restrictOfflineShowing],
);
const itemTemplate = useItemTemplate();
const sorted = useMemo(() => {
@@ -70,32 +82,39 @@ export const LocalCharacters = () => {
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
.sort(sortCharacters);
if (!settings.showOffline) {
if (!showOffline || !settings.showOffline) {
return sorted.filter(c => c.online);
}
return sorted;
// eslint-disable-next-line
}, [characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
}, [showOffline, characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
const isNobodyHere = sorted.length === 0;
const isNotSelectedSystem = selectedSystems.length !== 1;
const showList = sorted.length > 0 && selectedSystems.length === 1;
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 145);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full">
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
<LayoutEventBlocker className="flex items-center gap-2">
<WdCheckbox
size="xs"
labelSide="left"
label={'Show offline'}
value={settings.showOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
/>
{showOffline && (
<WdTooltipWrapper content="Show offline characters in system">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show offline'}
value={settings.showOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
/>
</WdTooltipWrapper>
)}
<span
className={clsx('w-4 h-4 cursor-pointer', {
@@ -115,7 +134,9 @@ export const LocalCharacters = () => {
)}
{isNobodyHere && !isNotSelectedSystem && (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">Nobody here</div>
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
Nobody here
</div>
)}
{showList && (

View File

@@ -5,7 +5,7 @@ import { SystemViewStandalone, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapp
import { getBackgroundClass, getShapeClass } from '@/hooks/Mapper/components/map/helpers';
import { MouseEvent, useCallback, useRef, useState } from 'react';
import { Commands } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { emitMapEvent } from '@/hooks/Mapper/events';
export type RouteSystemProps = {
destination: number;
@@ -88,11 +88,10 @@ export interface RoutesListProps {
export const RoutesList = ({ data, onContextMenu }: RoutesListProps) => {
const [selected, setSelected] = useState<number | null>(null);
const { mapRef } = useMapRootState();
const handleClick = useCallback(
(systemId: number) => mapRef.current?.command(Commands.selectSystem, systemId.toString()),
[mapRef],
(systemId: number) => emitMapEvent({ name: Commands.centerSystem, data: systemId?.toString() }),
[],
);
if (!data.has_connection) {

View File

@@ -1,12 +1,11 @@
import { Dialog } from 'primereact/dialog';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button } from 'primereact/button';
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import {
RoutesType,
useRouteProvider,
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
import { CheckboxChangeEvent } from 'primereact/checkbox';
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
interface RoutesSettingsDialog {
visible: boolean;
@@ -38,8 +37,8 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
currentData.current = data;
const handleChangeEvent = useCallback(
(propName: keyof RoutesType) => (event: CheckboxChangeEvent) => {
optionsRef.current = { ...optionsRef.current, [propName]: event.checked };
(propName: keyof RoutesType) => (event: boolean) => {
optionsRef.current = { ...optionsRef.current, [propName]: event };
updateKey(x => x + 1);
},
[],
@@ -71,14 +70,14 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
setVisible(false);
}}
>
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-3 p-2.5">
<div className="flex flex-col gap-2 mb-2">
{checkboxes.map(({ label, propName }) => (
<WdCheckbox
<PrettySwitchbox
key={propName}
label={label}
value={optionsRef.current[propName]}
onChange={handleChangeEvent(propName)}
checked={optionsRef.current[propName]}
setChecked={handleChangeEvent(propName)}
/>
))}
</div>

View File

@@ -19,6 +19,8 @@ import { PrimeIcons } from 'primereact/api';
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
const sortByDist = (a: Route, b: Route) => {
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
@@ -30,7 +32,6 @@ const sortByDist = (a: Route, b: Route) => {
export const RoutesWidgetContent = () => {
const {
data: { selectedSystems, hubs = [], systems, routes },
mapRef,
outCommand,
} = useMapRootState();
@@ -42,7 +43,6 @@ export const RoutesWidgetContent = () => {
const { open, ...systemCtxProps } = useContextMenuSystemInfoHandlers({
outCommand,
hubs,
mapRef,
});
const preparedHubs = useMemo(() => {
@@ -54,7 +54,7 @@ export const RoutesWidgetContent = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hubs, systems, systemStatics, lastUpdateKey]);
const preparedRoutes = useMemo(() => {
const preparedRoutes: Route[] = useMemo(() => {
return (
routes?.routes
.sort(sortByDist)
@@ -71,15 +71,17 @@ export const RoutesWidgetContent = () => {
);
}, [routes?.routes, routes?.systems_static_data, systemId]);
const refData = useRef({ open, loadSystems });
refData.current = { open, loadSystems };
const refData = useRef({ open, loadSystems, preparedRoutes });
refData.current = { open, loadSystems, preparedRoutes };
useEffect(() => {
(async () => await refData.current.loadSystems(hubs))();
}, [hubs]);
const handleClick = useCallback((e: MouseEvent, systemId: string) => {
refData.current.open(e, systemId);
const route = refData.current.preparedRoutes.find(x => x.destination.toString() === systemId);
refData.current.open(e, systemId, route?.mapped_systems ?? []);
}, []);
const handleContextMenu = useCallback(
@@ -146,7 +148,14 @@ export const RoutesWidgetContent = () => {
</div>
)}
<ContextMenuSystemInfo hubs={hubs} systems={systems} systemStatics={systemStatics} {...systemCtxProps} />
<ContextMenuSystemInfo
hubs={hubs}
routes={preparedRoutes}
systems={systems}
systemStatics={systemStatics}
systemIdFrom={systemId}
{...systemCtxProps}
/>
</>
);
};
@@ -163,20 +172,25 @@ export const RoutesWidgetComp = () => {
});
}, [data, update]);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 155);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full">
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<span className="select-none">Routes</span>
<LayoutEventBlocker className="flex items-center gap-2">
<WdCheckbox
size="xs"
labelSide="left"
label={'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel={clsx('text-red-400')}
/>
<WdTooltipWrapper content="Show shortest route">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel={clsx('text-red-400')}
/>
</WdTooltipWrapper>
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
</LayoutEventBlocker>
</div>

View File

@@ -13,9 +13,10 @@ export const SystemInfoContent = ({ systemId }: SystemInfoContentProps) => {
data: { systems, wormholesData },
} = useMapRootState();
const sys = getSystemById(systems, systemId)!;
const sys = getSystemById(systems, systemId)! || {};
const { description } = sys;
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } = sys.system_static_info;
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } =
sys.system_static_info || {};
const isWH = isWormholeSpace(system_class);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);

View File

@@ -0,0 +1,79 @@
.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

@@ -1,9 +1,19 @@
import { Dialog } from 'primereact/dialog';
import { useCallback, useState } from 'react';
import { Button } from 'primereact/button';
import { Checkbox } from 'primereact/checkbox';
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 };
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';
interface SystemSignatureSettingsDialogProps {
settings: Setting[];
@@ -16,8 +26,12 @@ export const SystemSignatureSettingsDialog = ({
onSave,
onCancel,
}: SystemSignatureSettingsDialogProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const [settings, setSettings] = useState<Setting[]>(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)));
};
@@ -27,23 +41,45 @@ export const SystemSignatureSettingsDialog = ({
}, [onSave, settings]);
return (
<Dialog header="Filter signatures" visible draggable={false} style={{ width: '300px' }} onHide={onCancel}>
<div className="flex flex-col gap-3">
<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">
{settings.map(setting => {
return (
<div key={setting.key} className="flex items-center">
<Checkbox
inputId={setting.key}
checked={setting.value}
onChange={() => handleSettingsChange(setting.key)}
/>
<label htmlFor={setting.key} className="ml-2">
{setting.name}
</label>
</div>
);
})}
<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>
</div>
<div className="flex gap-2 justify-end">

View File

@@ -1,34 +1,60 @@
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { InfoDrawer, LayoutEventBlocker, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import {
InfoDrawer,
LayoutEventBlocker,
TooltipPosition,
WdImgButton,
WdCheckbox,
} from '@/hooks/Mapper/components/ui-kit';
import { SystemSignaturesContent } from './SystemSignaturesContent';
import { Setting, SystemSignatureSettingsDialog } from './SystemSignatureSettingsDialog';
import {
Setting,
SystemSignatureSettingsDialog,
COSMIC_SIGNATURE,
COSMIC_ANOMALY,
DEPLOYABLE,
STRUCTURE,
STARBASE,
SHIP,
DRONE,
} from './SystemSignatureSettingsDialog';
import { SignatureGroup } from '@/hooks/Mapper/types';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react';
import { PrimeIcons } from 'primereact/api';
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';
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';
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';
const settings: Setting[] = [
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true },
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true },
{ key: DEPLOYABLE, name: 'Show Deployables', value: true },
{ key: STRUCTURE, name: 'Show Structures', value: true },
{ key: STARBASE, name: 'Show Starbase', value: true },
{ key: SHIP, name: 'Show Ships', value: true },
{ key: DRONE, name: 'Show Drones And Charges', value: true },
{ 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 SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings';
const defaultSettings = () => {
return [...settings];
};
@@ -45,12 +71,25 @@ export const SystemSignatures = () => {
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];
});
}, []);
useEffect(() => {
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
@@ -59,13 +98,27 @@ export const SystemSignatures = () => {
}
}, []);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 260);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full h-full">
<div className="flex gap-1">System Signatures</div>
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
<div className="flex gap-1 whitespace-nowrap text-ellipsis overflow-hidden">System Signatures</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={{
@@ -89,8 +142,7 @@ export const SystemSignatures = () => {
</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> or{' '}
<b className="text-sky-500">Backspace</b>
<br /> and then use <b className="text-sky-500">Del</b>
</InfoDrawer>
</div>
) as React.ReactNode,
@@ -106,7 +158,7 @@ export const SystemSignatures = () => {
System is not selected
</div>
) : (
<SystemSignaturesContent systemId={systemId} settings={settings} />
<SystemSignaturesContent systemId={systemId} settings={settings} onLazyDeleteChange={handleLazyDeleteChange} />
)}
{visible && (
<SystemSignatureSettingsDialog

View File

@@ -4,3 +4,7 @@
font-size: 12px !important;
line-height: 8px;
}
.Table {
}

View File

@@ -1,16 +1,17 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
import { parseSignatures } from '@/hooks/Mapper/helpers';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import { GROUPS_LIST } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { DataTable, DataTableRowMouseEvent } from 'primereact/datatable';
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import useRefState from 'react-usestateref';
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 classes from './SystemSignaturesContent.module.scss';
import clsx from 'clsx';
@@ -21,48 +22,122 @@ import {
getRowColorByTimeLeft,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
import {
renderAddedTimeLeft,
renderDescription,
renderIcon,
renderName,
renderTimeLeft,
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';
type SystemSignaturesSortSettings = {
sortField: string;
sortOrder: SortOrder;
};
const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
sortField: 'inserted_at',
sortOrder: -1,
};
interface SystemSignaturesContentProps {
systemId: string;
settings: Setting[];
hideLinkedSignatures?: boolean;
selectable?: boolean;
onSelect?: (signature: SystemSignature) => void;
onLazyDeleteChange?: (value: boolean) => void;
}
export const SystemSignaturesContent = ({ systemId, settings }: SystemSignaturesContentProps) => {
export const SystemSignaturesContent = ({
systemId,
settings,
hideLinkedSignatures,
selectable,
onSelect,
onLazyDeleteChange,
}: SystemSignaturesContentProps) => {
const { outCommand } = useMapRootState();
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
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 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 { clipboardContent } = useClipboard();
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 = 265;
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 showUpdatedColumn = useMemo(() => settings.find(s => s.key === SHOW_UPDATED_COLUMN_SETTING)?.value, [settings]);
const filteredSignatures = useMemo(() => {
return signatures
.filter(x => settings.find(y => y.key === x.kind)?.value)
.filter(x => {
if (hideLinkedSignatures && !!x.linked_system) {
return false;
}
const isCosmicSignature = x.kind === COSMIC_SIGNATURE;
if (isCosmicSignature) {
const showCosmicSignatures = settings.find(y => y.key === COSMIC_SIGNATURE)?.value;
if (showCosmicSignatures) {
return !x.group || groupSettings.find(y => y.key === x.group)?.value;
} else {
return !!x.group && groupSettings.find(y => y.key === x.group)?.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]);
}, [signatures, settings, groupSettings, hideLinkedSignatures]);
const handleGetSignatures = useCallback(async () => {
const { signatures } = await outCommand({
@@ -74,8 +149,13 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
}, [outCommand, systemId]);
const handleUpdateSignatures = useCallback(
async (newSignatures: SystemSignature[]) => {
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures);
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,
@@ -93,34 +173,86 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
[outCommand, systemId],
);
const handleDeleteSelected = useCallback(async () => {
if (selectedSignatures.length === 0) {
return;
}
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
await handleUpdateSignatures(signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)));
}, [handleUpdateSignatures, signatures, selectedSignatures]);
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]);
useHotkey(true, ['a'], handleSelectAll);
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],
);
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
useEffect(() => {
if (!clipboardContent) {
return;
}
const signatures = parseSignatures(
const handlePaste = async (clipboardContent: string) => {
const newSignatures = parseSignatures(
clipboardContent,
settings.map(x => x.key),
);
handleUpdateSignatures(signatures);
}, [clipboardContent]);
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) {
@@ -131,6 +263,18 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
handleGetSignatures();
}, [systemId]);
useMapEventListener(event => {
switch (event.name) {
case Commands.signaturesUpdated:
if (event.data?.toString() !== systemId.toString()) {
return;
}
handleGetSignatures();
return true;
}
});
useEffect(() => {
const observer = new ResizeObserver(handleResize);
if (tableRef.current) {
@@ -146,96 +290,152 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
};
}, []);
const handleEnterRow = useCallback(
(e: DataTableRowMouseEvent) => {
setHoveredSig(filteredSignatures[e.index]);
tooltipRef.current?.show(e.originalEvent);
},
[filteredSignatures],
);
const renderToolbar = (/*row: SystemSignature*/) => {
return (
<div className="flex justify-end items-center gap-2 mr-[4px]">
<WdTooltipWrapper content="To Edit Signature do double click">
<span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span>
</WdTooltipWrapper>
</div>
);
};
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
tooltipRef.current?.hide(e.originalEvent);
setHoveredSig(null);
}, []);
const [showSignatureSettings, setShowSignatureSettings] = useState(false);
const handleRowClick = (e: DataTableRowClickEvent) => {
setSelectedSignature(e.data as SystemSignature);
setShowSignatureSettings(true);
};
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>
) : (
<>
<DataTable
value={filteredSignatures}
size="small"
selectionMode="multiple"
selection={selectedSignatures}
onSelectionChange={e => setSelectedSignatures(e.value)}
dataKey="eve_id"
tableClassName="w-full select-none"
resizableColumns
rowHover
selectAll
showHeaders={false}
onRowMouseEnter={handleEnterRow}
onRowMouseLeave={handleLeaveRow}
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');
}
<>
<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.updated_at ? new Date(row.updated_at) : undefined);
if (!dateClass) {
return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 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);
}}
>
<Column
bodyClassName="p-0 px-1"
field="group"
body={renderIcon}
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
></Column>
return clsx(classes.TableRowCompact, dateClass);
}}
>
<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"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
style={{ maxWidth: 72, minWidth: 72, width: 72 }}
></Column>
<Column
field="group"
header="Group"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
hidden={compact}
></Column>
<Column
field="name"
header="Name"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderName}
style={{ maxWidth: nameColumnWidth }}
hidden={compact || medium}
></Column>
<Column
field="updated_at"
header="Updated"
dataType="date"
bodyClassName="w-[80px] text-ellipsis overflow-hidden whitespace-nowrap"
body={renderTimeLeft}
></Column>
</DataTable>
</>
)}
<WdTooltip
className="bg-stone-900/95 text-slate-50"
ref={tooltipRef}
content={hoveredSig ? <SignatureView {...hoveredSig} /> : null}
/>
</div>
<Column
field="eve_id"
header="Id"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
style={{ maxWidth: 72, minWidth: 72, width: 72 }}
sortable
></Column>
<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"
dataType="date"
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
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}
/>
{showSignatureSettings && (
<SignatureSettings
systemId={systemId}
show
onHide={() => setShowSignatureSettings(false)}
signatureData={selectedSignature}
/>
)}
</div>
</>
);
};

View File

@@ -5,6 +5,8 @@ import { getState } from './getState.ts';
export const getActualSigs = (
oldSignatures: SystemSignature[],
newSignatures: SystemSignature[],
updateOnly: boolean,
skipUpdateUntouched?: boolean,
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
const updated: SystemSignature[] = [];
const removed: SystemSignature[] = [];
@@ -18,9 +20,13 @@ export const getActualSigs = (
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
if (isNeedUpgrade) {
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
} else if (!skipUpdateUntouched) {
updated.push({ ...oldSig });
}
} else {
removed.push(oldSig);
if (!updateOnly) {
removed.push(oldSig);
}
}
});

View File

@@ -7,9 +7,9 @@ export const getState = (_: string[], newSig: SystemSignature) => {
let state = -1;
if (!newSig.group || newSig.group === '') {
state = 0;
} else if (!!newSig.group && newSig.group !== '' && newSig.name === '') {
} else if (!newSig.name || newSig.name === '') {
state = 1;
} else if (!!newSig.group && newSig.group !== '' && newSig.name !== '') {
} else if (newSig.name !== '') {
state = 2;
}
return state;

View File

@@ -1,3 +1,7 @@
export * from './renderIcon';
export * from './renderDescription';
export * from './renderName';
export * from './renderTimeLeft';
export * from './renderAddedTimeLeft';
export * from './renderUpdatedTimeLeft';
export * from './renderLinkedSystem';
export * from './renderInfoColumn';

View File

@@ -0,0 +1,10 @@
import { SystemSignature } from '@/hooks/Mapper/types';
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
export const renderAddedTimeLeft = (row: SystemSignature) => {
return (
<div className="flex w-full items-center">
<TimeLeft cDate={row.inserted_at ? new Date(row.inserted_at) : undefined} />
</div>
);
};

View File

@@ -0,0 +1,5 @@
import { SystemSignature } from '@/hooks/Mapper/types';
export const renderDescription = (row: SystemSignature) => {
return <span title={row?.description}>{row?.description}</span>;
};

View File

@@ -1,7 +1,7 @@
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { GroupType, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { GROUPS } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
export const renderIcon = (row: SystemSignature) => {
export const renderIcon = (row: SystemSignature, customSize?: Omit<GroupType, 'icon' | 'id'>) => {
if (row.group == null) {
return null;
}
@@ -13,7 +13,7 @@ export const renderIcon = (row: SystemSignature) => {
return (
<div className="flex justify-center items-center">
<img src={group.icon} style={{ width: group.w, height: group.h }} />
<img src={group.icon} style={{ width: customSize?.w ?? group.w, height: customSize?.h ?? group.h }} />
</div>
);
};

View File

@@ -0,0 +1,69 @@
import { PrimeIcons } from 'primereact/api';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { SystemViewStandalone, WHClassView } from '@/hooks/Mapper/components/ui-kit';
import {
k162Types,
renderK162Type,
} from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import clsx from 'clsx';
import { renderName } from './renderName.tsx';
export const renderInfoColumn = (row: SystemSignature) => {
if (!row.group || row.group === SignatureGroup.Wormhole) {
let k162TypeOption = null;
if (row.custom_info) {
const customInfo = JSON.parse(row.custom_info);
if (customInfo.k162Type) {
k162TypeOption = k162Types.find(x => x.value === customInfo.k162Type);
}
}
return (
<div className="flex justify-start items-center gap-[4px]">
{row.type && (
<WHClassView
className="text-[11px]"
classNameWh="!text-[11px] !font-bold"
hideWhClass={!!row.linked_system}
whClassName={row.type}
noOffset
useShortTitle
/>
)}
{!row.linked_system && row.type === 'K162' && !!k162TypeOption && <>{renderK162Type(k162TypeOption)}</>}
{row.linked_system && (
<>
<span title={row.linked_system?.solar_system_name}>
<SystemViewStandalone
className={clsx('select-none text-center cursor-context-menu')}
hideRegion
{...row.linked_system}
/>
</span>
</>
)}
{row.description && (
<WdTooltipWrapper content={row.description}>
<span className={clsx(PrimeIcons.EXCLAMATION_CIRCLE, 'text-[12px]')}></span>
</WdTooltipWrapper>
)}
</div>
);
}
return (
<div className="flex gap-1 items-center">
{renderName(row)}{' '}
{row.description && (
<WdTooltipWrapper content={row.description}>
<span className={clsx(PrimeIcons.EXCLAMATION_CIRCLE, 'text-[12px]')}></span>
</WdTooltipWrapper>
)}
</div>
);
};

View File

@@ -0,0 +1,20 @@
import clsx from 'clsx';
import { SystemSignature } from '@/hooks/Mapper/types';
import { SystemViewStandalone } from '@/hooks/Mapper/components/ui-kit';
export const renderLinkedSystem = (row: SystemSignature) => {
if (!row.linked_system) {
return null;
}
return (
<span title={row.linked_system?.solar_system_name}>
<SystemViewStandalone
className={clsx('select-none text-center cursor-context-menu')}
hideRegion
{...row.linked_system}
/>
</span>
);
};

View File

@@ -1,9 +1,9 @@
import { SystemSignature } from '@/hooks/Mapper/types';
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
export const renderTimeLeft = (row: SystemSignature) => {
export const renderUpdatedTimeLeft = (row: SystemSignature) => {
return (
<div className="flex justify-end w-full items-center">
<div className="flex w-full items-center">
<TimeLeft cDate={row.updated_at ? new Date(row.updated_at) : undefined} />
</div>
);

View File

@@ -6,21 +6,27 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useCallback, useState } from 'react';
import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components';
import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx';
import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu';
import { MapSettings } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings';
export interface MapRootContentProps {}
// eslint-disable-next-line no-empty-pattern
export const MapRootContent = ({}: MapRootContentProps) => {
const { mapRef, interfaceSettings } = useMapRootState();
const { interfaceSettings } = useMapRootState();
const { isShowMenu } = interfaceSettings;
const [showOnTheMap, setShowOnTheMap] = useState(false);
const [showMapSettings, setShowMapSettings] = useState(false);
const mapInterface = <MapInterface />;
const handleShowOnTheMap = useCallback(() => setShowOnTheMap(true), []);
const handleShowMapSettings = useCallback(() => setShowMapSettings(true), []);
useSkipContextMenu();
return (
<Layout map={<MapWrapper refn={mapRef} />}>
<Layout map={<MapWrapper />}>
{!isShowMenu ? (
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
@@ -28,18 +34,19 @@ export const MapRootContent = ({}: MapRootContentProps) => {
{mapInterface}
</div>
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
<RightBar onShowOnTheMap={handleShowOnTheMap} />
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
</div>
</div>
) : (
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
<Topbar>
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} />
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
</Topbar>
{mapInterface}
</div>
)}
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
</Layout>
);
};

View File

@@ -1,13 +1,22 @@
import classes from './Connections.module.scss';
import { Sidebar } from 'primereact/sidebar';
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useState, useCallback } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import clsx from 'clsx';
import { ConnectionOutput, OutCommand, Passage, SolarSystemConnection } from '@/hooks/Mapper/types';
import {
ConnectionType,
ConnectionOutput,
ConnectionInfoOutput,
OutCommand,
Passage,
SolarSystemConnection,
} from '@/hooks/Mapper/types';
import { PassageCard } from './PassageCard';
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
import { TimeAgo } from '@/hooks/Mapper/components/ui-kit';
const sortByDate = (a: string, b: string) => new Date(a).getTime() - new Date(b).getTime();
@@ -68,26 +77,49 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
return connections.find(x => x.source === selectedConnection.source && x.target === selectedConnection.target);
}, [connections, selectedConnection]);
const isWormhole = useMemo(() => {
return cnInfo?.type !== ConnectionType.gate;
}, [cnInfo]);
const [passages, setPassages] = useState<Passage[]>([]);
const [info, setInfo] = useState<ConnectionInfoOutput | null>(null);
const loadInfo = useCallback(
async (connection: SolarSystemConnection) => {
const result = await outCommand<ConnectionInfoOutput>({
type: OutCommand.getConnectionInfo,
data: {
from: connection.source,
to: connection.target,
},
});
setInfo(result);
},
[outCommand],
);
const loadPassages = useCallback(
async (connection: SolarSystemConnection) => {
const result = await outCommand<ConnectionOutput>({
type: OutCommand.getPassages,
data: {
from: connection.source,
to: connection.target,
},
});
setPassages(result.passages.sort((a, b) => sortByDate(b.inserted_at, a.inserted_at)));
},
[outCommand],
);
useEffect(() => {
if (!selectedConnection) {
return;
}
const loadInfo = async () => {
const result = await outCommand<ConnectionOutput>({
type: OutCommand.getPassages,
data: {
from: selectedConnection.source,
to: selectedConnection.target,
},
});
setPassages(result.passages.sort((a, b) => sortByDate(b.inserted_at, a.inserted_at)));
};
loadInfo();
loadInfo(selectedConnection);
loadPassages(selectedConnection);
}, [selectedConnection]);
const approximateMass = useMemo(() => {
@@ -109,7 +141,7 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
>
<div className={clsx(classes.SidebarContent, '')}>
{/* Connection Info */}
<div className="px-2 pb-3 flex flex-col gap-2">
<div className="px-2 flex flex-col gap-2">
{/* Connection Info Row */}
<InfoDrawer title="Connection" rightSide>
<div className="flex justify-end gap-2 items-center">
@@ -127,10 +159,25 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
</div>
</InfoDrawer>
{/* Connection Info Row */}
<InfoDrawer title="Approximate mass of passages" rightSide>
{kgToTons(approximateMass)}
</InfoDrawer>
<div className="flex justify-between gap-2">
{/*Left column*/}
<div>
{isWormhole && info?.marl_eol_time && (
<InfoDrawer title="Mark EOL Time">
<TimeAgo timestamp={info.marl_eol_time} />
</InfoDrawer>
)}
</div>
{/*Right column*/}
<div>
{isWormhole && (
<InfoDrawer title="Approximate mass of passages" rightSide>
{kgToTons(approximateMass)}
</InfoDrawer>
)}
</div>
</div>
<div className="flex gap-2"></div>
</div>

View File

@@ -5,13 +5,18 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { OutCommand } from '@/hooks/Mapper/types';
import { MenuItem } from 'primereact/menuitem';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
export interface MapContextMenuProps {
onShowOnTheMap?: () => void;
onShowMapSettings?: () => void;
}
export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
const { outCommand, setInterfaceSettings } = useMapRootState();
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
const menuRight = useRef<Menu>(null);
@@ -22,42 +27,41 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
});
}, [outCommand]);
const toggleMinimap = useCallback(() => {
setInterfaceSettings(x => ({
...x,
isShowMinimap: !x.isShowMinimap,
}));
}, [setInterfaceSettings]);
const items = useMemo(() => {
return [
{
label: 'Tracking',
icon: 'pi pi-user-plus',
command: handleAddCharacter,
},
{
label: 'On the map',
icon: 'pi pi-hashtag',
command: onShowOnTheMap,
},
{ separator: true },
{
label: interfaceSettings.isShowMinimap ? 'Hide minimap' : 'Show minimap',
icon: `pi ${interfaceSettings.isShowMinimap ? 'pi-eye-slash' : 'pi-eye'}`,
command: toggleMinimap,
},
{
label: 'Dock menu',
icon: 'pi pi-window-maximize',
command: () =>
setInterfaceSettings(x => ({
...x,
isShowMenu: !x.isShowMenu,
})),
},
] as MenuItem[];
}, [handleAddCharacter, interfaceSettings.isShowMinimap, onShowOnTheMap, setInterfaceSettings, toggleMinimap]);
return (
[
{
label: 'Tracking',
icon: 'pi pi-user-plus',
command: handleAddCharacter,
visible: true,
},
{
label: 'On the map',
icon: 'pi pi-hashtag',
command: onShowOnTheMap,
visible: canTrackCharacters,
},
{ separator: true, visible: true },
{
label: 'Settings',
icon: `pi pi-cog`,
command: onShowMapSettings,
visible: true,
},
{
label: 'Dock menu',
icon: 'pi pi-window-maximize',
command: () =>
setInterfaceSettings(x => ({
...x,
isShowMenu: !x.isShowMenu,
})),
visible: true,
},
] as MenuItem[]
).filter(item => item.visible);
}, [canTrackCharacters, handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
return (
<div className="ml-1">

View File

@@ -0,0 +1,127 @@
.verticalTabsContainer {
display: flex;
width: 100%;
min-height: 300px;
:global {
.p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
}
.p-tabview-nav-container {
border-right: none;
height: 100%;
}
.p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition: background-color 200ms, border-right-color 200ms;
&:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.p-tabview-nav-link {
transition: color 200ms;
justify-content: flex-end;
padding: 10px;
//background-color: var(--surface-card);
background-color: initial;
border: none;
color: var(--gray-400);
border-radius: initial;
font-weight: 400;
margin: 0;
}
&.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
.p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
&:hover {
//background-color: var(--surface-hover);
border-right: 4px solid var(--primary-color);
}
}
}
}
.p-tabview-panel {
flex-grow: 1;
}
}
}
.CheckboxContainer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
& > span:nth-child(1) {
color: var(--gray-200);
font-size: 13px;
}
& > :nth-child(2){
border-bottom: 2px dotted #3f3f3f;
height: 2px;
margin: 0 12px;
}
}
/* Уменьшение размеров InputSwitch с использованием глобальных стилей */
.smallInputSwitch {
height: 100%;
display: flex;
align-items: center;
:global {
.p-inputswitch {
height: 1rem;
width: 2rem;
&.p-inputswitch-checked {
.p-inputswitch-slider::before {
transform: translateX(1rem);
}
}
&.p-highlight .p-inputswitch-slider:before {
transform: translateX(1rem);
}
.p-inputswitch-slider::before {
width: 0.8rem;
height: 0.8rem;
margin-top: -0.4rem;
margin-left: -3px;
}
}
}
}

View File

@@ -0,0 +1,179 @@
import styles from './MapSettings.module.scss';
import { Dialog } from 'primereact/dialog';
import { useCallback, useMemo, useState } from 'react';
import { TabPanel, TabView } from 'primereact/tabview';
import { PrettySwitchbox } from './components';
import { InterfaceStoredSettings, InterfaceStoredSettingsProps, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { OutCommand } from '@/hooks/Mapper/types';
export enum UserSettingsRemoteProps {
link_signature_on_splash = 'link_signature_on_splash',
select_on_spash = 'select_on_spash',
delete_connection_with_sigs = 'delete_connection_with_sigs',
}
export const DEFAULT_REMOTE_SETTINGS = {
[UserSettingsRemoteProps.link_signature_on_splash]: false,
[UserSettingsRemoteProps.select_on_spash]: false,
[UserSettingsRemoteProps.delete_connection_with_sigs]: false,
};
export const UserSettingsRemoteList = [
UserSettingsRemoteProps.link_signature_on_splash,
UserSettingsRemoteProps.select_on_spash,
UserSettingsRemoteProps.delete_connection_with_sigs,
];
export type UserSettingsRemote = {
link_signature_on_splash: boolean;
select_on_spash: boolean;
delete_connection_with_sigs: boolean;
};
export type UserSettings = UserSettingsRemote & InterfaceStoredSettings;
export interface MapSettingsProps {
show: boolean;
onHide: () => void;
}
type CheckboxesList = {
prop: keyof UserSettings;
label: string;
}[];
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
];
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' },
{ prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' },
];
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
{ prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures, label: 'Show unsplashed signatures' },
];
const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' },
];
const UI_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
{ prop: InterfaceStoredSettingsProps.isShowBackgroundPattern, label: 'Show background pattern' },
{ prop: InterfaceStoredSettingsProps.isSoftBackground, label: 'Enable soft background' },
];
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({ ...DEFAULT_REMOTE_SETTINGS });
const mergedSettings = useMemo(() => {
return {
...interfaceSettings,
...userRemoteSettings,
};
}, [userRemoteSettings, interfaceSettings]);
const handleShow = async () => {
const { user_settings } = await outCommand({
type: OutCommand.getUserSettings,
data: null,
});
setUserRemoteSettings({
...user_settings,
});
};
const handleChangeChecked = useCallback(
(prop: keyof UserSettings) => async (checked: boolean) => {
// @ts-ignore
if (UserSettingsRemoteList.includes(prop)) {
const newRemoteSettings = {
...userRemoteSettings,
[prop]: checked,
};
await outCommand({
type: OutCommand.updateUserSettings,
data: newRemoteSettings,
});
setUserRemoteSettings(newRemoteSettings);
return;
}
setInterfaceSettings({
...interfaceSettings,
[prop]: checked,
});
},
[interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings],
);
const renderCheckboxesList = (list: CheckboxesList) => {
return list.map(x => {
return (
<PrettySwitchbox
key={x.prop}
label={x.label}
checked={mergedSettings[x.prop]}
setChecked={handleChangeChecked(x.prop)}
/>
);
});
};
return (
<Dialog
header="Map user settings"
visible={show}
draggable={false}
style={{ width: '550px' }}
onShow={handleShow}
onHide={() => {
if (!show) {
return;
}
setActiveIndex(0);
onHide();
}}
>
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
<div className={styles.verticalTabsContainer}>
<TabView
activeIndex={activeIndex}
onTabChange={e => setActiveIndex(e.index)}
className={styles.verticalTabView}
>
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">{renderCheckboxesList(COMMON_CHECKBOXES_PROPS)}</div>
</TabPanel>
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)}
</div>
</TabPanel>
<TabPanel header="Connections" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(UI_CHECKBOXES_PROPS)}
</TabPanel>
</TabView>
</div>
</div>
</div>
</Dialog>
);
};

View File

@@ -0,0 +1,48 @@
.CheckboxContainer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
& > span:nth-child(1) {
color: var(--gray-200);
font-size: 13px;
user-select: none;
}
& > :nth-child(2){
border-bottom: 2px dotted #3f3f3f;
height: 1px;
margin: 0 12px;
}
}
/* Уменьшение размеров InputSwitch с использованием глобальных стилей */
.smallInputSwitch {
height: 100%;
display: flex;
align-items: center;
:global {
.p-inputswitch {
height: 1rem;
width: 2rem;
&.p-inputswitch-checked {
.p-inputswitch-slider::before {
transform: translateX(1rem);
}
}
&.p-highlight .p-inputswitch-slider:before {
transform: translateX(1rem);
}
.p-inputswitch-slider::before {
width: 0.8rem;
height: 0.8rem;
margin-top: -0.4rem;
margin-left: -3px;
}
}
}
}

View File

@@ -0,0 +1,20 @@
import styles from './MapSettings.module.scss';
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
interface PrettySwitchboxProps {
checked: boolean;
setChecked: (checked: boolean) => void;
label: string;
}
export const PrettySwitchbox = ({ checked, setChecked, label }: PrettySwitchboxProps) => {
return (
<label className={styles.CheckboxContainer}>
<span>{label}</span>
<div />
<div className={styles.smallInputSwitch}>
<WdCheckbox size="m" label={''} value={checked} onChange={e => setChecked(e.checked ?? false)} />
</div>
</label>
);
};

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,8 @@ import clsx from 'clsx';
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { CharacterCard, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import useLocalStorageState from 'use-local-storage-state';
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
type WindowLocalSettingsType = {
compact: boolean;
@@ -50,14 +52,22 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
defaultValue: STORED_DEFAULT_VALUES,
});
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
const showOffline = useMemo(
() => !restrictOfflineShowing || isAdminOrManager,
[isAdminOrManager, restrictOfflineShowing],
);
const sorted = useMemo(() => {
const out = characters.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id) })).sort(sortCharacters);
if (!settings.hideOffline) {
if (showOffline && !settings.hideOffline) {
return out;
}
return out.filter(x => x.online);
}, [characters, settings.hideOffline, userCharacters]);
}, [showOffline, characters, settings.hideOffline, userCharacters]);
return (
<Sidebar
@@ -70,14 +80,16 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
>
<div className={clsx(classes.SidebarContent, '')}>
<div className={'flex justify-end items-center gap-2 px-3'}>
<WdCheckbox
size="m"
labelSide="left"
label={'Hide offline'}
value={settings.hideOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
/>
{showOffline && (
<WdCheckbox
size="m"
labelSide="left"
label={'Hide offline'}
value={settings.hideOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
/>
)}
</div>
<VirtualScroller

View File

@@ -6,13 +6,21 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
interface RightBarProps {
onShowOnTheMap?: () => void;
onShowMapSettings?: () => void;
}
export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
const handleAddCharacter = useCallback(() => {
outCommand({
type: OutCommand.addCharacter,
@@ -27,6 +35,13 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
}));
}, [setInterfaceSettings]);
const toggleKSpace = useCallback(() => {
setInterfaceSettings(x => ({
...x,
isShowKSpace: !x.isShowKSpace,
}));
}, [setInterfaceSettings]);
const toggleMenu = useCallback(() => {
setInterfaceSettings(x => ({
...x,
@@ -50,36 +65,56 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
type="button"
onClick={handleAddCharacter}
>
<i className="pi pi-user-plus text-lg"></i>
<i className="pi pi-user-plus"></i>
</button>
</WdTooltipWrapper>
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={onShowOnTheMap}
>
<i className="pi pi-hashtag text-lg"></i>
</button>
</WdTooltipWrapper>
{canTrackCharacters && (
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={onShowOnTheMap}
>
<i className="pi pi-hashtag"></i>
</button>
</WdTooltipWrapper>
)}
</div>
<div className="flex flex-col items-center mb-2 gap-1">
<WdTooltipWrapper content="Map user settings" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={onShowMapSettings}
>
<i className="pi pi-cog"></i>
</button>
</WdTooltipWrapper>
<WdTooltipWrapper
content={interfaceSettings.isShowMinimap ? 'Hide minimap' : 'Show minimap'}
content={
interfaceSettings.isShowKSpace ? 'Hide highlighting Imperial Space' : 'Show highlighting Imperial Space'
}
position={TooltipPosition.left}
>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={toggleKSpace}
>
<i className={interfaceSettings.isShowKSpace ? 'hero-cloud-solid' : 'hero-cloud'}></i>
</button>
</WdTooltipWrapper>
<WdTooltipWrapper content={isShowMinimap ? 'Hide minimap' : 'Show minimap'} position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={toggleMinimap}
>
{interfaceSettings.isShowMinimap ? (
<i className="pi pi-eye text-lg"></i>
) : (
<i className="pi pi-eye-slash text-lg"></i>
)}
<i className={isShowMinimap ? 'pi pi-eye' : 'pi pi-eye-slash'}></i>
</button>
</WdTooltipWrapper>
@@ -89,7 +124,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
type="button"
onClick={toggleMenu}
>
<i className="pi pi-window-minimize text-lg"></i>
<i className="pi pi-window-minimize"></i>
</button>
</WdTooltipWrapper>
</div>

View File

@@ -0,0 +1,10 @@
import { createGenericContext } from '@/hooks/Mapper/utils/abstractContextProvider.tsx';
export interface SystemsSettingsProvider {
systemId: string;
}
const { Provider, useContextValue } = createGenericContext<SystemsSettingsProvider>();
export const SystemsSettingsProvider = Provider;
export const useSystemsSettingsProvider = useContextValue;

View File

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

View File

@@ -0,0 +1,190 @@
import { Dialog } from 'primereact/dialog';
import { useCallback, useEffect } from 'react';
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import {
SignatureGroupContent,
SignatureGroupSelect,
} from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components';
import { InputText } from 'primereact/inputtext';
import { SystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
import { Button } from 'primereact/button';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
type SystemSignaturePrepared = Omit<SystemSignature, 'linked_system'> & { linked_system: string };
export interface MapSettingsProps {
systemId: string;
show: boolean;
onHide: () => void;
signatureData: SystemSignature | null;
}
export const SignatureSettings = ({ systemId, show, onHide, signatureData }: MapSettingsProps) => {
const { outCommand } = useMapRootState();
const handleShow = async () => {};
const signatureForm = useForm<Partial<SystemSignaturePrepared>>({});
const handleSave = useCallback(
async (e: any) => {
e?.preventDefault();
if (!signatureData) {
return;
}
const { group, ...values } = signatureForm.getValues();
let out = { ...signatureData };
switch (group) {
case SignatureGroup.Wormhole:
if (values.linked_system) {
await outCommand({
type: OutCommand.linkSignatureToSystem,
data: {
signature_eve_id: signatureData.eve_id,
solar_system_source: systemId,
solar_system_target: values.linked_system,
},
});
}
out = {
...out,
custom_info: JSON.stringify({
k162Type: values.k162Type,
}),
};
if (values.type != null) {
out = { ...out, type: values.type };
}
if (signatureData.group !== SignatureGroup.Wormhole) {
out = { ...out, name: '' };
}
break;
case SignatureGroup.CosmicSignature:
out = { ...out, type: '', name: '' };
break;
default:
if (values.name != null) {
out = { ...out, name: values.name ?? '' };
}
}
if (values.description != null) {
out = { ...out, description: values.description };
}
// Note: when type of signature changed from WH to other type - we should drop name
if (
group !== SignatureGroup.Wormhole && // new
signatureData.group === SignatureGroup.Wormhole && // prev
signatureData.linked_system
) {
await outCommand({
type: OutCommand.unlinkSignature,
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
});
out = { ...out, type: '' };
}
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
await outCommand({
type: OutCommand.unlinkSignature,
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
});
}
// Note: despite groups have optional type - this will always set
out = { ...out, group: group! };
await outCommand({
type: OutCommand.updateSignatures,
data: {
system_id: systemId,
added: [],
updated: [out],
removed: [],
},
});
signatureForm.reset();
onHide();
},
[signatureForm, onHide, outCommand, signatureData, systemId],
);
useEffect(() => {
if (!signatureData) {
signatureForm.reset();
return;
}
const { linked_system, custom_info, ...rest } = signatureData;
let k162Type = null;
if (custom_info) {
const customInfo = JSON.parse(custom_info);
k162Type = customInfo.k162Type;
}
signatureForm.reset({
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
k162Type: k162Type,
...rest,
});
}, [signatureForm, signatureData]);
return (
<Dialog
header={`Signature Edit [${signatureData?.eve_id}]`}
visible={show}
draggable={false}
style={{ width: '390px' }}
onShow={handleShow}
onHide={() => {
if (!show) {
return;
}
onHide();
}}
>
<SystemsSettingsProvider initialValue={{ systemId }}>
<FormProvider {...signatureForm}>
<form onSubmit={handleSave}>
<div className="flex flex-col gap-2 justify-between">
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Group:</span>
<SignatureGroupSelect name="group" />
</label>
<SignatureGroupContent />
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Description:</span>
<Controller
name="description"
control={signatureForm.control}
render={({ field }) => (
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
)}
/>
</label>
</div>
<div className="flex gap-2 justify-end">
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
</div>
</div>
</form>
</FormProvider>
</SystemsSettingsProvider>
</Dialog>
);
};

View File

@@ -0,0 +1,45 @@
import { Controller, useFormContext } from 'react-hook-form';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
import { SignatureGroupContentWormholes } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureGroupContentWormholes.tsx';
import { InputText } from 'primereact/inputtext';
export interface SignatureGroupContentProps {}
export const SignatureGroupContent = ({}: SignatureGroupContentProps) => {
const { watch, control } = useFormContext<SystemSignature>();
const group = watch('group');
const {
value: { systemId },
} = useSystemsSettingsProvider();
if (!systemId) {
return null;
}
if (group === SignatureGroup.Wormhole) {
return (
<>
<SignatureGroupContentWormholes />
</>
);
}
if (group === SignatureGroup.CosmicSignature) {
return <div></div>;
}
return (
<div>
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Name:</span>
<Controller
name="name"
control={control}
render={({ field }) => <InputText placeholder="Name" value={field.value} onChange={field.onChange} />}
/>
</label>
</div>
);
};

View File

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

View File

@@ -0,0 +1,31 @@
import { useFormContext } from 'react-hook-form';
import { SystemSignature } from '@/hooks/Mapper/types';
import { SignatureWormholeTypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureWormholeTypeSelect';
import { SignatureK162TypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect';
export const SignatureGroupContentWormholes = () => {
const { watch } = useFormContext<SystemSignature>();
const type = watch('type');
return (
<>
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Type:</span>
<SignatureWormholeTypeSelect name="type" />
</label>
{type === 'K162' && (
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>K162 Type:</span>
<SignatureK162TypeSelect name="k162Type" />
</label>
)}
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Leads To:</span>
<SignatureLeadsToSelect name="linked_system" />
</label>
</>
);
};

View File

@@ -0,0 +1,59 @@
import { Dropdown } from 'primereact/dropdown';
import clsx from 'clsx';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { renderIcon } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
import { Controller, useFormContext } from 'react-hook-form';
const signatureGroupOptions = Object.keys(SignatureGroup).map(x => ({
value: SignatureGroup[x as keyof typeof SignatureGroup],
label: SignatureGroup[x as keyof typeof SignatureGroup],
}));
// @ts-ignore
const renderSignatureTemplate = option => {
if (!option) {
return 'No group selected';
}
return (
<div className="flex gap-2 items-center">
<span className="w-[20px] mt-[1px] flex justify-center items-center">
{renderIcon(
{ group: option.label } as SystemSignature,
option.label === SignatureGroup.CosmicSignature ? { w: 10, h: 10 } : { w: 16, h: 16 },
)}
</span>
<span>{option.label}</span>
</div>
);
};
export interface SignatureGroupSelectProps {
name: string;
defaultValue?: string;
}
export const SignatureGroupSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
defaultValue={defaultValue}
render={({ field }) => (
<Dropdown
value={field.value}
onChange={field.onChange}
options={signatureGroupOptions}
optionLabel="label"
optionValue="value"
placeholder="Select group"
className={clsx('w-full')}
scrollHeight="240px"
itemTemplate={renderSignatureTemplate}
valueTemplate={renderSignatureTemplate}
/>
)}
/>
);
};

View File

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

View File

@@ -0,0 +1,136 @@
import { Dropdown } from 'primereact/dropdown';
import clsx from 'clsx';
import { Controller, useFormContext } from 'react-hook-form';
import { useMemo } from 'react';
import { SystemSignature } from '@/hooks/Mapper/types';
import { WHClassView } from '@/hooks/Mapper/components/ui-kit';
export const k162Types = [
{
label: 'Hi-Sec',
value: 'hs',
whClassName: 'A641',
},
{
label: 'Low-Sec',
value: 'ls',
whClassName: 'J377',
},
{
label: 'Null-Sec',
value: 'ns',
whClassName: 'C248',
},
{
label: 'C1',
value: 'c1',
whClassName: 'E004',
},
{
label: 'C2',
value: 'c2',
whClassName: 'D382',
},
{
label: 'C3',
value: 'c3',
whClassName: 'L477',
},
{
label: 'C4',
value: 'c4',
whClassName: 'M001',
},
{
label: 'C5',
value: 'c5',
whClassName: 'L614',
},
{
label: 'C6',
value: 'c6',
whClassName: 'G008',
},
{
label: 'C13',
value: 'c13',
whClassName: 'A009',
},
{
label: 'Thera',
value: 'thera',
whClassName: 'F353',
},
{
label: 'Pochven',
value: 'pochven',
whClassName: 'F216',
},
];
const renderNoValue = () => <div className="flex gap-2 items-center">-Unknown-</div>;
// @ts-ignore
export const renderK162Type = (option: {
label?: string;
value: string;
security?: string;
system_class?: number;
whClassName?: string;
}) => {
if (!option) {
return renderNoValue();
}
const { value, whClassName = '' } = option;
if (value == null) {
return renderNoValue();
}
return (
<WHClassView
classNameWh="!text-[11px] !font-bold"
hideWhClassName
hideTooltip
whClassName={whClassName}
noOffset
useShortTitle
/>
);
};
export interface SignatureK162TypeSelectProps {
name: string;
defaultValue?: string;
}
export const SignatureK162TypeSelect = ({ name, defaultValue = '' }: SignatureK162TypeSelectProps) => {
const { control } = useFormContext<SystemSignature>();
const options = useMemo(() => {
return [{ value: null }, ...k162Types];
}, []);
return (
<Controller
// @ts-ignore
name={name}
control={control}
defaultValue={defaultValue}
render={({ field }) => {
return (
<Dropdown
value={field.value}
onChange={field.onChange}
options={options}
optionValue="value"
placeholder="Select K162 type"
className={clsx('w-full')}
scrollHeight="240px"
itemTemplate={renderK162Type}
valueTemplate={renderK162Type}
/>
);
}}
/>
);
};

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