Compare commits

..

7 Commits

Author SHA1 Message Date
Dmitry Popov
0a545ce5dd feat(Map): Added EOL status for unsplashed signatures. Show precise time for connection passages on hover. 2025-01-26 16:56:19 +01:00
Dmitry Popov
514f6933ff Merge branch 'main' into signature-eol 2025-01-25 09:09:43 +01:00
achichenkov
e25cc1e044 fix(Map): Refactor signatures code. Add ability to set EOL for signature marked as EOL 2025-01-18 16:26:47 +03:00
achichenkov
c614f1c6bc fix(Map): Add correct styles for switch 2025-01-18 14:24:46 +03:00
achichenkov
099ac9639c Merge branch 'refs/heads/main' into signature-eol 2025-01-18 12:07:02 +03:00
Dmitry Popov
109697cfeb chore: release version v1.39.1 2025-01-13 12:10:38 +01:00
Dmitry Popov
0681f3692e feat(Map): Added an ability to mark signature as EOL 2025-01-13 10:13:00 +01:00
476 changed files with 7985 additions and 56311 deletions

View File

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

View File

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

View File

@@ -14,15 +14,15 @@ services:
wanderer:
environment:
PORT: 4444
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: .
dockerfile: Dockerfile
ports:
- 4444:4444
- 8000:8000
volumes:
- ..:/app:delegated
- ~/.gitconfig:/root/.gitconfig

View File

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

View File

@@ -1,6 +1,8 @@
name: Build
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
push:
branches:
- main
@@ -39,28 +41,8 @@ jobs:
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
manual-approval:
name: Manual Approval
runs-on: ubuntu-latest
needs: deploy-test
if: success()
permissions:
issues: write
steps:
- name: Await Manual Approval
uses: trstringer/manual-approval@v1
with:
secret: ${{ github.TOKEN }}
approvers: DmitryPopov
minimum-approvals: 1
issue-title: "Manual Approval Required for Release"
issue-body: "Please approve or deny the deployment."
build:
name: 🛠 Build
needs: manual-approval
runs-on: ubuntu-22.04
if: ${{ (github.ref == 'refs/heads/main') && github.event_name == 'push' }}
permissions:

1
.gitignore vendored
View File

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

View File

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

View File

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

View File

@@ -58,7 +58,6 @@ Now you can visit [`localhost:8000`](http://localhost:8000) from your browser.
- `root@0d0a785313b6:/app# apt update`
- `root@0d0a785313b6:/app# curl -sL https://deb.nodesource.com/setup_18.x | bash -`
- `root@0d0a785313b6:/app# apt-get install nodejs inotify-tools -y`
- `root@0d0a785313b6:/app# npm install -g yarn`
- `root@0d0a785313b6:/app# mix setup`
- See how to run server in #Run section

View File

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

View File

@@ -25,10 +25,6 @@ body {
width: 400px; /* As IE6 ignores !important it will set width as 400px; */
}
body > div:first-of-type {
min-height: 500px !important;
}
.lending-normal {
font-family: 'Shentox', 'Rogan', sans-serif !important;
font-weight: 500;
@@ -112,19 +108,19 @@ body > div:first-of-type {
}
.wd-characters-icons {
/*display: flex;*/
/*transition:*/
/* border-color 250ms,*/
/* opacity 250ms;*/
/*width: 35px;*/
/*height: 35px;*/
/*border-radius: 50%;*/
/*border-width: 2px;*/
/*border-style: solid;*/
/*border-color: #5a5a5a;*/
/*background-color: rgba(0, 0, 0, 0);*/
/*cursor: pointer;*/
/*opacity: 0.6;*/
display: flex;
transition:
border-color 250ms,
opacity 250ms;
width: 35px;
height: 35px;
border-radius: 50%;
border-width: 2px;
border-style: solid;
border-color: #5a5a5a;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
opacity: 0.6;
}
.wd-bg-default {
@@ -936,66 +932,3 @@ body > div:first-of-type {
width: 16px;
height: 16px;
}
.verticalTabsContainer {
display: flex;
width: 100%;
min-height: 300px;
}
.verticalTabsContainer .p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.verticalTabsContainer .p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
height: 100%;
}
.verticalTabsContainer .p-tabview-nav-container {
border-right: none;
height: 100%;
}
.verticalTabsContainer .p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
}
.verticalTabsContainer .p-tabview-nav li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition:
background-color 200ms,
border-right-color 200ms;
}
.verticalTabsContainer .p-tabview-nav li:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.verticalTabsContainer .p-tabview-nav li .p-tabview-nav-link {
transition: color 200ms;
justify-content: flex-end;
padding: 10px;
background-color: initial;
border: none;
color: var(--gray-400);
border-radius: initial;
font-weight: 400;
margin: 0;
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected .p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected:hover {
border-right: 4px solid var(--primary-color);
}
.verticalTabsContainer .p-tabview-panel {
flex-grow: 1;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@ import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo }
import ReactFlow, {
Background,
Edge,
EdgeChange,
MiniMap,
Node,
NodeChange,
@@ -78,12 +79,12 @@ const edgeTypes = {
floating: SolarSystemEdge,
};
export const MAP_ROOT_ID = 'MAP_ROOT_ID';
interface MapCompProps {
refn: ForwardedRef<MapHandlers>;
onCommand: OutCommandHandler;
onSelectionChange: OnMapSelectionChange;
onManualDelete(systems: string[]): void;
canRemoveConnection?(connectionId: string): boolean;
onConnectionInfoClick?(e: SolarSystemConnection): void;
onAddSystem?: OnMapAddSystemCallback;
onSelectionContextMenu?: NodeSelectionMouseHandler;
@@ -105,6 +106,7 @@ const MapComp = ({
onSystemContextMenu,
onConnectionInfoClick,
onSelectionContextMenu,
onManualDelete,
isShowMinimap,
showKSpaceBG,
isThickConnections,
@@ -112,8 +114,9 @@ const MapComp = ({
isSoftBackground,
theme,
onAddSystem,
canRemoveConnection,
}: MapCompProps) => {
const { getNodes } = useReactFlow();
const { getEdge, getNode, getNodes } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
@@ -125,6 +128,7 @@ const MapComp = ({
const { variant, gap, size, color } = useBackgroundVars(theme);
const { isPanAndDrag, nodeComponent, connectionMode } = getBehaviorForTheme(theme || 'default');
// You can create nodeTypes dynamically based on the node component
const nodeTypes = useMemo(() => {
return {
custom: nodeComponent,
@@ -186,6 +190,8 @@ const MapComp = ({
const handleNodesChange = useCallback(
(changes: NodeChange[]) => {
const systemsIdsToRemove: string[] = [];
// prevents single node deselection on background / same node click
// allows deseletion of all nodes if multiple are currently selected
if (changes.length === 1 && changes[0].type == 'select' && changes[0].selected === false) {
@@ -193,12 +199,64 @@ const MapComp = ({
}
const nextChanges = changes.reduce((acc, change) => {
if (change.type !== 'remove') {
return [...acc, change];
}
const node = getNode(change.id);
if (!node) {
return [...acc, change];
}
if (node.data.locked) {
return acc;
}
systemsIdsToRemove.push(node.data.id);
return [...acc, change];
}, [] as NodeChange[]);
if (systemsIdsToRemove.length > 0) {
onManualDelete(systemsIdsToRemove);
}
onNodesChange(nextChanges);
},
[getNodes, onNodesChange],
[getNode, getNodes, onManualDelete, onNodesChange],
);
const handleEdgesChange = useCallback(
(changes: EdgeChange[]) => {
const nextChanges = changes.reduce((acc, change) => {
if (change.type !== 'remove') {
return [...acc, change];
}
if (canRemoveConnection?.(change.id)) {
return [...acc, change];
}
const edge = getEdge(change.id);
if (!edge) {
return [...acc, change];
}
const sourceNode = getNode(edge.source);
const targetNode = getNode(edge.target);
if (!sourceNode || !targetNode) {
return [...acc, change];
}
if (sourceNode.data.locked || targetNode.data.locked) {
return acc;
}
return [...acc, change];
}, [] as EdgeChange[]);
onEdgesChange(nextChanges);
},
[getEdge, getNode, onEdgesChange],
);
useEffect(() => {
@@ -211,15 +269,12 @@ const MapComp = ({
return (
<>
<div
data-window-id={MAP_ROOT_ID}
className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}
>
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={handleNodesChange}
onEdgesChange={onEdgesChange}
onEdgesChange={handleEdgesChange}
onConnect={onConnect}
// TODO we need save into session all of this
// and on any action do either
@@ -258,7 +313,7 @@ const MapComp = ({
minZoom={0.2}
maxZoom={1.5}
elevateNodesOnSelect
deleteKeyCode={['']}
deleteKeyCode={['Delete']}
{...(isPanAndDrag
? {
selectionOnDrag: true,

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext } from 'react';
import { OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { MapUnionTypes, SystemSignature } from '@/hooks/Mapper/types';
import { MapUnionTypes } from '@/hooks/Mapper/types';
import { ContextStoreDataUpdate, useContextStore } from '@/hooks/Mapper/utils';
export type MapData = MapUnionTypes & {
@@ -30,16 +30,10 @@ const INITIAL_DATA: MapData = {
isConnecting: false,
connections: [],
hoverNodeId: null,
linkedSigEveId: '',
visibleNodes: new Set(),
showKSpaceBG: false,
isThickConnections: false,
userPermissions: {},
systemSignatures: {} as Record<string, SystemSignature[]>,
options: {} as Record<string, string | boolean>,
isSubscriptionActive: false,
mainCharacterEveId: null,
followingCharacterEveId: null,
};
export interface MapContextProps {
@@ -55,7 +49,7 @@ const MapContext = createContext<MapContextProps>({
outCommand: async () => void 0,
});
export const MapProvider = ({ children, onCommand }: MapProviderProps) => {
export const MapProvider: React.FC<MapProviderProps> = ({ children, onCommand }) => {
const { update, ref } = useContextStore<MapData>({ ...INITIAL_DATA });
return (

View File

@@ -2,6 +2,7 @@ import React, { RefObject, useMemo } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { PrimeIcons } from 'primereact/api';
import { MenuItem } from 'primereact/menuitem';
import { Edge } from '@reactflow/core/dist/esm/types/edges';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import clsx from 'clsx';
import classes from './ContextMenuConnection.module.scss';
@@ -13,7 +14,6 @@ import {
SHIP_SIZES_NAMES_SHORT,
SHIP_SIZES_SIZE,
} from '@/hooks/Mapper/components/map/constants.ts';
import { Edge } from 'reactflow';
export interface ContextMenuConnectionProps {
contextMenuRef: RefObject<ContextMenu>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,30 +2,34 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020;
$node-bg-color: #202020;
$node-soft-bg-color: #202020;
$text-color: #ffffff;
$tag-color: #38BDF8;
$region-name: #D6D3D1;
$custom-name: #93C5FD;
.RootCustomNode {
display: flex;
width: 130px;
height: 34px;
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
flex-direction: column;
padding: 2px 6px;
font-size: 10px;
background-color: var(--rf-node-bg-color, #202020) !important;
color: var(--rf-text-color, #ffffff);
background-color: $tooltip-bg;
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
border: 1px solid darken($pastel-blue, 10%);
border-radius: 5px;
position: relative;
z-index: 3;
z-index: 1;
overflow: hidden;
&.Mataria,
@@ -88,9 +92,21 @@ $tooltip-bg: #202020;
box-shadow: 0 0 10px #9a1af1c2;
}
&.tooltip {
background-color: $tooltip-bg;
color: $text-color;
padding: 5px 10px;
border-radius: 3px;
border: 1px solid $pastel-pink;
}
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(45deg, var(--eve-solar-system-status-color-background), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-home),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
}
@@ -98,7 +114,11 @@ $tooltip-bg: #202020;
&.eve-system-status-friendly {
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly-dark30), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-friendly-dark30),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-friendly-dark5);
}
@@ -113,15 +133,27 @@ $tooltip-bg: #202020;
}
&.eve-system-status-warning {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-warning), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-warning),
transparent
);
}
&.eve-system-status-dangerous {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-dangerous), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-dangerous),
transparent
);
}
&.eve-system-status-target {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-target), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-target),
transparent
);
}
}
@@ -146,6 +178,8 @@ $tooltip-bg: #202020;
padding-left: 3px;
padding-right: 3px;
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
@@ -232,17 +266,26 @@ $tooltip-bg: #202020;
.TagTitle {
font-size: 11px;
font-weight: 500;
font-weight: medium;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
color: var(--rf-tag-color, #38bdf8);
color: var(--rf-tag-color, #38BDF8);
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
font-weight: bold;
}
}
.classSystemName {
//font-weight: bold;
}
.solarSystemName {
}
}
.BottomRow {
@@ -251,23 +294,22 @@ $tooltip-bg: #202020;
align-items: center;
height: 19px;
.hasLocalCounter {
margin-right: 2px;
&.countAbove9 {
margin-right: 1.5rem;
.localCounter {
display: flex;
//align-items: center;
gap: 2px;
& > i {
position: relative;
top: 1px;
}
}
.lockIcon {
font-size: 0.45rem;
font-weight: bold;
position: relative;
}
.mapMarker {
font-size: 0.45rem;
font-weight: bold;
position: relative;
& > span {
font-size: 9px;
line-height: 9px;
font-weight: 500;
//margin-top: 1px;
}
}
}
@@ -298,8 +340,7 @@ $tooltip-bg: #202020;
.Handlers {
position: absolute;
z-index: 4;
pointer-events: none;
z-index: 2;
top: 0;
left: 0;
width: 100%;
@@ -312,7 +353,6 @@ $tooltip-bg: #202020;
border: 1px solid $pastel-blue;
width: 5px;
height: 5px;
pointer-events: auto;
&.selected {
border-color: $pastel-pink;
@@ -355,15 +395,3 @@ $tooltip-bg: #202020;
}
}
}
.ShatteredIcon {
position: relative;
//top: -1px;
left: -1px;
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
background-image: url(/images/chart-network-svgrepo-com.svg)
}

View File

@@ -1,61 +1,43 @@
import { memo } from 'react';
import { MapSolarSystemType } from '../../map.types';
import { Handle, NodeProps, Position } from 'reactflow';
import { Handle, Position, NodeProps } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeDefault.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks';
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
EFFECT_BACKGROUND_STYLES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { Tag } from 'primereact/tag';
// let render = 0;
export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
// console.log('JOipP', `render ${nodeVars.id}`, render++);
return (
<>
{nodeVars.visible && (
<div className={classes.Bookmarks}>
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered, '!pr-[2px]')}>
<WdTooltipWrapper content="Shattered" position={TooltipPosition.top}>
<span className={clsx('block w-[10px] h-[10px]', classes.ShatteredIcon)} />
</WdTooltipWrapper>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{nodeVars.labelCustom}</span>
</div>
)}
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size={TooltipSize.lg}
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{nodeVars.killsCount && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</KillsCounter>
)}
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
@@ -71,10 +53,11 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
classes[STATUS_CLASSES[nodeVars.status]],
{
[classes.selected]: nodeVars.selected,
},
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
@@ -90,11 +73,7 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<Tag
value={nodeVars.tag}
severity="warning"
className="py-0 px-[2px] text-[9px] [&_.p-tag-value]:leading-[1.3]"
></Tag>
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{nodeVars.tag}</div>
)}
<div
@@ -109,7 +88,7 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={String(whClass)} id={String(whClass)} />
<WormholeClassComp key={whClass} id={whClass} />
))}
</div>
)}
@@ -134,18 +113,27 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{nodeVars.locked && (
<i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }} />
)}
{nodeVars.hubs.includes(nodeVars.solarSystemId.toString()) && (
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }} />
)}
{nodeVars.charactersInSystem.length > 0 && (
<div
className={clsx(classes.localCounter, {
['text-amber-300']: nodeVars.hasUserCharacters,
})}
>
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
<span className="font-sans">{nodeVars.charactersInSystem.length}</span>
</div>
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
@@ -157,7 +145,7 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
@@ -165,14 +153,14 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
</>
)}
<div className={classes.Handlers}>
<div onMouseDownCapture={nodeVars.dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {

View File

@@ -1,20 +1,91 @@
@import './SolarSystemNodeDefault.module.scss';
/* ---------------------------------------------
Only override what's different from the base
Currently none required
---------------------------------------------- */
/* ---------------------------
Only override what's different
--------------------------- */
/* 1) .RootCustomNode:
- new background-color using CSS var
- plus color, font-family, and font-weight */
.RootCustomNode {
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-home),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
background-color: var(--rf-node-bg-color, #202020) !important;
color: var(--rf-text-color, #ffffff);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
/* 2) .Bookmarks:
- add var-based font family/weight
*/
.Bookmarks {
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
/* 3) .HeadRow, .classTitle, .classSystemName:
- add new references to var-based font family/weight
*/
.HeadRow {
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
.classTitle {
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
@-moz-document url-prefix() {
.classSystemName {
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
}
.classSystemName {
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
}
/* 4) .BottomRow:
- introduces .tagTitle, .regionName, .customName, .localCounter
referencing new CSS variables */
.BottomRow {
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
.tagTitle {
font-size: 11px;
font-weight: medium;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
color: var(--rf-tag-color, #38BDF8);
}
.regionName {
color: var(--rf-region-name, #D6D3D1);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
.customName {
color: var(--rf-custom-name, #93C5FD);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
.localCounter {
display: flex;
color: var(--rf-has-user-characters, #fbbf24);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
gap: 2px;
.hasUserCharacters {
color: var(--rf-has-user-characters, #fbbf24);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
}
}

View File

@@ -1,60 +1,43 @@
import { memo } from 'react';
import { MapSolarSystemType } from '../../map.types';
import { Handle, NodeProps, Position } from 'reactflow';
import { Handle, Position, NodeProps } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeTheme.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks';
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
EFFECT_BACKGROUND_STYLES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
// let render = 0;
export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
// console.log('JOipP', `render ${nodeVars.id}`, render++);
return (
<>
{nodeVars.visible && (
<div className={classes.Bookmarks}>
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered, '!pr-[2px]')}>
<WdTooltipWrapper content="Shattered" position={TooltipPosition.top}>
<span className={clsx('block w-[10px] h-[10px]', classes.ShatteredIcon)} />
</WdTooltipWrapper>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{nodeVars.labelCustom}</span>
</div>
)}
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size={TooltipSize.lg}
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{nodeVars.killsCount && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</KillsCounter>
)}
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
@@ -70,10 +53,11 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
classes[STATUS_CLASSES[nodeVars.status]],
{
[classes.selected]: nodeVars.selected,
},
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
@@ -104,7 +88,7 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={String(whClass)} id={String(whClass)} />
<WormholeClassComp key={whClass} id={whClass} />
))}
</div>
)}
@@ -139,18 +123,27 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{nodeVars.locked && (
<i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }} />
)}
{nodeVars.hubs.includes(nodeVars.solarSystemId.toString()) && (
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }} />
)}
{nodeVars.charactersInSystem.length > 0 && (
<div
className={clsx(classes.localCounter, {
[classes.hasUserCharacters]: nodeVars.hasUserCharacters,
})}
>
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
<span className="font-sans">{nodeVars.charactersInSystem.length}</span>
</div>
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
@@ -162,7 +155,7 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
@@ -170,14 +163,14 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
</>
)}
<div className={classes.Handlers}>
<div onMouseDownCapture={nodeVars.dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@ export function useBackgroundVars(themeName?: string) {
const [gap, setGap] = useState<number>(16);
const [size, setSize] = useState<number>(1);
const [color, setColor] = useState('#81818b');
const [snapSize, setSnapSize] = useState<number>(25);
useEffect(() => {
// match any element whose entire `class` attribute ends with "-theme"
@@ -30,19 +29,16 @@ export function useBackgroundVars(themeName?: string) {
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
const cssVarSize = style.getPropertyValue('--rf-bg-size');
const cssVarSnapSize = style.getPropertyValue('--rf-snap-size');
const cssColor = style.getPropertyValue('--rf-bg-pattern-color');
const gapNum = parseInt(cssVarGap, 10) || 16;
const sizeNum = parseInt(cssVarSize, 10) || 1;
const snapSize = parseInt(cssVarSnapSize, 10) || 25; //react-flow default
setVariant(finalVariant);
setGap(gapNum);
setSize(sizeNum);
setColor(cssColor);
setSnapSize(snapSize);
}, [themeName]);
return { variant, gap, size, color, snapSize };
return { variant, gap, size, color };
}

View File

@@ -1,43 +0,0 @@
import { useMemo } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useSystemKills } from '@/hooks/Mapper/components/mapInterface/widgets/WSystemKills/hooks/useSystemKills.ts';
interface UseKillsCounterProps {
realSystemId: string;
}
export function useKillsCounter({ realSystemId }: UseKillsCounterProps) {
const { data: mapData, outCommand } = useMapRootState();
const { systems } = mapData;
const systemNameMap = useMemo(() => {
const m: Record<string, string> = {};
systems.forEach(sys => {
m[sys.id] = sys.temporary_name || sys.name || '???';
});
return m;
}, [systems]);
const { kills: allKills, isLoading } = useSystemKills({
systemId: realSystemId,
outCommand,
showAllVisible: false,
});
const filteredKills = useMemo(() => {
if (!allKills || allKills.length === 0) return [];
// Sort kills by time, most recent first, but don't limit the number of kills
return [...allKills].sort((a, b) => {
const aTime = a.kill_time ? new Date(a.kill_time).getTime() : 0;
const bTime = b.kill_time ? new Date(b.kill_time).getTime() : 0;
return bTime - aTime;
});
}, [allKills]);
return {
isLoading,
kills: filteredKills,
systemNameMap,
};
}

View File

@@ -1,31 +0,0 @@
import { useMemo } from 'react';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
interface UseLabelsInfoParams {
labels: string | null;
linkedSigPrefix: string | null;
isShowLinkedSigId: boolean;
}
export type LabelInfo = {
id: string;
shortName: string;
};
function sortedLabels(labels: string[]): LabelInfo[] {
if (!labels) return [];
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x] as LabelInfo);
}
export function useLabelsInfo({ labels, linkedSigPrefix, isShowLinkedSigId }: UseLabelsInfoParams) {
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
const labelCustom = useMemo(() => {
if (isShowLinkedSigId && linkedSigPrefix) {
return labelsManager.customLabel ? `${linkedSigPrefix}${labelsManager.customLabel}` : linkedSigPrefix;
}
return labelsManager.customLabel;
}, [linkedSigPrefix, isShowLinkedSigId, labelsManager]);
return { labelsInfo, labelCustom };
}

View File

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

View File

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

View File

@@ -7,15 +7,13 @@ import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { CharacterTypeRaw, OutCommand, SystemSignature } from '@/hooks/Mapper/types';
import { useUnsplashedSignatures } from './useUnsplashedSignatures';
import { useSystemName } from './useSystemName';
import { LabelInfo, useLabelsInfo } from './useLabelsInfo';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { CharacterTypeRaw, OutCommand } from '@/hooks/Mapper/types';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
function getActivityType(count: number): string {
function getActivityType(count: number) {
if (count <= 5) return 'activityNormal';
if (count <= 30) return 'activityWarn';
return 'activityDanger';
@@ -28,23 +26,16 @@ const SpaceToClass: Record<string, string> = {
[Spaces.Gallente]: 'Gallente',
};
export function useLocalCounter(nodeVars: SolarSystemNodeVars) {
const localCounterCharacters = useMemo(() => {
return nodeVars.charactersInSystem
.map(char => ({
...char,
compact: true,
isOwn: nodeVars.userCharacters.includes(char.eve_id),
}))
.sort((a, b) => a.name.localeCompare(b.name));
}, [nodeVars.charactersInSystem, nodeVars.userCharacters]);
return { localCounterCharacters };
function sortedLabels(labels: string[]) {
if (!labels) return [];
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
}
export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarSystemNodeVars {
export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>) {
const { id, data, selected } = props;
const {
id: solar_system_id,
system_static_info,
system_signatures,
locked,
name,
tag,
@@ -54,26 +45,23 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
linked_sig_eve_id: linkedSigEveId = '',
} = data;
const {
storedSettings: { interfaceSettings },
data: { systemSignatures: mapSystemSignatures },
} = useMapRootState();
const systemStaticInfo = useMemo(() => {
return getSystemStaticInfo(solar_system_id)!;
}, [solar_system_id]);
const {
system_class,
security,
class_title,
solar_system_id,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = systemStaticInfo;
} = system_static_info;
const {
interfaceSettings,
data: { systemSignatures: mapSystemSignatures },
} = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
@@ -83,6 +71,7 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
const {
data: {
characters,
presentCharacters,
wormholesData,
hubs,
kills,
@@ -98,11 +87,15 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const systemSigs = useMemo(() => mapSystemSignatures[solar_system_id] || [], [solar_system_id, mapSystemSignatures]);
const systemSignatures = useMemo(
() => mapSystemSignatures[solar_system_id] || system_signatures,
[system_signatures, solar_system_id, mapSystemSignatures],
);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === parseInt(solar_system_id) && c.online);
}, [characters, solar_system_id]);
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
// eslint-disable-next-line
}, [characters, presentCharacters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
@@ -115,55 +108,80 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
const linkedSigPrefix = useMemo(() => (linkedSigEveId ? linkedSigEveId.split('-')[0] : null), [linkedSigEveId]);
const { labelsInfo, labelCustom } = useLabelsInfo({
labels,
linkedSigPrefix,
isShowLinkedSigId,
});
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
const labelCustom = useMemo(() => {
if (isShowLinkedSigId && linkedSigPrefix) {
return labelsManager.customLabel ? `${linkedSigPrefix}${labelsManager.customLabel}` : linkedSigPrefix;
}
return labelsManager.customLabel;
}, [linkedSigPrefix, isShowLinkedSigId, labelsManager]);
const killsCount = useMemo(() => kills[parseInt(solar_system_id)] ?? null, [kills, solar_system_id]);
const killsCount = useMemo(() => kills[solar_system_id] ?? null, [kills, solar_system_id]);
const killsActivityType = killsCount ? getActivityType(killsCount) : null;
const hasUserCharacters = useMemo(
() => charactersInSystem.some(x => userCharacters.includes(x.eve_id)),
[charactersInSystem, userCharacters],
);
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: { system_id: solar_system_id },
data: { system_id: solar_system_id.toString() },
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] || null : null;
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const { systemName, computedTemporaryName, customName } = useSystemName({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
});
const temporaryName = useMemo(() => {
if (!isTempSystemNameEnabled) {
return '';
}
const { unsplashedLeft, unsplashedRight } = useUnsplashedSignatures(systemSigs, isShowUnsplashedSignatures);
if (isShowLinkedSigIdTempName && linkedSigPrefix) {
return temporary_name ? `${linkedSigPrefix}${temporary_name}` : `${linkedSigPrefix}${solar_system_name}`;
}
const hubsAsStrings = useMemo(() => hubs.map(item => item.toString()), [hubs]);
return temporary_name;
}, [isShowLinkedSigIdTempName, isTempSystemNameEnabled, linkedSigPrefix, solar_system_name, temporary_name]);
const nodeVars: SolarSystemNodeVars = {
const systemName = useMemo(() => {
if (isTempSystemNameEnabled && temporaryName) {
return temporaryName;
}
return solar_system_name;
}, [isTempSystemNameEnabled, solar_system_name, temporaryName]);
const customName = (isTempSystemNameEnabled && temporaryName && name) || (solar_system_name !== name && name) || null;
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
systemSignatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
})),
);
}, [isShowUnsplashedSignatures, systemSignatures]);
const nodeVars = {
id,
selected,
visible,
isWormhole,
classTitleColor,
killsCount,
killsActivityType,
hasUserCharacters,
userCharacters,
showHandlers,
regionClass,
systemName,
@@ -176,10 +194,12 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
dbClick,
sortedStatics,
effectName: effect_name,
solarSystemId: solar_system_id.toString(),
regionName: region_name,
solarSystemId: solar_system_id,
solarSystemName: solar_system_name,
locked,
hubs: hubsAsStrings,
name,
hubs,
name: name,
isConnecting,
hoverNodeId,
charactersInSystem,
@@ -187,9 +207,7 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarS
unsplashedRight,
isThickConnections,
classTitle: class_title,
temporaryName: computedTemporaryName,
regionName: region_name,
solarSystemName: solar_system_name,
temporaryName: temporary_name,
};
return nodeVars;
@@ -212,22 +230,24 @@ export interface SolarSystemNodeVars {
isShattered: boolean;
tag?: string | null;
status?: number;
labelsInfo: LabelInfo[];
dbClick: (event: React.MouseEvent<HTMLDivElement>) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
labelsInfo: Array<any>;
dbClick: (event?: void) => void;
sortedStatics: Array<string | number>;
effectName: string | null;
regionName: string | null;
solarSystemId: string;
solarSystemId: number;
solarSystemName: string | null;
locked: boolean;
hubs: string[];
hubs: string[] | number[];
name: string | null;
isConnecting: boolean;
hoverNodeId: string | null;
charactersInSystem: Array<CharacterTypeRaw>;
userCharacters: string[];
unsplashedLeft: Array<SystemSignature>;
unsplashedRight: Array<SystemSignature>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
unsplashedLeft: Array<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
unsplashedRight: Array<any>;
isThickConnections: boolean;
classTitle: string | null;
temporaryName?: string | null;

View File

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

View File

@@ -1,30 +0,0 @@
import { useMemo } from 'react';
import { SystemSignature } from '@/hooks/Mapper/types';
import { prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
export type UnsplashedSignatureType = SystemSignature & { sig_id: string };
export function useUnsplashedSignatures(systemSigs: SystemSignature[], isShowUnsplashedSignatures: boolean) {
return useMemo(() => {
if (!isShowUnsplashedSignatures) {
return {
unsplashedLeft: [] as SystemSignature[],
unsplashedRight: [] as SystemSignature[],
};
}
const chunks = prepareUnsplashedChunks(
systemSigs
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
kind: s.kind,
name: s.name,
group: s.group,
})) as UnsplashedSignatureType[],
);
const [unsplashedLeft, unsplashedRight] = chunks;
return { unsplashedLeft, unsplashedRight };
}, [isShowUnsplashedSignatures, systemSigs]);
}

View File

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

View File

@@ -11,8 +11,7 @@
--rf-tag-color: #38BDF8;
--rf-region-name: #D6D3D1;
--rf-custom-name: #93C5FD;
--rf-node-font-family: 'Shentox', 'Rogan', sans-serif !important;
--rf-node-font-weight: 500;
--rf-bg-variant: "dots";
--rf-bg-gap: 15;
@@ -29,8 +28,4 @@
--tooltip-bg: #202020;
--window-corner: #72716f;
--rf-local-counter-font-weight: 500;
--rf-node-local-counter: inherit;
--rf-has-user-characters: #ffc75d;
}

View File

@@ -1,19 +1,19 @@
$friendlyBase: #3bbd39;
$friendlyAlpha: #3bbd3952;
$friendlyBase: #3bbd39;
$friendlyAlpha: #3bbd3952;
$friendlyDark20: darken($friendlyBase, 20%);
$friendlyDark30: darken($friendlyBase, 30%);
$friendlyDark5: darken($friendlyBase, 5%);
$lookingForBase: #43c2fd;
$lookingForBase: #43c2fd;
$lookingForAlpha: rgba(67, 176, 253, 0.48);
$lookingForDark15: darken($lookingForBase, 15%);
$homeBase: rgb(179, 253, 67);
$homeAlpha: rgba(186, 248, 48, 0.32);
$homeBackground: #a0fa5636;
$homeBase: rgb(197, 253, 67);
$homeAlpha: rgba(197, 253, 67, 0.32);
$homeDark30: darken($homeBase, 30%);
:root {
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
@@ -98,7 +98,6 @@ $homeDark30: darken($homeBase, 30%);
--eve-solar-system-status-color-unknown: transparent;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
--eve-solar-system-status-color-background: #{$homeBackground};
--eve-solar-system-status-color-home-dark30: #{$homeDark30};
--eve-solar-system-status-friendly: #{$friendlyAlpha};
--eve-solar-system-status-color-friendly: #{$friendlyBase};

View File

@@ -542,32 +542,48 @@
background-color: #d10600;
}
.react-flow {
color: var(--text-color);
.react-flow__minimap-node {
fill: #ffb03a;
}
&__pane {
cursor: auto;
}
.react-flow__minimap {
border: 1px solid #282828;
border-radius: 4px;
background-color: rgb(47 37 37) !important;
overflow: hidden;
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
.react-flow__minimap-mask {
stroke-width: 2px;
fill: rgba(0, 0, 0, 0.5);
mix-blend-mode: overlay;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
.react-flow__minimap-mask {
stroke-width: 2px;
fill: rgb(0 0 0 / 50%) !important;
mix-blend-mode: inherit;
opacity: 1;
stroke: #fff;
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}
.p-dialog {
.p-dialog-header {
height: 40px;
padding: 1rem;
padding-right: 10px !important;
}
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -2,31 +2,24 @@
@import './eve-common';
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
$homeBase: rgb(197, 253, 67);
$homeAlpha: rgba(197, 253, 67, 0.32);
$homeDark30: darken($homeBase, 30%);
.pathfinder-theme {
/* -- Override values from the default theme -- */
--rf-bg-color: #000000;
--rf-soft-bg-color: #282828;
--rf-node-bg-color: #202020;
--rf-node-soft-bg-color: #313335;
--rf-node-font-weight: bold;
--rf-text-color: #adadad;
--rf-region-name: var(--rf-text-color);
--rf-custom-name: var(--rf-text-color);
--tooltip-bg: #202020;
--rf-bg-variant: "lines";
--rf-bg-gap: 34;
--rf-snap-size: 17;
--rf-bg-gap: 32;
--rf-bg-size: 1;
--rf-bg-pattern-color: #313131;
--rf-local-counter-font-weight: 700;
/* Additional node-specific overrides */
--rf-node-line-height: normal;
--rf-node-font-family: 'Oxygen', sans-serif;
--rf-tag-color: #fbbf24;
/* -- theme-specific variables -- */
--eve-effect-pulsar: #428bca;
--eve-effect-magnetar: #e06fdf;
--eve-effect-wolfRayet: #e28a0d;
@@ -46,9 +39,13 @@ $homeDark30: darken($homeBase, 30%);
--eve-wh-type-color-c13: #7986cb;
--eve-wh-type-color-drifter: #44aa82;
--rf-node-local-counter: #5cb85c;
--rf-has-user-characters: #ffc75d;
--rf-node-font-weight: bold;
--rf-node-line-height: normal;
--rf-node-font-family: 'Oxygen', sans-serif;
--rf-node-text-color: var(--pf-text-color);
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
--rf-tag-color: #fbbf24;
--rf-has-user-characters: #5cb85c;
--window-corner: #72716f;
}

View File

@@ -1,3 +1,5 @@
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import { useMemo } from 'react';
import { WindowManager } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { DEFAULT_WIDGETS } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
@@ -19,12 +21,5 @@ export const MapInterface = () => {
.filter(x => windowsSettings.visible.some(j => x.id === j));
}, [windowsSettings]);
return (
<WindowManager
windows={items}
viewPort={windowsSettings.viewPort}
dragSelector=".react-grid-dragHandleExample"
onChange={updateWidgetSettings}
/>
);
return <WindowManager windows={items} dragSelector=".react-grid-dragHandleExample" onChange={updateWidgetSettings} />;
};

View File

@@ -1,52 +0,0 @@
import { MarkdownComment } from '@/hooks/Mapper/components/mapInterface/components/Comments/components';
import { useEffect, useRef, useState } from 'react';
import { CommentType } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export interface CommentsProps {}
// eslint-disable-next-line no-empty-pattern
export const Comments = ({}: CommentsProps) => {
const [commentsList, setCommentsList] = useState<CommentType[]>([]);
const {
data: { selectedSystems },
comments: { loadComments, comments, lastUpdateKey },
} = useMapRootState();
const [systemId] = selectedSystems;
const ref = useRef({ loadComments, systemId });
ref.current = { loadComments, systemId };
useEffect(() => {
const commentsBySystem = comments.get(systemId);
if (!commentsBySystem) {
return;
}
const els = [...commentsBySystem.comments].sort((a, b) => +new Date(b.updated_at) - +new Date(a.updated_at));
setCommentsList(els);
}, [systemId, lastUpdateKey, comments]);
useEffect(() => {
ref.current.loadComments(systemId);
}, [systemId]);
if (commentsList.length === 0) {
return (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
Not comments found here
</div>
);
}
return (
<div className="flex flex-col gap-1 whitespace-nowrap overflow-auto text-ellipsis custom-scrollbar">
{commentsList.map(({ id, text, updated_at, characterEveId }) => (
<MarkdownComment key={id} text={text} time={updated_at} characterEveId={characterEveId} id={id} />
))}
</div>
);
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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