mirror of
				https://github.com/wanderer-industries/wanderer
				synced 2025-10-31 22:47:07 +00:00 
			
		
		
		
	Compare commits
	
		
			107 Commits
		
	
	
		
			refactorin
			...
			map-events
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b1f1098df6 | ||
|   | ed5d824c0a | ||
|   | d8e4631981 | ||
|   | 7091341b4b | ||
|   | 8795ce6af3 | ||
|   | 239b34d15a | ||
|   | 729a5ad1a9 | ||
|   | 8febc2476b | ||
|   | 84b1317927 | ||
|   | bfb504e5db | ||
|   | 9975deacfb | ||
|   | f77f071003 | ||
|   | 4a8d55e83d | ||
|   | 9a99f40e2a | ||
|   | 428fa8035c | ||
|   | 745f3dee17 | ||
|   | 9907cc1875 | ||
|   | 130cd780a2 | ||
|   | a808e5d1a5 | ||
|   | b926117e26 | ||
|   | fdf238accf | ||
|   | 4e1c27e8a3 | ||
|   | a3e51a0ac5 | ||
|   | d27bb0d54f | ||
|   | f6a750f06b | ||
|   | c4e2f63e69 | ||
|   | 675ffc8f42 | ||
|   | cdc4221175 | ||
|   | 843f3363fd | ||
|   | 17653a6374 | ||
|   | 7d860533a0 | ||
|   | 0c751b3ced | ||
|   | 80a522ab06 | ||
|   | 0718d76e00 | ||
|   | a4887c5358 | ||
|   | 2ad5e122ff | ||
|   | 832d874a0e | ||
|   | 6a133d4dbd | ||
|   | d96dfa63c9 | ||
|   | d5c8c05426 | ||
|   | b6bb4647c7 | ||
|   | a81f06b743 | ||
|   | cb41a33546 | ||
|   | 005068de9c | ||
|   | d8c7b1e070 | ||
|   | 4835dfcc42 | ||
|   | 15bceb09a2 | ||
|   | 13e818abfd | ||
|   | 9c5f6049b5 | ||
|   | 2095b619a4 | ||
|   | df155cbc1b | ||
|   | 3781729fd1 | ||
|   | d03c634ec0 | ||
|   | 93c979c218 | ||
|   | 90fef94583 | ||
|   | 0b8eec2263 | ||
|   | 9511af4e6d | ||
|   | 7deaf1fd9f | ||
|   | 43cc5bd520 | ||
|   | 68b58aa520 | ||
|   | dbadd09af3 | ||
|   | fcbe2c754f | ||
|   | ad4580677b | ||
|   | 01a6cc7d92 | ||
|   | 95ce95a187 | ||
|   | ce8e6fbfb0 | ||
|   | a20eaed76b | ||
|   | 419af72028 | ||
|   | 8e499522f6 | ||
|   | 84321b847e | ||
|   | c969a4d465 | ||
|   | 0e12c850b6 | ||
|   | 442835dd9b | ||
|   | b4ff99cb2e | ||
|   | aa0ecbc998 | ||
|   | cc412e93c0 | ||
|   | 1d36fadbfa | ||
|   | 56182bd87d | ||
|   | d290ff92b3 | ||
|   | 298c5fd3b8 | ||
|   | e365c43781 | ||
|   | 23a9f22ef4 | ||
|   | 242f437237 | ||
|   | 2eae8cffdb | ||
|   | 68ab3d4f72 | ||
|   | 1ea805aff0 | ||
|   | 6ce45349dc | ||
|   | 8f20cd9863 | ||
|   | 4ed0e85680 | ||
|   | 8ce9eb9955 | ||
|   | 363330f3d1 | ||
|   | fbf9c5ddd6 | ||
|   | fbf2ee314c | ||
|   | c9f83fb419 | ||
|   | 9737d91e16 | ||
|   | 2f672ae970 | ||
|   | 25339546c6 | ||
|   | 912cad42ac | ||
|   | b3752c8d8f | ||
|   | e8a11333f2 | ||
|   | 8bb6d09e6e | ||
|   | 56bf955297 | ||
|   | ef6c08dfe8 | ||
|   | 495c3e1cd7 | ||
|   | 9a5fe3d744 | ||
|   | 72607cae4d | ||
|   | 4891cdb04d | 
							
								
								
									
										32
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -58,6 +58,8 @@ jobs: | ||||
|         otp: ["27"] | ||||
|         elixir: ["1.17"] | ||||
|         node-version: ["18.x"] | ||||
|     outputs: | ||||
|       commit_hash: ${{ steps.generate-changelog.outputs.commit_hash }} | ||||
|     steps: | ||||
|       - name: Prepare | ||||
|         run: | | ||||
| @@ -108,16 +110,17 @@ jobs: | ||||
|         run: mix compile | ||||
|  | ||||
|       - name: Generate Changelog & Update Tag Version | ||||
|         id: generate-changelog | ||||
|         run: | | ||||
|           git config --global user.name 'CI' | ||||
|           git config --global user.email 'ci@users.noreply.github.com' | ||||
|           mix git_ops.release --force-patch --yes | ||||
|           git push --follow-tags | ||||
|           echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | ||||
|  | ||||
|   docker: | ||||
|     name: 🛠 Build Docker Images | ||||
|     needs: | ||||
|       - build | ||||
|     needs: build | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       checks: write | ||||
| @@ -141,6 +144,7 @@ jobs: | ||||
|       - name: ⬇️ Checkout repo | ||||
|         uses: actions/checkout@v3 | ||||
|         with: | ||||
|           ref: ${{ needs.build.outputs.commit_hash }} | ||||
|           fetch-depth: 0 | ||||
|  | ||||
|       - name: Prepare Changelog | ||||
| @@ -189,6 +193,30 @@ jobs: | ||||
|       - name: Image digest | ||||
|         run: echo ${{ steps.build.outputs.digest }} | ||||
|  | ||||
|       - uses: markpatterson27/markdown-to-output@v1 | ||||
|         id: extract-changelog | ||||
|         with: | ||||
|           filepath: CHANGELOG.md | ||||
|  | ||||
|       - name: Get content | ||||
|         uses: 2428392/gh-truncate-string-action@v1.3.0 | ||||
|         id: get-content | ||||
|         with: | ||||
|           stringToTruncate: | | ||||
|             📣 Wanderer new release available 🎉 | ||||
|  | ||||
|             **Version**: ${{ steps.get-latest-tag.outputs.tag }} | ||||
|  | ||||
|             ${{ steps.extract-changelog.outputs.body }} | ||||
|           maxLength: 500 | ||||
|           truncationSymbol: "…" | ||||
|  | ||||
|       - name: Discord Webhook Action | ||||
|         uses: tsickert/discord-webhook@v5.3.0 | ||||
|         with: | ||||
|           webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} | ||||
|           content: ${{ steps.get-content.outputs.string }} | ||||
|  | ||||
|   create-release: | ||||
|     name: 🏷 Create Release | ||||
|     runs-on: ubuntu-22.04 | ||||
|   | ||||
							
								
								
									
										114
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,12 +1,87 @@ | ||||
| # Change Log | ||||
|  | ||||
| <!-- changelog --> | ||||
|  | ||||
| ## [v1.3.1](https://github.com/wanderer-industries/wanderer/compare/v1.3.0...v1.3.1) (2024-10-07) | ||||
| ## [v1.13.0](https://github.com/wanderer-industries/wanderer/compare/v1.12.11...v1.13.0) (2024-10-28) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Features: | ||||
|  | ||||
| * Core: Use ESI /characters/affiliation API | ||||
|  | ||||
| ## [v1.12.4](https://github.com/wanderer-industries/wanderer/compare/v1.12.3...v1.12.4) (2024-10-21) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Bug Fixes: | ||||
|  | ||||
| * Map: Fix systems cleanup | ||||
|  | ||||
| ## [v1.12.3](https://github.com/wanderer-industries/wanderer/compare/v1.12.2...v1.12.3) (2024-10-18) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Bug Fixes: | ||||
|  | ||||
| * Map: Fix regression issues | ||||
|  | ||||
| ## [v1.12.1](https://github.com/wanderer-industries/wanderer/compare/v1.12.0...v1.12.1) (2024-10-16) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Bug Fixes: | ||||
|  | ||||
| * Map: Fix system add error after map page refresh | ||||
|  | ||||
| ## [v1.12.0](https://github.com/wanderer-industries/wanderer/compare/v1.11.5...v1.12.0) (2024-10-16) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Features: | ||||
|  | ||||
| * Map: Prettify user settings | ||||
|  | ||||
| ## [v1.11.0](https://github.com/wanderer-industries/wanderer/compare/v1.10.0...v1.11.0) (2024-10-14) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Features: | ||||
|  | ||||
| * Map: Add map level option to store custom labels | ||||
|  | ||||
| ## [v1.10.0](https://github.com/wanderer-industries/wanderer/compare/v1.9.0...v1.10.0) (2024-10-13) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Features: | ||||
|  | ||||
| * Map: Link signature on splash | ||||
|  | ||||
| ## [v1.5.0](https://github.com/wanderer-industries/wanderer/compare/v1.4.0...v1.5.0) (2024-10-11) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Features: | ||||
|  | ||||
| * Map: Follow Character on Map and auto select their current system | ||||
|  | ||||
| ## [v1.3.6](https://github.com/wanderer-industries/wanderer/compare/v1.3.5...v1.3.6) (2024-10-09) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Bug Fixes: | ||||
|  | ||||
| * Signatures: Signatures update fixes | ||||
|  | ||||
| ## [v1.3.0](https://github.com/wanderer-industries/wanderer/compare/v1.2.10...v1.3.0) (2024-10-07) | ||||
|  | ||||
|  | ||||
| @@ -20,26 +95,6 @@ | ||||
|  | ||||
| * Map: Revision of sorting from also adding ability to sort all columns | ||||
|  | ||||
| ## [v1.2.10](https://github.com/wanderer-industries/wanderer/compare/v1.2.9...v1.2.10) (2024-10-07) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [v1.2.9](https://github.com/wanderer-industries/wanderer/compare/v1.2.8...v1.2.9) (2024-10-07) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [v1.2.8](https://github.com/wanderer-industries/wanderer/compare/v1.2.7...v1.2.8) (2024-10-06) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [v1.2.7](https://github.com/wanderer-industries/wanderer/compare/v1.2.6...v1.2.7) (2024-10-05) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [v1.2.6](https://github.com/wanderer-industries/wanderer/compare/v1.2.5...v1.2.6) (2024-10-05) | ||||
|  | ||||
|  | ||||
| @@ -76,11 +131,6 @@ | ||||
|  | ||||
| * Map: Fix map loading after select a different map. | ||||
|  | ||||
| ## [v1.2.2](https://github.com/wanderer-industries/wanderer/compare/v1.2.1...v1.2.2) (2024-10-02) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [v1.2.1](https://github.com/wanderer-industries/wanderer/compare/v1.2.0...v1.2.1) (2024-10-02) | ||||
|  | ||||
|  | ||||
| @@ -197,20 +247,10 @@ | ||||
|  | ||||
| * docker: Fix DB connection in docker-compose internal network | ||||
|  | ||||
| ## [v1.0.7](https://github.com/wanderer-industries/wanderer/compare/v1.0.6...v1.0.7) (2024-09-19) | ||||
|  | ||||
| ## [v1.0.6](https://github.com/wanderer-industries/wanderer/compare/v1.0.5...v1.0.6) (2024-09-18) | ||||
|  | ||||
| ## [v1.0.5](https://github.com/wanderer-industries/wanderer/compare/v1.0.4...v1.0.5) (2024-09-18) | ||||
|  | ||||
| ## [v1.0.4](https://github.com/wanderer-industries/wanderer/compare/v1.0.3...v1.0.4) (2024-09-18) | ||||
|  | ||||
| ### Bug Fixes | ||||
|  | ||||
| * core: skip search results for failed character info request | ||||
|  | ||||
| ## [v1.0.3](https://github.com/wanderer-industries/wanderer/compare/v1.0.2...v1.0.3) (2024-09-18) | ||||
|  | ||||
| ## [v1.0.2](https://github.com/wanderer-industries/wanderer/compare/v1.0.1...v1.0.2) (2024-09-18) | ||||
|  | ||||
| ## [v1.0.1](https://github.com/wanderer-industries/wanderer/compare/v1.0.0...v1.0.1) (2024-09-18) | ||||
|   | ||||
| @@ -28,6 +28,12 @@ body { | ||||
|   font-weight: 500; | ||||
| } | ||||
|  | ||||
| #bg-canvas { | ||||
|   position: absolute; | ||||
|   width: 100vw; | ||||
|   height: 100vh; | ||||
| } | ||||
|  | ||||
| .ccp-font { | ||||
|   font-family: 'Shentox', 'Rogan', sans-serif !important; | ||||
| } | ||||
|   | ||||
| @@ -85,3 +85,26 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| .p-dropdown-label, .p-inputtext { | ||||
|   padding: 0.25rem 0.75rem; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| .p-dropdown-item { | ||||
|   padding: 0.25rem 0.5rem; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| .p-dropdown-item-group { | ||||
|   padding: 0.25rem 0.75rem; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| .p-dropdown-trigger { | ||||
|   width: 14px; | ||||
|   margin: 0 12px; | ||||
| } | ||||
|  | ||||
| .p-dropdown-empty-message { | ||||
|   padding: 0.25rem 0.5rem; | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* Основной класс диалога */ | ||||
| .p-dialog { | ||||
| body .p-dialog { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   //position: absolute; | ||||
| @@ -7,11 +7,26 @@ | ||||
|   left: 0; | ||||
|   //visibility: hidden; | ||||
|   overflow: hidden; | ||||
|   border-radius: 6px; | ||||
|   border-radius: 2px; | ||||
|   box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2); | ||||
|   transition: box-shadow 0.3s; | ||||
|   background: #fff; | ||||
|   z-index: 1000; | ||||
|   border: 1px solid #212121; | ||||
|   background: var(--surface-h); | ||||
|   color: var(--text-color); | ||||
|  | ||||
|   .p-dialog-header { | ||||
|     background: #171717 !important; | ||||
|     color: var(--text-color); | ||||
|  | ||||
|     .p-dialog-header-icon:focus-visible { | ||||
|       box-shadow: none !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .p-dialog-footer { | ||||
|     border-top: 1px solid var(--surface-d); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* Стиль видимого диалога */ | ||||
| @@ -45,12 +60,12 @@ | ||||
|   justify-content: space-between; | ||||
|   padding: 1rem; | ||||
|   background: #f4f4f4; | ||||
|   border-bottom: 1px solid #ddd; | ||||
|   //border-bottom: 1px solid #ddd; | ||||
| } | ||||
|  | ||||
| /* Содержимое диалога */ | ||||
| .p-dialog-content { | ||||
|   padding: 1rem; | ||||
|   padding: 0.5rem; | ||||
|   overflow-y: auto; | ||||
|   flex: 1; | ||||
| } | ||||
| @@ -78,23 +93,3 @@ | ||||
| .p-dialog-header-close .pi { | ||||
|   font-size: 1.25rem; | ||||
| } | ||||
|  | ||||
| /* Тема Saga Blue (пример) */ | ||||
| body .p-dialog { | ||||
|   background: var(--surface-a); | ||||
|   color: var(--text-color); | ||||
| } | ||||
|  | ||||
| body .p-dialog .p-dialog-header, | ||||
| body .p-dialog .p-dialog-footer { | ||||
|   background: var(--surface-b); | ||||
|   color: var(--text-color); | ||||
| } | ||||
|  | ||||
| body .p-dialog .p-dialog-header { | ||||
|   border-bottom: 1px solid var(--surface-d); | ||||
| } | ||||
|  | ||||
| body .p-dialog .p-dialog-footer { | ||||
|   border-top: 1px solid var(--surface-d); | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
|   --surface-d: #3f4b5b; | ||||
|   --surface-e: #2a323d; | ||||
|   --surface-f: #2a323d; | ||||
|   --surface-h: #171717; | ||||
|   --text-color: rgba(255, 255, 255, 0.87); | ||||
|   --text-color-secondary: rgba(255, 255, 255, 0.6); | ||||
|   --primary-color: #8dd0ff; | ||||
|   | ||||
| @@ -11,7 +11,7 @@ const Characters = ({ data }: { data: CharacterTypeRaw[] }) => { | ||||
|  | ||||
|   const handleSelect = useCallback( | ||||
|     (character: CharacterTypeRaw) => { | ||||
|       mapRef.current?.command(Commands.selectSystem, character?.location?.solar_system_id?.toString()); | ||||
|       mapRef.current?.command(Commands.centerSystem, character?.location?.solar_system_id?.toString()); | ||||
|     }, | ||||
|     [mapRef], | ||||
|   ); | ||||
|   | ||||
| @@ -46,7 +46,7 @@ export const useLabelsMenu = ( | ||||
|     } | ||||
|  | ||||
|     // const labels = getLabels(system.labels); | ||||
|     const hasLabels = labels.list.length > 0; | ||||
|     const hasLabels = labels?.list?.length > 0; | ||||
|     const statusList = hasLabels ? LABELS_ORDER : LABELS_ORDER.slice(1); | ||||
|  | ||||
|     return [ | ||||
|   | ||||
| @@ -18,7 +18,7 @@ export const useTagMenu = ( | ||||
|   ref.current = { onSystemTag, systems, systemId }; | ||||
|  | ||||
|   return useCallback(() => { | ||||
|     const { onSystemTag, systemId , systems} = ref.current; | ||||
|     const { onSystemTag, systemId, systems } = ref.current; | ||||
|     const system = systemId ? getSystemById(systems, systemId) : undefined; | ||||
|  | ||||
|     const isSelectedLetters = AVAILABLE_LETTERS.includes(system?.tag ?? ''); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers. | ||||
| import { SolarSystemRawType } from '@/hooks/Mapper/types'; | ||||
| import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts'; | ||||
| import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts'; | ||||
| import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks'; | ||||
|  | ||||
| interface UseContextMenuSystemHandlersProps { | ||||
|   hubs: string[]; | ||||
| @@ -16,8 +17,10 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC | ||||
|  | ||||
|   const [system, setSystem] = useState<string>(); | ||||
|  | ||||
|   const ref = useRef({ hubs, system, systems, outCommand }); | ||||
|   ref.current = { hubs, system, systems, outCommand }; | ||||
|   const { deleteSystems } = useDeleteSystems(); | ||||
|  | ||||
|   const ref = useRef({ hubs, system, systems, outCommand, deleteSystems }); | ||||
|   ref.current = { hubs, system, systems, outCommand, deleteSystems }; | ||||
|  | ||||
|   const open = useCallback((ev: any, systemId: string) => { | ||||
|     setSystem(systemId); | ||||
| @@ -27,12 +30,12 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC | ||||
|   }, []); | ||||
|  | ||||
|   const onDeleteSystem = useCallback(() => { | ||||
|     const { system, outCommand } = ref.current; | ||||
|     const { system, deleteSystems } = ref.current; | ||||
|     if (!system) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     outCommand({ type: OutCommand.deleteSystems, data: [system] }); | ||||
|     deleteSystems([system]); | ||||
|     setSystem(undefined); | ||||
|   }, []); | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import { getSystemById } from '@/hooks/Mapper/helpers'; | ||||
| import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks'; | ||||
| import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts'; | ||||
| import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components'; | ||||
| import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks/useJumpPlannerMenu'; | ||||
| import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks'; | ||||
| import { Route } from '@/hooks/Mapper/types/routes.ts'; | ||||
|  | ||||
| export interface ContextMenuSystemInfoProps { | ||||
|   | ||||
| @@ -48,19 +48,19 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: U | ||||
|   }, []); | ||||
|  | ||||
|   const onAddSystem = useCallback(() => { | ||||
|     const { system, outCommand, mapRef } = ref.current; | ||||
|     if (!system) { | ||||
|     const { system: solarSystemId, outCommand, mapRef } = ref.current; | ||||
|     if (!solarSystemId) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     outCommand({ | ||||
|       type: OutCommand.addSystem, | ||||
|       data: { | ||||
|         system_id: system, | ||||
|         system_id: solarSystemId, | ||||
|       }, | ||||
|     }); | ||||
|     setTimeout(() => { | ||||
|       mapRef.current?.command(Commands.selectSystem, system); | ||||
|       mapRef.current?.command(Commands.centerSystem, solarSystemId); | ||||
|       setSystem(undefined); | ||||
|     }, 200); | ||||
|   }, []); | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| import { Node } from 'reactflow'; | ||||
| import { useRef, useState } from 'react'; | ||||
| import { useCallback, useRef, useState } from 'react'; | ||||
| import { ContextMenu } from 'primereact/contextmenu'; | ||||
| import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { SolarSystemRawType } from '@/hooks/Mapper/types'; | ||||
| import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts'; | ||||
| import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks'; | ||||
|  | ||||
| export const useContextMenuSystemMultipleHandlers = () => { | ||||
|   const contextMenuRef = useRef<ContextMenu | null>(null); | ||||
|   const { outCommand } = useMapRootState(); | ||||
|   const [systems, setSystems] = useState<Node<SolarSystemRawType>[]>(); | ||||
|  | ||||
|   const { deleteSystems } = useDeleteSystems(); | ||||
|  | ||||
|   const handleSystemMultipleContext: NodeSelectionMouseHandler = (ev, systems_) => { | ||||
|     setSystems(systems_); | ||||
|     ev.preventDefault(); | ||||
| @@ -19,7 +19,7 @@ export const useContextMenuSystemMultipleHandlers = () => { | ||||
|     contextMenuRef.current?.show(ev); | ||||
|   }; | ||||
|  | ||||
|   const onDeleteSystems = () => { | ||||
|   const onDeleteSystems = useCallback(() => { | ||||
|     if (!systems) { | ||||
|       return; | ||||
|     } | ||||
| @@ -29,12 +29,11 @@ export const useContextMenuSystemMultipleHandlers = () => { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     outCommand({ type: OutCommand.deleteSystems, data: sysToDel }); | ||||
|   }; | ||||
|     deleteSystems(sysToDel); | ||||
|   }, [deleteSystems, systems]); | ||||
|  | ||||
|   return { | ||||
|     handleSystemMultipleContext, | ||||
|  | ||||
|     contextMenuRef, | ||||
|     onDeleteSystems, | ||||
|   }; | ||||
|   | ||||
| @@ -1 +1,3 @@ | ||||
| export * from './useWaypointMenu'; | ||||
| export * from './useJumpPlannerMenu'; | ||||
| export * from './useDeleteSystems'; | ||||
|   | ||||
| @@ -0,0 +1,18 @@ | ||||
| import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
|  | ||||
| export const useDeleteSystems = () => { | ||||
|   const { outCommand } = useMapRootState(); | ||||
|  | ||||
|   const deleteSystems = (systemIds: string[]) => { | ||||
|     if (!systemIds || !systemIds.length) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     outCommand({ type: OutCommand.deleteSystems, data: systemIds }); | ||||
|   }; | ||||
|  | ||||
|   return { | ||||
|     deleteSystems, | ||||
|   }; | ||||
| }; | ||||
							
								
								
									
										2
									
								
								assets/js/hooks/Mapper/components/hooks/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								assets/js/hooks/Mapper/components/hooks/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| export * from './useSystemInfo'; | ||||
| export * from './useGetOwnOnlineCharacters'; | ||||
							
								
								
									
										33
									
								
								assets/js/hooks/Mapper/components/hooks/useSystemInfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								assets/js/hooks/Mapper/components/hooks/useSystemInfo.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { useMemo } from 'react'; | ||||
| import { getSystemById } from '@/hooks/Mapper/helpers'; | ||||
| import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts'; | ||||
|  | ||||
| interface UseSystemInfoProps { | ||||
|   systemId: string; | ||||
| } | ||||
|  | ||||
| export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => { | ||||
|   const { | ||||
|     data: { systems, connections }, | ||||
|   } = useMapRootState(); | ||||
|  | ||||
|   const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] }); | ||||
|  | ||||
|   return useMemo(() => { | ||||
|     const staticInfo = systemStatics.get(parseInt(systemId)); | ||||
|     const dynamicInfo = getSystemById(systems, systemId); | ||||
|  | ||||
|     if (!staticInfo || !dynamicInfo) { | ||||
|       throw new Error(`Error on getting system ${systemId}`); | ||||
|     } | ||||
|  | ||||
|     const leadsTo = connections | ||||
|       .filter(x => [x.source, x.target].includes(systemId)) | ||||
|       .map(x => [x.source, x.target]) | ||||
|       .flat() | ||||
|       .filter(x => x !== systemId); | ||||
|  | ||||
|     return { dynamicInfo, staticInfo, leadsTo }; | ||||
|   }, [systemStatics, systemId, systems, connections]); | ||||
| }; | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect } from 'react'; | ||||
| import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useRef } from 'react'; | ||||
| import ReactFlow, { | ||||
|   Background, | ||||
|   ConnectionMode, | ||||
| @@ -13,12 +13,15 @@ import ReactFlow, { | ||||
|   SelectionMode, | ||||
|   useEdgesState, | ||||
|   useNodesState, | ||||
|   NodeChange, | ||||
|   useReactFlow, | ||||
| } from 'reactflow'; | ||||
| import 'reactflow/dist/style.css'; | ||||
| import classes from './Map.module.scss'; | ||||
| import './styles/neon-theme.scss'; | ||||
| import './styles/eve-common.scss'; | ||||
| import { MapProvider, useMapState } from './MapProvider'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { useMapHandlers, useUpdateNodes } from './hooks'; | ||||
| import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { | ||||
| @@ -34,6 +37,7 @@ import { SESSION_KEY } from '@/hooks/Mapper/constants.ts'; | ||||
| import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types'; | ||||
| import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts'; | ||||
| import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts'; | ||||
| import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks'; | ||||
|  | ||||
| const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 }; | ||||
|  | ||||
| @@ -108,6 +112,7 @@ const MapComp = ({ | ||||
|   isShowMinimap, | ||||
|   showKSpaceBG, | ||||
| }: MapCompProps) => { | ||||
|   const { getNode } = useReactFlow(); | ||||
|   const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes); | ||||
|   const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges); | ||||
|  | ||||
| @@ -115,8 +120,15 @@ const MapComp = ({ | ||||
|   useUpdateNodes(nodes); | ||||
|   const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers(); | ||||
|   const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers(); | ||||
|  | ||||
|   const { update } = useMapState(); | ||||
|   const { | ||||
|     data: { systems }, | ||||
|   } = useMapRootState(); | ||||
|  | ||||
|   const { deleteSystems } = useDeleteSystems(); | ||||
|  | ||||
|   const systemsRef = useRef({ systems }); | ||||
|   systemsRef.current = { systems }; | ||||
|  | ||||
|   const onConnect: OnConnect = useCallback( | ||||
|     params => { | ||||
| @@ -171,6 +183,32 @@ const MapComp = ({ | ||||
|     localStorage.setItem(SESSION_KEY.viewPort, JSON.stringify(viewport)); | ||||
|   }; | ||||
|  | ||||
|   const handleNodesChange = useCallback( | ||||
|     (changes: NodeChange[]) => { | ||||
|       const systemsIdsToRemove: string[] = []; | ||||
|       const nextChanges = changes.reduce((acc, change) => { | ||||
|         if (change.type === 'remove') { | ||||
|           const node = getNode(change.id); | ||||
|           const { systems = [] } = systemsRef.current; | ||||
|           if (node?.data?.id && !systems.map(s => s.id).includes(node?.data?.id)) { | ||||
|             return [...acc, change]; | ||||
|           } else if (!node?.data?.locked) { | ||||
|             systemsIdsToRemove.push(node?.data?.id); | ||||
|           } | ||||
|           return acc; | ||||
|         } | ||||
|         return [...acc, change]; | ||||
|       }, [] as NodeChange[]); | ||||
|  | ||||
|       if (systemsIdsToRemove.length) { | ||||
|         deleteSystems(systemsIdsToRemove); | ||||
|       } | ||||
|  | ||||
|       onNodesChange(nextChanges); | ||||
|     }, | ||||
|     [deleteSystems, getNode, onNodesChange], | ||||
|   ); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     update(x => ({ | ||||
|       ...x, | ||||
| @@ -184,7 +222,7 @@ const MapComp = ({ | ||||
|         <ReactFlow | ||||
|           nodes={nodes} | ||||
|           edges={edges} | ||||
|           onNodesChange={onNodesChange} | ||||
|           onNodesChange={handleNodesChange} | ||||
|           onEdgesChange={onEdgesChange} | ||||
|           onConnect={onConnect} | ||||
|           // TODO we need save into session all of this | ||||
| @@ -219,10 +257,10 @@ const MapComp = ({ | ||||
|           minZoom={0.2} | ||||
|           maxZoom={1.5} | ||||
|           elevateNodesOnSelect | ||||
|           deleteKeyCode={['Delete']} | ||||
|           // TODO need create clear example with problem with that flag | ||||
|           //  if system is not visible edge not drawing (and any render in Custom node is not happening) | ||||
|           // onlyRenderVisibleElements | ||||
|           deleteKeyCode={null} | ||||
|           selectionMode={SelectionMode.Partial} | ||||
|         > | ||||
|           {isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />} | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| @import "@/hooks/Mapper/components/map/styles/eve-common-variables"; | ||||
| @import '@/hooks/Mapper/components/map/styles/eve-common-variables'; | ||||
|  | ||||
| $pastel-blue: #5a7d9a; | ||||
| $pastel-pink: #d291bc; | ||||
| @@ -25,9 +25,11 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|   z-index: 1; | ||||
|   overflow: hidden; | ||||
|  | ||||
|  | ||||
|   &.Mataria, &.Amarria, &.Gallente, &.Caldaria { | ||||
|     &::Before { | ||||
|   &.Mataria, | ||||
|   &.Amarria, | ||||
|   &.Gallente, | ||||
|   &.Caldaria { | ||||
|     &::before { | ||||
|       content: ''; | ||||
|       position: absolute; | ||||
|       top: 0; | ||||
| @@ -44,42 +46,40 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|  | ||||
|   &.Mataria { | ||||
|     &::before { | ||||
|       background-image: url("/images/mataria.png"); | ||||
|       background-image: url('/images/mataria-180.png'); | ||||
|       opacity: 0.6; | ||||
|       background-position-x: -28px; | ||||
|       background-position-y: -3px; | ||||
|       background-position-x: 1px; | ||||
|       background-position-y: -14px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.Caldaria { | ||||
|     &::before { | ||||
|       background-image: url("/images/caldaria.png"); | ||||
|       background-image: url('/images/caldaria-180.png'); | ||||
|       opacity: 0.6; | ||||
|       background-position-x: -16px; | ||||
|       background-position-y: -17px; | ||||
|       background-position-x: 1px; | ||||
|       background-position-y: -10px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.Amarria { | ||||
|     &::before { | ||||
|       opacity: 0.45; | ||||
|       background-image: url("/images/amarr.png"); | ||||
|       background-position-x: 0px; | ||||
|       background-position-y: -1px; | ||||
|       width: calc(100% + 10px) | ||||
|       background-image: url('/images/amarr-180.png'); | ||||
|       background-position-x: 0; | ||||
|       background-position-y: -13px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.Gallente { | ||||
|     &::before { | ||||
|       opacity: 0.6; | ||||
|       background-image: url("/images/gallente.png"); | ||||
|       background-position-x: -1px; | ||||
|       background-position-y: -10px; | ||||
|       opacity: 0.5; | ||||
|       background-image: url('/images/gallente-180.png'); | ||||
|       background-position-x: 1px; | ||||
|       background-position-y: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   &.selected { | ||||
|     border-color: $pastel-pink; | ||||
|     box-shadow: 0 0 10px #9a1af1c2; | ||||
| @@ -95,7 +95,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|  | ||||
|   &.eve-system-status-home { | ||||
|     border: 1px solid darken($eve-solar-system-status-color-home, 30%); | ||||
|     background-image: linear-gradient(45deg, $eve-solar-system-status-friendly, transparent); | ||||
|     background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent); | ||||
|  | ||||
|     &.selected { | ||||
|       border-color: $eve-solar-system-status-color-home; | ||||
| @@ -104,7 +104,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|  | ||||
|   &.eve-system-status-friendly { | ||||
|     border: 1px solid darken($eve-solar-system-status-color-friendly, 20%); | ||||
|     background-image: linear-gradient(45deg, darken($eve-solar-system-status-friendly, 30%), transparent); | ||||
|     background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent); | ||||
|  | ||||
|     &.selected { | ||||
|       border-color: darken($eve-solar-system-status-color-friendly, 5%); | ||||
| @@ -113,7 +113,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|  | ||||
|   &.eve-system-status-lookingFor { | ||||
|     border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%); | ||||
|     background-image: linear-gradient(45deg, #45ff8f2f, #457fff2f); | ||||
|     background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f); | ||||
|  | ||||
|     &.selected { | ||||
|       border-color: $pastel-pink; | ||||
| @@ -121,17 +121,16 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|   } | ||||
|  | ||||
|   &.eve-system-status-warning { | ||||
|     background-image: linear-gradient(45deg, $eve-solar-system-status-warning, transparent); | ||||
|     background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent); | ||||
|   } | ||||
|  | ||||
|   &.eve-system-status-dangerous { | ||||
|     background-image: linear-gradient(45deg, $eve-solar-system-status-dangerous, transparent); | ||||
|     background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent); | ||||
|   } | ||||
|  | ||||
|   &.eve-system-status-target { | ||||
|     background-image: linear-gradient(45deg, $eve-solar-system-status-target, transparent); | ||||
|     background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| .Bookmarks { | ||||
| @@ -158,7 +157,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|     //background-color: #833ca4; | ||||
|  | ||||
|     &:not(:first-child) { | ||||
|       box-shadow: inset 4px -3px 4px rgba(0, 0, 0, .3); | ||||
|       box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -181,7 +180,6 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|       font-size: 9px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| .icon { | ||||
| @@ -219,9 +217,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|   } | ||||
|  | ||||
|   .solarSystemName { | ||||
|  | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| .BottomRow { | ||||
| @@ -288,11 +284,19 @@ $tooltip-bg: #202020; // Темный фон для подсказок | ||||
|     border-color: $pastel-pink; | ||||
|   } | ||||
|  | ||||
|   &.HandleTop { top: -2px } | ||||
|   &.HandleTop { | ||||
|     top: -2px; | ||||
|   } | ||||
|  | ||||
|   &.HandleRight { right: -2px } | ||||
|   &.HandleRight { | ||||
|     right: -2px; | ||||
|   } | ||||
|  | ||||
|   &.HandleBottom { bottom: -2px } | ||||
|   &.HandleBottom { | ||||
|     bottom: -2px; | ||||
|   } | ||||
|  | ||||
|   &.HandleLeft { left: -2px } | ||||
|   &.HandleLeft { | ||||
|     left: -2px; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -29,7 +29,7 @@ const SpaceToClass: Record<string, string> = { | ||||
| }; | ||||
|  | ||||
| const sortedLabels = (labels: string[]) => { | ||||
|   if (labels === null) { | ||||
|   if (!labels) { | ||||
|     return []; | ||||
|   } | ||||
|  | ||||
| @@ -133,7 +133,7 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS | ||||
|         <div className={classes.Bookmarks}> | ||||
|           {labelCustom !== '' && ( | ||||
|             <div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}> | ||||
|               <div>{labelCustom}</div> | ||||
|               <span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span> | ||||
|             </div> | ||||
|           )} | ||||
|  | ||||
| @@ -168,14 +168,16 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS | ||||
|         {visible && ( | ||||
|           <> | ||||
|             <div className={classes.HeadRow}> | ||||
|               <div className={clsx(classes.classTitle, classTitleColor)}>{class_title ?? '-'}</div> | ||||
|               <div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}> | ||||
|                 {class_title ?? '-'} | ||||
|               </div> | ||||
|               {tag != null && tag !== '' && ( | ||||
|                 <div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{tag}</div> | ||||
|               )} | ||||
|               <div | ||||
|                 className={clsx( | ||||
|                   classes.classSystemName, | ||||
|                   'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans', | ||||
|                   '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans', | ||||
|                 )} | ||||
|               > | ||||
|                 {solar_system_name} | ||||
| @@ -196,16 +198,16 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS | ||||
|  | ||||
|             <div className={clsx(classes.BottomRow, 'flex items-center justify-between')}> | ||||
|               {customName && ( | ||||
|                 <div className="text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">{customName}</div> | ||||
|                 <div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5"> | ||||
|                   {customName} | ||||
|                 </div> | ||||
|               )} | ||||
|  | ||||
|               {!isWormhole && !customName && ( | ||||
|                 <div | ||||
|                   className={clsx('text-stone-400 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5', { | ||||
|                     ['text-teal-100 font-bold']: space === Spaces.Caldari, | ||||
|                     ['text-yellow-100 font-bold']: space === Spaces.Amarr || space === Spaces.Matar, | ||||
|                     ['text-lime-200/80 font-bold']: space === Spaces.Gallente, | ||||
|                   })} | ||||
|                   className={clsx( | ||||
|                     '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5', | ||||
|                   )} | ||||
|                 > | ||||
|                   {region_name} | ||||
|                 </div> | ||||
| @@ -215,10 +217,10 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS | ||||
|  | ||||
|               <div className="flex items-center justify-end"> | ||||
|                 <div className="flex gap-1 items-center"> | ||||
|                   {locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem' }}></i>} | ||||
|                   {locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>} | ||||
|  | ||||
|                   {hubs.includes(solar_system_id.toString()) && ( | ||||
|                     <i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem' }}></i> | ||||
|                     <i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i> | ||||
|                   )} | ||||
|  | ||||
|                   {charactersInSystem.length > 0 && ( | ||||
|   | ||||
| @@ -18,5 +18,9 @@ export const WormholeClassComp = ({ id }: WormholeClassComp) => { | ||||
|   } | ||||
|  | ||||
|   const colorClass = WORMHOLE_CLASS_STYLES[wormholeDataAdditional.wormholeClassID.toString()]; | ||||
|   return <div className={clsx(colorClass)}>{wormholeDataAdditional.shortName}</div>; | ||||
|   return ( | ||||
|     <div className={clsx(colorClass, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}> | ||||
|       {wormholeDataAdditional.shortName} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -30,11 +30,77 @@ export enum SOLAR_SYSTEM_CLASS_IDS { | ||||
|   zarzakh = 10100, | ||||
| } | ||||
|  | ||||
| export enum SOLAR_SYSTEM_CLASS_GROUPS { | ||||
|   ccp = 'ccp', | ||||
|   c1 = 'c1', | ||||
|   c2 = 'c2', | ||||
|   c3 = 'c3', | ||||
|   c4 = 'c4', | ||||
|   c5 = 'c5', | ||||
|   c6 = 'c6', | ||||
|   hs = 'hs', | ||||
|   ls = 'ls', | ||||
|   ns = 'ns', | ||||
|   thera = 'thera', | ||||
|   c13 = 'c13', | ||||
|   drifter = 'drifter', | ||||
|   unknown = 'unknown', | ||||
|   pochven = 'pochven', | ||||
|   jovian = 'jovian', | ||||
| } | ||||
|  | ||||
| export const SOLAR_SYSTEM_TO_CLASS_GROUPS_CLASSES = { | ||||
|   c1: ['c1'], | ||||
|   c2: ['c2'], | ||||
|   c3: ['c3'], | ||||
|   c4: ['c4'], | ||||
|   c5: ['c5'], | ||||
|   c6: ['c6'], | ||||
|   hs: ['hs'], | ||||
|   ls: ['ls'], | ||||
|   ns: ['ns'], | ||||
|   thera: ['thera'], | ||||
|   c13: ['c13'], | ||||
|   pochven: ['pochven'], | ||||
|   drifter: ['sentinel', 'barbican', 'vidette', 'conflux', 'redoubt'], | ||||
|   jove: ['jove'], | ||||
| }; | ||||
|  | ||||
| export const SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS = { | ||||
|   ccp1: SOLAR_SYSTEM_CLASS_GROUPS.ccp, | ||||
|   c1: SOLAR_SYSTEM_CLASS_GROUPS.c1, | ||||
|   c2: SOLAR_SYSTEM_CLASS_GROUPS.c2, | ||||
|   c3: SOLAR_SYSTEM_CLASS_GROUPS.c3, | ||||
|   c4: SOLAR_SYSTEM_CLASS_GROUPS.c4, | ||||
|   c5: SOLAR_SYSTEM_CLASS_GROUPS.c5, | ||||
|   c6: SOLAR_SYSTEM_CLASS_GROUPS.c6, | ||||
|   hs: SOLAR_SYSTEM_CLASS_GROUPS.hs, | ||||
|   ls: SOLAR_SYSTEM_CLASS_GROUPS.ls, | ||||
|   ns: SOLAR_SYSTEM_CLASS_GROUPS.ns, | ||||
|   ccp2: SOLAR_SYSTEM_CLASS_GROUPS.ccp, | ||||
|   ccp3: SOLAR_SYSTEM_CLASS_GROUPS.ccp, | ||||
|   thera: SOLAR_SYSTEM_CLASS_GROUPS.thera, | ||||
|   c13: SOLAR_SYSTEM_CLASS_GROUPS.c13, | ||||
|   sentinel: SOLAR_SYSTEM_CLASS_GROUPS.drifter, | ||||
|   baribican: SOLAR_SYSTEM_CLASS_GROUPS.drifter, | ||||
|   vidette: SOLAR_SYSTEM_CLASS_GROUPS.drifter, | ||||
|   conflux: SOLAR_SYSTEM_CLASS_GROUPS.drifter, | ||||
|   redoubt: SOLAR_SYSTEM_CLASS_GROUPS.drifter, | ||||
|   a1: SOLAR_SYSTEM_CLASS_GROUPS.unknown, | ||||
|   a2: SOLAR_SYSTEM_CLASS_GROUPS.unknown, | ||||
|   a3: SOLAR_SYSTEM_CLASS_GROUPS.unknown, | ||||
|   a4: SOLAR_SYSTEM_CLASS_GROUPS.unknown, | ||||
|   a5: SOLAR_SYSTEM_CLASS_GROUPS.unknown, | ||||
|   ccp4: SOLAR_SYSTEM_CLASS_GROUPS.ccp, | ||||
|   pochven: SOLAR_SYSTEM_CLASS_GROUPS.pochven, | ||||
| }; | ||||
|  | ||||
| type WormholesAdditionalInfoType = { | ||||
|   id: string; | ||||
|   shortName: string; | ||||
|   wormholeClassID: number; | ||||
|   title: string; | ||||
|   shortTitle: string; | ||||
|   effectPower?: number; | ||||
| }; | ||||
|  | ||||
| @@ -45,6 +111,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     shortName: 'CCP', | ||||
|     wormholeClassID: -1, | ||||
|     title: 'CCP System', | ||||
|     shortTitle: 'CCP', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c1', | ||||
| @@ -52,6 +119,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 1, | ||||
|     effectPower: 1, | ||||
|     title: 'Class 1', | ||||
|     shortTitle: 'C1', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c2', | ||||
| @@ -59,6 +127,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 2, | ||||
|     effectPower: 2, | ||||
|     title: 'Class 2', | ||||
|     shortTitle: 'C2', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c3', | ||||
| @@ -66,6 +135,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 3, | ||||
|     effectPower: 3, | ||||
|     title: 'Class 3', | ||||
|     shortTitle: 'C3', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c4', | ||||
| @@ -73,6 +143,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 4, | ||||
|     effectPower: 4, | ||||
|     title: 'Class 4', | ||||
|     shortTitle: 'C4', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c5', | ||||
| @@ -80,6 +151,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 5, | ||||
|     effectPower: 5, | ||||
|     title: 'Class 5', | ||||
|     shortTitle: 'C5', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c6', | ||||
| @@ -87,42 +159,49 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 6, | ||||
|     effectPower: 6, | ||||
|     title: 'Class 6', | ||||
|     shortTitle: 'C6', | ||||
|   }, | ||||
|   { | ||||
|     id: 'hs', | ||||
|     shortName: 'H', | ||||
|     wormholeClassID: 7, | ||||
|     title: 'High-sec', | ||||
|     shortTitle: 'High-sec', | ||||
|   }, | ||||
|   { | ||||
|     id: 'ls', | ||||
|     shortName: 'L', | ||||
|     wormholeClassID: 8, | ||||
|     title: 'Low-sec', | ||||
|     shortTitle: 'Low-sec', | ||||
|   }, | ||||
|   { | ||||
|     id: 'ns', | ||||
|     shortName: 'N', | ||||
|     wormholeClassID: 9, | ||||
|     title: 'Null-sec', | ||||
|     shortTitle: 'Null-sec', | ||||
|   }, | ||||
|   { | ||||
|     id: 'ccp2', | ||||
|     shortName: 'CCP', | ||||
|     wormholeClassID: 10, | ||||
|     title: 'CCP System', | ||||
|     shortTitle: 'CCP', | ||||
|   }, | ||||
|   { | ||||
|     id: 'ccp3', | ||||
|     shortName: 'CCP', | ||||
|     wormholeClassID: 11, | ||||
|     title: 'CCP System', | ||||
|     shortTitle: 'CCP', | ||||
|   }, | ||||
|   { | ||||
|     id: 'thera', | ||||
|     shortName: 'T', | ||||
|     wormholeClassID: 12, | ||||
|     title: 'Class 12 (Thera)', | ||||
|     shortTitle: 'Thera', | ||||
|   }, | ||||
|   { | ||||
|     id: 'c13', | ||||
| @@ -130,6 +209,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 13, | ||||
|     effectPower: 6, | ||||
|     title: 'Class 13 (Shattered Frigate)', | ||||
|     shortTitle: 'C13', | ||||
|   }, | ||||
|   { | ||||
|     id: 'sentinel', | ||||
| @@ -137,6 +217,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 14, | ||||
|     effectPower: 2, | ||||
|     title: 'Class 14 (Sentinel Drifter)', | ||||
|     shortTitle: 'Sentinel', | ||||
|   }, | ||||
|   { | ||||
|     id: 'barbican', | ||||
| @@ -144,6 +225,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 15, | ||||
|     effectPower: 2, | ||||
|     title: 'Class 15 (Barbican Drifter)', | ||||
|     shortTitle: 'Barbican', | ||||
|   }, | ||||
|   { | ||||
|     id: 'vidette', | ||||
| @@ -151,6 +233,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 16, | ||||
|     effectPower: 2, | ||||
|     title: 'Class 16 (Vidette Drifter)', | ||||
|     shortTitle: 'Vidette', | ||||
|   }, | ||||
|   { | ||||
|     id: 'conflux', | ||||
| @@ -158,6 +241,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 17, | ||||
|     effectPower: 2, | ||||
|     title: 'Class 17 (Conflux Drifter)', | ||||
|     shortTitle: 'Conflux', | ||||
|   }, | ||||
|   { | ||||
|     id: 'redoubt', | ||||
| @@ -165,59 +249,79 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [ | ||||
|     wormholeClassID: 18, | ||||
|     effectPower: 2, | ||||
|     title: 'Class 18 (Redoubt Drifter)', | ||||
|     shortTitle: 'Redoubt', | ||||
|   }, | ||||
|   { | ||||
|     id: 'a1', | ||||
|     shortName: 'A1', | ||||
|     wormholeClassID: 19, | ||||
|     title: '(Abyssal class 1)', | ||||
|     shortTitle: 'A1', | ||||
|   }, | ||||
|   { | ||||
|     id: 'a2', | ||||
|     shortName: 'A2', | ||||
|     wormholeClassID: 20, | ||||
|     title: '(Abyssal class 2)', | ||||
|     shortTitle: 'A2', | ||||
|   }, | ||||
|   { | ||||
|     id: 'a3', | ||||
|     shortName: 'A3', | ||||
|     wormholeClassID: 21, | ||||
|     title: '(Abyssal class 3)', | ||||
|     shortTitle: 'A3', | ||||
|   }, | ||||
|   { | ||||
|     id: 'a4', | ||||
|     shortName: 'A4', | ||||
|     wormholeClassID: 22, | ||||
|     title: '(Abyssal class 4)', | ||||
|     shortTitle: 'A4', | ||||
|   }, | ||||
|   { | ||||
|     id: 'a5', | ||||
|     shortName: 'A5', | ||||
|     wormholeClassID: 23, | ||||
|     title: '(Abyssal class 5)', | ||||
|     shortTitle: 'A5', | ||||
|   }, | ||||
|   { | ||||
|     id: 'ccp4', | ||||
|     shortName: 'CCP', | ||||
|     wormholeClassID: 24, | ||||
|     title: 'CCP System (Penalty)', | ||||
|     shortTitle: 'CCP', | ||||
|   }, | ||||
|   { | ||||
|     id: 'pochven', | ||||
|     shortName: 'P', | ||||
|     wormholeClassID: 25, | ||||
|     title: 'Triglavian space (Pochven)', | ||||
|     shortTitle: 'Pochven', | ||||
|   }, | ||||
|   { | ||||
|     id: 'zarzakh', | ||||
|     shortName: 'N', | ||||
|     wormholeClassID: 10100, | ||||
|     title: 'Pirate space', | ||||
|     shortTitle: 'Zarzakh', | ||||
|   }, | ||||
|   { | ||||
|     id: 'k162', | ||||
|     shortName: 'K162', | ||||
|     wormholeClassID: 10101, | ||||
|     title: 'Reverse', | ||||
|     shortTitle: 'K162', | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| export const WORMHOLES_ADDITIONAL_INFO: Record<string, WormholesAdditionalInfoType> = | ||||
|   WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.id]: x }), {}); | ||||
|  | ||||
| export const WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID: Record<string, WormholesAdditionalInfoType> = | ||||
|   WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.wormholeClassID]: x }), {}); | ||||
|  | ||||
| // export const SOLAR_SYSTEM_CLASS_NAMES = { | ||||
| //   ccp1 =  , | ||||
| //   c1 = , | ||||
|   | ||||
| @@ -5,5 +5,6 @@ export * from './useMapRemoveSystems'; | ||||
| export * from './useCommandsCharacters'; | ||||
| export * from './useCommandsConnections'; | ||||
| export * from './useCommandsConnections'; | ||||
| export * from './useCenterSystem'; | ||||
| export * from './useSelectSystem'; | ||||
| export * from './useMapCommands'; | ||||
|   | ||||
| @@ -0,0 +1,18 @@ | ||||
| import { useReactFlow } from 'reactflow'; | ||||
| import { useCallback, useRef } from 'react'; | ||||
| import { CommandCenterSystem } from '@/hooks/Mapper/types'; | ||||
|  | ||||
| export const useCenterSystem = () => { | ||||
|   const rf = useReactFlow(); | ||||
|  | ||||
|   const ref = useRef({ rf }); | ||||
|   ref.current = { rf }; | ||||
|  | ||||
|   return useCallback((systemId: CommandCenterSystem) => { | ||||
|     const systemNode = ref.current.rf.getNodes().find(x => x.data.id === systemId); | ||||
|     if (!systemNode) { | ||||
|       return; | ||||
|     } | ||||
|     ref.current.rf.setCenter(systemNode.position.x, systemNode.position.y, { duration: 1000 }); | ||||
|   }, []); | ||||
| }; | ||||
| @@ -2,28 +2,17 @@ import { Node, useReactFlow } from 'reactflow'; | ||||
| import { useCallback, useRef } from 'react'; | ||||
| import { CommandAddSystems } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { convertSystem2Node } from '../../helpers'; | ||||
| import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx'; | ||||
|  | ||||
| export const useMapAddSystems = () => { | ||||
|   const rf = useReactFlow(); | ||||
|   const { | ||||
|     data: { systems }, | ||||
|     update, | ||||
|   } = useMapState(); | ||||
|  | ||||
|   const ref = useRef({ rf, systems, update }); | ||||
|   ref.current = { update, systems, rf }; | ||||
|   const ref = useRef({ rf }); | ||||
|   ref.current = { rf }; | ||||
|  | ||||
|   return useCallback( | ||||
|     (systems: CommandAddSystems) => { | ||||
|       const nodes = rf.getNodes(); | ||||
|       const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node); | ||||
|       rf.addNodes(prepared); | ||||
|  | ||||
|       ref.current.update({ | ||||
|         systems: [...ref.current.systems.filter(sys => systems.some(x => sys.id !== x.id)), ...systems], | ||||
|       }); | ||||
|     }, | ||||
|     [rf], | ||||
|   ); | ||||
|   return useCallback((systems: CommandAddSystems) => { | ||||
|     const { rf } = ref.current; | ||||
|     const nodes = rf.getNodes(); | ||||
|     const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node); | ||||
|     rf.addNodes(prepared); | ||||
|   }, []); | ||||
| }; | ||||
|   | ||||
| @@ -4,18 +4,18 @@ import { CommandSelectSystem } from '@/hooks/Mapper/types'; | ||||
|  | ||||
| export const useSelectSystem = () => { | ||||
|   const rf = useReactFlow(); | ||||
|  | ||||
|   const ref = useRef({ rf }); | ||||
|   ref.current = { rf }; | ||||
|  | ||||
|   return useCallback((systemId: CommandSelectSystem) => { | ||||
|     if (!ref.current?.rf) { | ||||
|       return; | ||||
|     } | ||||
|     const systemNode = ref.current.rf.getNodes().find(x => x.data.id === systemId); | ||||
|     if (!systemNode) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     ref.current.rf.setCenter(systemNode.position.x, systemNode.position.y, { duration: 1000 }); | ||||
|     ref.current.rf.setNodes(nds => | ||||
|       nds.map(node => { | ||||
|         return { | ||||
|           ...node, | ||||
|           selected: node.id === systemId, | ||||
|         }; | ||||
|       }), | ||||
|     ); | ||||
|   }, []); | ||||
| }; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { ForwardedRef, useImperativeHandle } from 'react'; | ||||
| import { ForwardedRef, useImperativeHandle, useRef } from 'react'; | ||||
| import { | ||||
|   CommandAddConnections, | ||||
|   CommandAddSystems, | ||||
| @@ -18,6 +18,9 @@ import { | ||||
|   CommandUpdateSystems, | ||||
|   MapHandlers, | ||||
| } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
|  | ||||
| import { useMapEventListener } from '@/hooks/Mapper/events'; | ||||
|  | ||||
| import { | ||||
|   useCommandsCharacters, | ||||
|   useCommandsConnections, | ||||
| @@ -26,6 +29,7 @@ import { | ||||
|   useMapInit, | ||||
|   useMapRemoveSystems, | ||||
|   useMapUpdateSystems, | ||||
|   useCenterSystem, | ||||
|   useSelectSystem, | ||||
| } from './api'; | ||||
| import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts'; | ||||
| @@ -35,8 +39,12 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange | ||||
|   const mapAddSystems = useMapAddSystems(); | ||||
|   const mapUpdateSystems = useMapUpdateSystems(); | ||||
|   const removeSystems = useMapRemoveSystems(onSelectionChange); | ||||
|   const centerSystem = useCenterSystem(); | ||||
|   const selectSystem = useSelectSystem(); | ||||
|  | ||||
|   const selectRef = useRef({ onSelectionChange }); | ||||
|   selectRef.current = { onSelectionChange }; | ||||
|  | ||||
|   const { addConnections, removeConnections, updateConnection } = useCommandsConnections(); | ||||
|   const { mapUpdated, killsUpdated } = useMapCommands(); | ||||
|   const { charactersUpdated, presentCharacters, characterAdded, characterRemoved, characterUpdated } = | ||||
| @@ -52,16 +60,13 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange | ||||
|               mapInit(data as CommandInit); | ||||
|               break; | ||||
|             case Commands.addSystems: | ||||
|               mapAddSystems(data as CommandAddSystems); | ||||
|               break; | ||||
|             case Commands.updateSystems: | ||||
|               mapUpdateSystems(data as CommandUpdateSystems); | ||||
|               break; | ||||
|             case Commands.removeSystems: | ||||
|               removeSystems(data as CommandRemoveSystems); | ||||
|               break; | ||||
|             case Commands.addConnections: | ||||
|               addConnections(data as CommandAddConnections); | ||||
|               break; | ||||
|             case Commands.removeConnections: | ||||
|               removeConnections(data as CommandRemoveConnections); | ||||
| @@ -91,14 +96,32 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange | ||||
|               killsUpdated(data as CommandKillsUpdated); | ||||
|               break; | ||||
|  | ||||
|             case Commands.centerSystem: | ||||
|               setTimeout(() => { | ||||
|                 const systemId = `${data}`; | ||||
|                 centerSystem(systemId as CommandSelectSystem); | ||||
|               }, 100); | ||||
|               break; | ||||
|  | ||||
|             case Commands.selectSystem: | ||||
|               selectSystem(data as CommandSelectSystem); | ||||
|               setTimeout(() => { | ||||
|                 const systemId = `${data}`; | ||||
|                 selectRef.current.onSelectionChange({ | ||||
|                   systems: [systemId], | ||||
|                   connections: [], | ||||
|                 }); | ||||
|                 selectSystem(systemId as CommandSelectSystem); | ||||
|               }, 100); | ||||
|               break; | ||||
|  | ||||
|             case Commands.routes: | ||||
|               // do nothing here | ||||
|               break; | ||||
|  | ||||
|             case Commands.linkSignatureToSystem: | ||||
|               // do nothing here | ||||
|               break; | ||||
|  | ||||
|             default: | ||||
|               console.warn(`Map handlers: Unknown command: ${type}`, data); | ||||
|               break; | ||||
| @@ -108,4 +131,20 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange | ||||
|     }, | ||||
|     [], | ||||
|   ); | ||||
|  | ||||
|   useMapEventListener(event => { | ||||
|     switch (event.name) { | ||||
|       case Commands.addConnections: | ||||
|         addConnections(event.data as CommandAddConnections); | ||||
|         break; | ||||
|       case Commands.addSystems: | ||||
|         mapAddSystems(event.data as CommandAddSystems); | ||||
|         break; | ||||
|       case Commands.removeSystems: | ||||
|         removeSystems(event.data as CommandRemoveSystems); | ||||
|         break; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import { Button } from 'primereact/button'; | ||||
| import { OutCommand } from '@/hooks/Mapper/types'; | ||||
| import { IconField } from 'primereact/iconfield'; | ||||
| import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts'; | ||||
| import { WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit'; | ||||
| import { WdImageSize, WdImgButton, TooltipPosition } from '@/hooks/Mapper/components/ui-kit'; | ||||
|  | ||||
| interface SystemCustomLabelDialog { | ||||
|   systemId: string; | ||||
| @@ -79,14 +79,14 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste | ||||
|  | ||||
|   // @ts-ignore | ||||
|   const handleInput = useCallback(e => { | ||||
|     e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, ''); | ||||
|     e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, ''); | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <Dialog | ||||
|       header="Edit label" | ||||
|       visible={visible} | ||||
|       draggable={false} | ||||
|       draggable={true} | ||||
|       style={{ width: '250px' }} | ||||
|       onHide={onHide} | ||||
|       onShow={onShow} | ||||
| @@ -100,9 +100,13 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste | ||||
|               <IconField> | ||||
|                 {label !== '' && ( | ||||
|                   <WdImgButton | ||||
|                     className="pi pi-trash p-input-icon" | ||||
|                     className="pi pi-trash text-red-400" | ||||
|                     textSize={WdImageSize.large} | ||||
|                     tooltip={{ content: 'Reset label' }} | ||||
|                     tooltip={{ | ||||
|                       content: 'Remove custom label', | ||||
|                       className: 'pi p-input-icon', | ||||
|                       position: TooltipPosition.top, | ||||
|                     }} | ||||
|                     onClick={handleReset} | ||||
|                   /> | ||||
|                 )} | ||||
| @@ -111,7 +115,7 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste | ||||
|                   aria-describedby="username-help" | ||||
|                   autoComplete="off" | ||||
|                   value={label} | ||||
|                   maxLength={3} | ||||
|                   maxLength={5} | ||||
|                   onChange={e => setLabel(e.target.value)} | ||||
|                   // @ts-expect-error | ||||
|                   ref={inputRef} | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| import { useCallback, useRef } from 'react'; | ||||
| import { Dialog } from 'primereact/dialog'; | ||||
|  | ||||
| import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types'; | ||||
| import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent'; | ||||
| import { | ||||
|   Setting, | ||||
|   COSMIC_SIGNATURE, | ||||
| } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog'; | ||||
|  | ||||
| interface SystemLinkSignatureDialogProps { | ||||
|   data: CommandLinkSignatureToSystem; | ||||
|   setVisible: (visible: boolean) => void; | ||||
| } | ||||
|  | ||||
| const signatureSettings: Setting[] = [{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true }]; | ||||
|  | ||||
| export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => { | ||||
|   const { outCommand } = useMapRootState(); | ||||
|  | ||||
|   const ref = useRef({ outCommand }); | ||||
|   ref.current = { outCommand }; | ||||
|  | ||||
|   const handleHide = useCallback(() => { | ||||
|     setVisible(false); | ||||
|   }, [setVisible]); | ||||
|  | ||||
|   const handleSelect = useCallback( | ||||
|     (signature: SystemSignature) => { | ||||
|       if (!signature) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       const { outCommand } = ref.current; | ||||
|  | ||||
|       outCommand({ | ||||
|         type: OutCommand.linkSignatureToSystem, | ||||
|         data: { | ||||
|           ...data, | ||||
|           signature_eve_id: signature.eve_id, | ||||
|         }, | ||||
|       }); | ||||
|       setVisible(false); | ||||
|     }, | ||||
|     [data, setVisible], | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <Dialog | ||||
|       header="Select signature to link" | ||||
|       visible | ||||
|       draggable={false} | ||||
|       style={{ width: '500px' }} | ||||
|       onHide={handleHide} | ||||
|       contentClassName="!p-0" | ||||
|     > | ||||
|       <SystemSignaturesContent | ||||
|         systemId={`${data.solar_system_source}`} | ||||
|         settings={signatureSettings} | ||||
|         onSelect={handleSelect} | ||||
|         selectable={true} | ||||
|       /> | ||||
|     </Dialog> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './SystemLinkSignatureDialog'; | ||||
| @@ -90,7 +90,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe | ||||
|   }, []); | ||||
|  | ||||
|   const handleInput = useCallback((e: any) => { | ||||
|     e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, ''); | ||||
|     e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, ''); | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
| @@ -160,7 +160,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe | ||||
|                   aria-describedby="label" | ||||
|                   autoComplete="off" | ||||
|                   value={label} | ||||
|                   maxLength={3} | ||||
|                   maxLength={5} | ||||
|                   onChange={e => setLabel(e.target.value)} | ||||
|                   onInput={handleInput} | ||||
|                 /> | ||||
|   | ||||
| @@ -2,3 +2,4 @@ export * from './Widget'; | ||||
| export * from './WidgetsGrid'; | ||||
| export * from './SystemSettingsDialog'; | ||||
| export * from './SystemCustomLabelDialog'; | ||||
| export * from './SystemLinkSignatureDialog'; | ||||
|   | ||||
| @@ -91,7 +91,7 @@ export const RoutesList = ({ data, onContextMenu }: RoutesListProps) => { | ||||
|   const { mapRef } = useMapRootState(); | ||||
|  | ||||
|   const handleClick = useCallback( | ||||
|     (systemId: number) => mapRef.current?.command(Commands.selectSystem, systemId.toString()), | ||||
|     (systemId: number) => mapRef.current?.command(Commands.centerSystem, systemId.toString()), | ||||
|     [mapRef], | ||||
|   ); | ||||
|  | ||||
|   | ||||
| @@ -13,9 +13,10 @@ export const SystemInfoContent = ({ systemId }: SystemInfoContentProps) => { | ||||
|     data: { systems, wormholesData }, | ||||
|   } = useMapRootState(); | ||||
|  | ||||
|   const sys = getSystemById(systems, systemId)!; | ||||
|   const sys = getSystemById(systems, systemId)! || {}; | ||||
|   const { description } = sys; | ||||
|   const { system_class, region_name, constellation_name, statics, effect_name, effect_power } = sys.system_static_info; | ||||
|   const { system_class, region_name, constellation_name, statics, effect_name, effect_power } = | ||||
|     sys.system_static_info || {}; | ||||
|   const isWH = isWormholeSpace(system_class); | ||||
|   const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]); | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,14 @@ import { Checkbox } from 'primereact/checkbox'; | ||||
|  | ||||
| export type Setting = { key: string; name: string; value: boolean }; | ||||
|  | ||||
| export const COSMIC_SIGNATURE = 'Cosmic Signature'; | ||||
| export const COSMIC_ANOMALY = 'Cosmic Anomaly'; | ||||
| export const DEPLOYABLE = 'Deployable'; | ||||
| export const STRUCTURE = 'Structure'; | ||||
| export const STARBASE = 'Starbase'; | ||||
| export const SHIP = 'Ship'; | ||||
| export const DRONE = 'Drone'; | ||||
|  | ||||
| interface SystemSignatureSettingsDialogProps { | ||||
|   settings: Setting[]; | ||||
|   onSave: (settings: Setting[]) => void; | ||||
|   | ||||
| @@ -1,7 +1,17 @@ | ||||
| import { Widget } from '@/hooks/Mapper/components/mapInterface/components'; | ||||
| import { InfoDrawer, LayoutEventBlocker, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit'; | ||||
| import { SystemSignaturesContent } from './SystemSignaturesContent'; | ||||
| import { Setting, SystemSignatureSettingsDialog } from './SystemSignatureSettingsDialog'; | ||||
| import { | ||||
|   Setting, | ||||
|   SystemSignatureSettingsDialog, | ||||
|   COSMIC_SIGNATURE, | ||||
|   COSMIC_ANOMALY, | ||||
|   DEPLOYABLE, | ||||
|   STRUCTURE, | ||||
|   STARBASE, | ||||
|   SHIP, | ||||
|   DRONE, | ||||
| } from './SystemSignatureSettingsDialog'; | ||||
|  | ||||
| import React, { useCallback, useEffect, useState } from 'react'; | ||||
|  | ||||
| @@ -9,14 +19,6 @@ import { PrimeIcons } from 'primereact/api'; | ||||
|  | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
|  | ||||
| export const COSMIC_SIGNATURE = 'Cosmic Signature'; | ||||
| export const COSMIC_ANOMALY = 'Cosmic Anomaly'; | ||||
| export const DEPLOYABLE = 'Deployable'; | ||||
| export const STRUCTURE = 'Structure'; | ||||
| export const STARBASE = 'Starbase'; | ||||
| export const SHIP = 'Ship'; | ||||
| export const DRONE = 'Drone'; | ||||
|  | ||||
| const settings: Setting[] = [ | ||||
|   { key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true }, | ||||
|   { key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true }, | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard'; | ||||
| import { parseSignatures } from '@/hooks/Mapper/helpers'; | ||||
| import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit'; | ||||
|  | ||||
| import { DataTable, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable'; | ||||
| import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable'; | ||||
| import { Column } from 'primereact/column'; | ||||
| import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | ||||
| import useRefState from 'react-usestateref'; | ||||
| @@ -22,11 +22,14 @@ import { | ||||
| } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers'; | ||||
| import { | ||||
|   renderIcon, | ||||
|   renderName, | ||||
|   renderInfoColumn, | ||||
|   renderTimeLeft, | ||||
| } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders'; | ||||
| // import { PrimeIcons } from 'primereact/api'; | ||||
| import useLocalStorageState from 'use-local-storage-state'; | ||||
| import { PrimeIcons } from 'primereact/api'; | ||||
| import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings'; | ||||
| import { useMapEventListener } from '@/hooks/Mapper/events'; | ||||
| import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper'; | ||||
|  | ||||
| type SystemSignaturesSortSettings = { | ||||
|   sortField: string; | ||||
| @@ -41,13 +44,18 @@ const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = { | ||||
| interface SystemSignaturesContentProps { | ||||
|   systemId: string; | ||||
|   settings: Setting[]; | ||||
|   selectable?: boolean; | ||||
|   onSelect?: (signature: SystemSignature) => void; | ||||
| } | ||||
| export const SystemSignaturesContent = ({ systemId, settings }: SystemSignaturesContentProps) => { | ||||
| export const SystemSignaturesContent = ({ systemId, settings, selectable, onSelect }: SystemSignaturesContentProps) => { | ||||
|   const { outCommand } = useMapRootState(); | ||||
|  | ||||
|   const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]); | ||||
|   const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]); | ||||
|   const [nameColumnWidth, setNameColumnWidth] = useState('auto'); | ||||
|   const [parsedSignatures, setParsedSignatures] = useState<SystemSignature[]>([]); | ||||
|   const [askUser, setAskUser] = useState(false); | ||||
|   const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null); | ||||
|  | ||||
|   const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null); | ||||
|  | ||||
| @@ -86,12 +94,33 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures | ||||
|       data: { system_id: systemId }, | ||||
|     }); | ||||
|  | ||||
|     setAskUser(false); | ||||
|     setSignatures(signatures); | ||||
|   }, [outCommand, systemId]); | ||||
|  | ||||
|   // const updateSignatures = useCallback( | ||||
|   //   async (newSignatures: SystemSignature[], updateOnly: boolean) => { | ||||
|   //     const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly); | ||||
|  | ||||
|   //     const { signatures: updatedSignatures } = await outCommand({ | ||||
|   //       type: OutCommand.updateSignatures, | ||||
|   //       data: { | ||||
|   //         system_id: systemId, | ||||
|   //         added, | ||||
|   //         updated, | ||||
|   //         removed, | ||||
|   //       }, | ||||
|   //     }); | ||||
|  | ||||
|   //     setSignatures(() => updatedSignatures); | ||||
|   //     setSelectedSignatures([]); | ||||
|   //   }, | ||||
|   //   [outCommand, systemId], | ||||
|   // ); | ||||
|  | ||||
|   const handleUpdateSignatures = useCallback( | ||||
|     async (newSignatures: SystemSignature[]) => { | ||||
|       const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures); | ||||
|     async (newSignatures: SystemSignature[], updateOnly: boolean) => { | ||||
|       const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly); | ||||
|  | ||||
|       const { signatures: updatedSignatures } = await outCommand({ | ||||
|         type: OutCommand.updateSignatures, | ||||
| @@ -110,43 +139,96 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures | ||||
|   ); | ||||
|  | ||||
|   const handleDeleteSelected = useCallback(async () => { | ||||
|     if (selectable) { | ||||
|       return; | ||||
|     } | ||||
|     if (selectedSignatures.length === 0) { | ||||
|       return; | ||||
|     } | ||||
|     const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id); | ||||
|     await handleUpdateSignatures(signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id))); | ||||
|   }, [handleUpdateSignatures, signatures, selectedSignatures]); | ||||
|     await handleUpdateSignatures( | ||||
|       signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)), | ||||
|       false, | ||||
|     ); | ||||
|   }, [handleUpdateSignatures, selectable, signatures, selectedSignatures]); | ||||
|  | ||||
|   const handleSelectAll = useCallback(() => { | ||||
|     setSelectedSignatures(signatures); | ||||
|   }, [signatures]); | ||||
|  | ||||
|   const handleReplaceAll = useCallback(() => { | ||||
|     handleUpdateSignatures(parsedSignatures, false); | ||||
|     setAskUser(false); | ||||
|   }, [parsedSignatures, handleUpdateSignatures]); | ||||
|  | ||||
|   const handleUpdateOnly = useCallback(() => { | ||||
|     handleUpdateSignatures(parsedSignatures, true); | ||||
|     setAskUser(false); | ||||
|   }, [parsedSignatures, handleUpdateSignatures]); | ||||
|  | ||||
|   const handleSelectSignatures = useCallback( | ||||
|     // TODO still will be good to define types if we use typescript | ||||
|     // @ts-ignore | ||||
|     e => { | ||||
|       if (selectable) { | ||||
|         onSelect?.(e.value); | ||||
|       } else { | ||||
|         setSelectedSignatures(e.value); | ||||
|       } | ||||
|     }, | ||||
|     [onSelect, selectable], | ||||
|   ); | ||||
|  | ||||
|   useHotkey(true, ['a'], handleSelectAll); | ||||
|  | ||||
|   useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (selectable) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (!clipboardContent) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const signatures = parseSignatures( | ||||
|     const newSignatures = parseSignatures( | ||||
|       clipboardContent, | ||||
|       settings.map(x => x.key), | ||||
|     ); | ||||
|  | ||||
|     handleUpdateSignatures(signatures); | ||||
|   }, [clipboardContent]); | ||||
|     const { removed } = getActualSigs(signaturesRef.current, newSignatures, false); | ||||
|  | ||||
|     if (!signaturesRef.current || !signaturesRef.current.length || !removed.length) { | ||||
|       handleUpdateSignatures(newSignatures, false); | ||||
|     } else { | ||||
|       setParsedSignatures(newSignatures); | ||||
|       setAskUser(true); | ||||
|     } | ||||
|   }, [clipboardContent, selectable]); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (!systemId) { | ||||
|       setSignatures([]); | ||||
|       setAskUser(false); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     handleGetSignatures(); | ||||
|   }, [systemId]); | ||||
|  | ||||
|   useMapEventListener(event => { | ||||
|     switch (event.name) { | ||||
|       case Commands.signaturesUpdated: | ||||
|         if (event.data?.toString() !== systemId.toString()) { | ||||
|           return; | ||||
|         } | ||||
|  | ||||
|         handleGetSignatures(); | ||||
|         return true; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     const observer = new ResizeObserver(handleResize); | ||||
|     if (tableRef.current) { | ||||
| @@ -175,107 +257,147 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures | ||||
|     setHoveredSig(null); | ||||
|   }, []); | ||||
|  | ||||
|   // const renderToolbar = (/*row: SystemSignature*/) => { | ||||
|   //   return ( | ||||
|   //     <div className="flex justify-end items-center gap-2"> | ||||
|   //       <span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span> | ||||
|   //     </div> | ||||
|   //   ); | ||||
|   // }; | ||||
|   const renderToolbar = (/*row: SystemSignature*/) => { | ||||
|     return ( | ||||
|       <div className="flex justify-end items-center gap-2 mr-[4px]"> | ||||
|         <WdTooltipWrapper content="To Edit Signature do double click"> | ||||
|           <span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span> | ||||
|         </WdTooltipWrapper> | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   const [showSignatureSettings, setShowSignatureSettings] = useState(false); | ||||
|  | ||||
|   const handleRowClick = (e: DataTableRowClickEvent) => { | ||||
|     setSelectedSignature(e.data as SystemSignature); | ||||
|     setShowSignatureSettings(true); | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <div ref={tableRef} className="h-full"> | ||||
|       {filteredSignatures.length === 0 ? ( | ||||
|         <div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm"> | ||||
|           No signatures | ||||
|         </div> | ||||
|       ) : ( | ||||
|         <> | ||||
|           <DataTable | ||||
|             className={classes.Table} | ||||
|             value={filteredSignatures} | ||||
|             size="small" | ||||
|             selectionMode="multiple" | ||||
|             selection={selectedSignatures} | ||||
|             metaKeySelection | ||||
|             onSelectionChange={e => setSelectedSignatures(e.value)} | ||||
|             dataKey="eve_id" | ||||
|             tableClassName="w-full select-none" | ||||
|             resizableColumns={false} | ||||
|             rowHover | ||||
|             selectAll | ||||
|             sortField={sortSettings.sortField} | ||||
|             sortOrder={sortSettings.sortOrder} | ||||
|             onSort={event => setSortSettings(() => ({ sortField: event.sortField, sortOrder: event.sortOrder }))} | ||||
|             onRowMouseEnter={compact || medium ? handleEnterRow : undefined} | ||||
|             onRowMouseLeave={compact || medium ? handleLeaveRow : undefined} | ||||
|             rowClassName={row => { | ||||
|               if (selectedSignatures.some(x => x.eve_id === row.eve_id)) { | ||||
|                 return clsx(classes.TableRowCompact, 'bg-amber-500/50 hover:bg-amber-500/70 transition duration-200'); | ||||
|               } | ||||
|     <> | ||||
|       <div ref={tableRef} className={'h-full '}> | ||||
|         {filteredSignatures.length === 0 ? ( | ||||
|           <div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm"> | ||||
|             No signatures | ||||
|           </div> | ||||
|         ) : ( | ||||
|           <> | ||||
|             {/* @ts-ignore */} | ||||
|             <DataTable | ||||
|               className={classes.Table} | ||||
|               value={filteredSignatures} | ||||
|               size="small" | ||||
|               selectionMode={selectable ? 'single' : 'multiple'} | ||||
|               selection={selectedSignatures} | ||||
|               metaKeySelection | ||||
|               onSelectionChange={handleSelectSignatures} | ||||
|               dataKey="eve_id" | ||||
|               tableClassName="w-full select-none" | ||||
|               resizableColumns={false} | ||||
|               onRowDoubleClick={handleRowClick} | ||||
|               rowHover | ||||
|               selectAll | ||||
|               sortField={sortSettings.sortField} | ||||
|               sortOrder={sortSettings.sortOrder} | ||||
|               onSort={event => setSortSettings(() => ({ sortField: event.sortField, sortOrder: event.sortOrder }))} | ||||
|               onRowMouseEnter={compact || medium ? handleEnterRow : undefined} | ||||
|               onRowMouseLeave={compact || medium ? handleLeaveRow : undefined} | ||||
|               rowClassName={row => { | ||||
|                 if (selectedSignatures.some(x => x.eve_id === row.eve_id)) { | ||||
|                   return clsx(classes.TableRowCompact, 'bg-amber-500/50 hover:bg-amber-500/70 transition duration-200'); | ||||
|                 } | ||||
|  | ||||
|               const dateClass = getRowColorByTimeLeft(row.updated_at ? new Date(row.updated_at) : undefined); | ||||
|               if (!dateClass) { | ||||
|                 return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200'); | ||||
|               } | ||||
|                 const dateClass = getRowColorByTimeLeft(row.updated_at ? new Date(row.updated_at) : undefined); | ||||
|                 if (!dateClass) { | ||||
|                   return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200'); | ||||
|                 } | ||||
|  | ||||
|               return clsx(classes.TableRowCompact, dateClass); | ||||
|             }} | ||||
|           > | ||||
|             <Column | ||||
|               bodyClassName="p-0 px-1" | ||||
|               field="group" | ||||
|               body={renderIcon} | ||||
|               style={{ maxWidth: 26, minWidth: 26, width: 26, height: 25 }} | ||||
|             ></Column> | ||||
|                 return clsx(classes.TableRowCompact, dateClass); | ||||
|               }} | ||||
|             > | ||||
|               <Column | ||||
|                 bodyClassName="p-0 px-1" | ||||
|                 field="group" | ||||
|                 body={x => renderIcon(x)} | ||||
|                 style={{ maxWidth: 26, minWidth: 26, width: 26, height: 25 }} | ||||
|               ></Column> | ||||
|  | ||||
|             <Column | ||||
|               field="eve_id" | ||||
|               header="Id" | ||||
|               bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|               style={{ maxWidth: 72, minWidth: 72, width: 72 }} | ||||
|               sortable | ||||
|             ></Column> | ||||
|             <Column | ||||
|               field="group" | ||||
|               header="Group" | ||||
|               bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|               hidden={compact} | ||||
|               sortable | ||||
|             ></Column> | ||||
|             <Column | ||||
|               field="name" | ||||
|               header="Name" | ||||
|               bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|               body={renderName} | ||||
|               style={{ maxWidth: nameColumnWidth }} | ||||
|               hidden={compact || medium} | ||||
|               sortable | ||||
|             ></Column> | ||||
|             <Column | ||||
|               field="updated_at" | ||||
|               header="Updated" | ||||
|               dataType="date" | ||||
|               bodyClassName="w-[80px] text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|               body={renderTimeLeft} | ||||
|               sortable | ||||
|             ></Column> | ||||
|               <Column | ||||
|                 field="eve_id" | ||||
|                 header="Id" | ||||
|                 bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|                 style={{ maxWidth: 72, minWidth: 72, width: 72 }} | ||||
|                 sortable | ||||
|               ></Column> | ||||
|               <Column | ||||
|                 field="group" | ||||
|                 header="Group" | ||||
|                 bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|                 hidden={compact} | ||||
|                 sortable | ||||
|               ></Column> | ||||
|               <Column | ||||
|                 field="info" | ||||
|                 // header="Info" | ||||
|                 bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|                 body={renderInfoColumn} | ||||
|                 style={{ maxWidth: nameColumnWidth }} | ||||
|                 hidden={compact || medium} | ||||
|               ></Column> | ||||
|               <Column | ||||
|                 field="updated_at" | ||||
|                 header="Updated" | ||||
|                 dataType="date" | ||||
|                 bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap" | ||||
|                 body={renderTimeLeft} | ||||
|                 sortable | ||||
|               ></Column> | ||||
|  | ||||
|             {/*<Column*/} | ||||
|             {/*  bodyClassName="p-0 pl-1 pr-2"*/} | ||||
|             {/*  field="group"*/} | ||||
|             {/*  body={renderToolbar}*/} | ||||
|             {/*  headerClassName={headerClasses}*/} | ||||
|             {/*  style={{ maxWidth: 26, minWidth: 26, width: 26 }}*/} | ||||
|             {/*></Column>*/} | ||||
|           </DataTable> | ||||
|         </> | ||||
|       )} | ||||
|       <WdTooltip | ||||
|         className="bg-stone-900/95 text-slate-50" | ||||
|         ref={tooltipRef} | ||||
|         content={hoveredSig ? <SignatureView {...hoveredSig} /> : null} | ||||
|       /> | ||||
|     </div> | ||||
|               <Column | ||||
|                 bodyClassName="p-0 pl-1 pr-2" | ||||
|                 field="group" | ||||
|                 body={renderToolbar} | ||||
|                 // headerClassName={headerClasses} | ||||
|                 style={{ maxWidth: 26, minWidth: 26, width: 26 }} | ||||
|               ></Column> | ||||
|             </DataTable> | ||||
|           </> | ||||
|         )} | ||||
|         <WdTooltip | ||||
|           className="bg-stone-900/95 text-slate-50" | ||||
|           ref={tooltipRef} | ||||
|           content={hoveredSig ? <SignatureView {...hoveredSig} /> : null} | ||||
|         /> | ||||
|  | ||||
|         <SignatureSettings | ||||
|           systemId={systemId} | ||||
|           show={showSignatureSettings} | ||||
|           onHide={() => setShowSignatureSettings(false)} | ||||
|           signatureData={selectedSignature} | ||||
|         /> | ||||
|  | ||||
|         {askUser && ( | ||||
|           <div className="absolute left-[1px] top-[29px] h-[calc(100%-30px)] w-[calc(100%-3px)] bg-stone-900/10 backdrop-blur-sm"> | ||||
|             <div className="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"> | ||||
|               <div className="text-stone-400/80 text-sm"> | ||||
|                 <div className="flex flex-col text-center gap-2"> | ||||
|                   <button className="p-button p-component p-button-outlined p-button-sm btn-wide"> | ||||
|                     <span className="p-button-label p-c" onClick={handleUpdateOnly}> | ||||
|                       Update | ||||
|                     </span> | ||||
|                   </button> | ||||
|                   <button className="p-button p-component p-button-outlined p-button-sm btn-wide"> | ||||
|                     <span className="p-button-label p-c" onClick={handleReplaceAll}> | ||||
|                       Update & Delete missing | ||||
|                     </span> | ||||
|                   </button> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|       </div> | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { getState } from './getState.ts'; | ||||
| export const getActualSigs = ( | ||||
|   oldSignatures: SystemSignature[], | ||||
|   newSignatures: SystemSignature[], | ||||
|   updateOnly: boolean, | ||||
| ): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => { | ||||
|   const updated: SystemSignature[] = []; | ||||
|   const removed: SystemSignature[] = []; | ||||
| @@ -20,7 +21,9 @@ export const getActualSigs = ( | ||||
|         updated.push({ ...oldSig, group: newSig.group, name: newSig.name }); | ||||
|       } | ||||
|     } else { | ||||
|       removed.push(oldSig); | ||||
|       if (!updateOnly) { | ||||
|         removed.push(oldSig); | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   | ||||
| @@ -7,9 +7,9 @@ export const getState = (_: string[], newSig: SystemSignature) => { | ||||
|   let state = -1; | ||||
|   if (!newSig.group || newSig.group === '') { | ||||
|     state = 0; | ||||
|   } else if (!!newSig.group && newSig.group !== '' && newSig.name === '') { | ||||
|   } else if (!newSig.name || newSig.name === '') { | ||||
|     state = 1; | ||||
|   } else if (!!newSig.group && newSig.group !== '' && newSig.name !== '') { | ||||
|   } else if (newSig.name !== '') { | ||||
|     state = 2; | ||||
|   } | ||||
|   return state; | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| export * from './renderIcon'; | ||||
| export * from './renderName'; | ||||
| export * from './renderTimeLeft'; | ||||
| export * from './renderLinkedSystem'; | ||||
| export * from './renderInfoColumn'; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { GroupType, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { GROUPS } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts'; | ||||
|  | ||||
| export const renderIcon = (row: SystemSignature) => { | ||||
| export const renderIcon = (row: SystemSignature, customSize?: Omit<GroupType, 'icon' | 'id'>) => { | ||||
|   if (row.group == null) { | ||||
|     return null; | ||||
|   } | ||||
| @@ -13,7 +13,7 @@ export const renderIcon = (row: SystemSignature) => { | ||||
|  | ||||
|   return ( | ||||
|     <div className="flex justify-center items-center"> | ||||
|       <img src={group.icon} style={{ width: group.w, height: group.h }} /> | ||||
|       <img src={group.icon} style={{ width: customSize?.w ?? group.w, height: customSize?.h ?? group.h }} /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -0,0 +1,3 @@ | ||||
| .whFontSize { | ||||
|   font-size: 11px !important; | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { SystemViewStandalone, WHClassView } from '@/hooks/Mapper/components/ui-kit'; | ||||
| import clsx from 'clsx'; | ||||
| import { renderName } from './renderName.tsx'; | ||||
| import classes from './renderInfoColumn.module.scss'; | ||||
|  | ||||
| export const renderInfoColumn = (row: SystemSignature) => { | ||||
|   if (!row.group || row.group === SignatureGroup.Wormhole) { | ||||
|     return ( | ||||
|       <div className="flex justify-start items-center gap-[6px]"> | ||||
|         {row.type && ( | ||||
|           <WHClassView | ||||
|             className="text-[11px]" | ||||
|             classNameWh={classes.whFontSize} | ||||
|             highlightName | ||||
|             hideWhClass={!!row.linked_system} | ||||
|             whClassName={row.type} | ||||
|             noOffset | ||||
|             useShortTitle | ||||
|           /> | ||||
|         )} | ||||
|  | ||||
|         {row.linked_system && ( | ||||
|           <> | ||||
|             {/*<span className="w-4 h-4 hero-arrow-long-right"></span>*/} | ||||
|             <span title={row.linked_system?.solar_system_name}> | ||||
|               <SystemViewStandalone | ||||
|                 className={clsx('select-none text-center cursor-context-menu')} | ||||
|                 hideRegion | ||||
|                 {...row.linked_system} | ||||
|               /> | ||||
|             </span> | ||||
|           </> | ||||
|         )} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (row.description != null && row.description.length > 0) { | ||||
|     return <span title={row.description}>{row.description}</span>; | ||||
|   } | ||||
|  | ||||
|   return renderName(row); | ||||
| }; | ||||
| @@ -0,0 +1,20 @@ | ||||
| import clsx from 'clsx'; | ||||
|  | ||||
| import { SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { SystemViewStandalone } from '@/hooks/Mapper/components/ui-kit'; | ||||
|  | ||||
| export const renderLinkedSystem = (row: SystemSignature) => { | ||||
|   if (!row.linked_system) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <span title={row.linked_system?.solar_system_name}> | ||||
|       <SystemViewStandalone | ||||
|         className={clsx('select-none text-center cursor-context-menu')} | ||||
|         hideRegion | ||||
|         {...row.linked_system} | ||||
|       /> | ||||
|     </span> | ||||
|   ); | ||||
| }; | ||||
| @@ -6,6 +6,8 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { useCallback, useState } from 'react'; | ||||
| import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components'; | ||||
| import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx'; | ||||
| import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu'; | ||||
| import { MapSettings } from "@/hooks/Mapper/components/mapRootContent/components/MapSettings"; | ||||
|  | ||||
| export interface MapRootContentProps {} | ||||
|  | ||||
| @@ -15,9 +17,13 @@ export const MapRootContent = ({}: MapRootContentProps) => { | ||||
|   const { isShowMenu } = interfaceSettings; | ||||
|  | ||||
|   const [showOnTheMap, setShowOnTheMap] = useState(false); | ||||
|   const [showMapSettings, setShowMapSettings] = useState(false); | ||||
|   const mapInterface = <MapInterface />; | ||||
|  | ||||
|   const handleShowOnTheMap = useCallback(() => setShowOnTheMap(true), []); | ||||
|   const handleShowMapSettings = useCallback(() => setShowMapSettings(true), []); | ||||
|  | ||||
|   useSkipContextMenu(); | ||||
|  | ||||
|   return ( | ||||
|     <Layout map={<MapWrapper refn={mapRef} />}> | ||||
| @@ -28,18 +34,19 @@ export const MapRootContent = ({}: MapRootContentProps) => { | ||||
|             {mapInterface} | ||||
|           </div> | ||||
|           <div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto"> | ||||
|             <RightBar onShowOnTheMap={handleShowOnTheMap} /> | ||||
|             <RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} /> | ||||
|           </div> | ||||
|         </div> | ||||
|       ) : ( | ||||
|         <div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none"> | ||||
|           <Topbar> | ||||
|             <MapContextMenu onShowOnTheMap={handleShowOnTheMap} /> | ||||
|             <MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} /> | ||||
|           </Topbar> | ||||
|           {mapInterface} | ||||
|         </div> | ||||
|       )} | ||||
|       <OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} /> | ||||
|       <MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} /> | ||||
|     </Layout> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -8,10 +8,11 @@ import { MenuItem } from 'primereact/menuitem'; | ||||
|  | ||||
| export interface MapContextMenuProps { | ||||
|   onShowOnTheMap?: () => void; | ||||
|   onShowMapSettings?: () => void; | ||||
| } | ||||
|  | ||||
| export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => { | ||||
|   const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState(); | ||||
| export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => { | ||||
|   const { outCommand, setInterfaceSettings } = useMapRootState(); | ||||
|  | ||||
|   const menuRight = useRef<Menu>(null); | ||||
|  | ||||
| @@ -22,13 +23,6 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => { | ||||
|     }); | ||||
|   }, [outCommand]); | ||||
|  | ||||
|   const toggleMinimap = useCallback(() => { | ||||
|     setInterfaceSettings(x => ({ | ||||
|       ...x, | ||||
|       isShowMinimap: !x.isShowMinimap, | ||||
|     })); | ||||
|   }, [setInterfaceSettings]); | ||||
|  | ||||
|   const items = useMemo(() => { | ||||
|     return [ | ||||
|       { | ||||
| @@ -43,9 +37,9 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => { | ||||
|       }, | ||||
|       { separator: true }, | ||||
|       { | ||||
|         label: interfaceSettings.isShowMinimap ? 'Hide minimap' : 'Show minimap', | ||||
|         icon: `pi ${interfaceSettings.isShowMinimap ? 'pi-eye-slash' : 'pi-eye'}`, | ||||
|         command: toggleMinimap, | ||||
|         label: 'Settings', | ||||
|         icon: `pi pi-cog`, | ||||
|         command: onShowMapSettings, | ||||
|       }, | ||||
|       { | ||||
|         label: 'Dock menu', | ||||
| @@ -57,7 +51,7 @@ export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => { | ||||
|           })), | ||||
|       }, | ||||
|     ] as MenuItem[]; | ||||
|   }, [handleAddCharacter, interfaceSettings.isShowMinimap, onShowOnTheMap, setInterfaceSettings, toggleMinimap]); | ||||
|   }, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]); | ||||
|  | ||||
|   return ( | ||||
|     <div className="ml-1"> | ||||
|   | ||||
| @@ -0,0 +1,127 @@ | ||||
| .verticalTabsContainer { | ||||
|   display: flex; | ||||
|   width: 100%; | ||||
|   min-height: 300px; | ||||
|  | ||||
|   :global { | ||||
|     .p-tabview { | ||||
|       width: 100%; | ||||
|       display: flex; | ||||
|       align-items: flex-start; | ||||
|     } | ||||
|  | ||||
|     .p-tabview-panels { | ||||
|       padding: 6px 1rem !important; | ||||
|       flex-grow: 1; | ||||
|     } | ||||
|  | ||||
|     .p-tabview-nav-container { | ||||
|       border-right: none; | ||||
|       height: 100%; | ||||
|     } | ||||
|  | ||||
|     .p-tabview-nav { | ||||
|       flex-direction: column; | ||||
|       width: 150px; | ||||
|       min-height: 100%; | ||||
|       border: none; | ||||
|  | ||||
|       li { | ||||
|         width: 100%; | ||||
|         border-right: 4px solid var(--surface-hover); | ||||
|         background-color: var(--surface-card); | ||||
|  | ||||
|         transition: background-color 200ms, border-right-color 200ms; | ||||
|  | ||||
|         &:hover { | ||||
|           background-color: var(--surface-hover); | ||||
|           border-right: 4px solid var(--surface-100); | ||||
|         } | ||||
|  | ||||
|         .p-tabview-nav-link { | ||||
|           transition: color 200ms; | ||||
|  | ||||
|           justify-content: flex-end; | ||||
|           padding: 10px; | ||||
|           //background-color: var(--surface-card); | ||||
|           background-color: initial; | ||||
|           border: none; | ||||
|           color: var(--gray-400); | ||||
|  | ||||
|           border-radius: initial; | ||||
|           font-weight: 400; | ||||
|           margin: 0; | ||||
|         } | ||||
|  | ||||
|         &.p-tabview-selected { | ||||
|           background-color: var(--surface-50); | ||||
|           border-right: 4px solid var(--primary-color); | ||||
|  | ||||
|           .p-tabview-nav-link { | ||||
|             font-weight: 600; | ||||
|             color: var(--primary-color); | ||||
|           } | ||||
|  | ||||
|           &:hover { | ||||
|             //background-color: var(--surface-hover); | ||||
|             border-right: 4px solid var(--primary-color); | ||||
|           } | ||||
|  | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .p-tabview-panel { | ||||
|       flex-grow: 1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| .CheckboxContainer { | ||||
|   display: grid; | ||||
|   grid-template-columns: auto 1fr auto; | ||||
|   align-items: center; | ||||
|  | ||||
|   & > span:nth-child(1) { | ||||
|     color: var(--gray-200); | ||||
|     font-size: 13px; | ||||
|   } | ||||
|  | ||||
|   & > :nth-child(2){ | ||||
|     border-bottom: 2px dotted #3f3f3f; | ||||
|     height: 2px; | ||||
|     margin: 0 12px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* Уменьшение размеров InputSwitch с использованием глобальных стилей */ | ||||
| .smallInputSwitch { | ||||
|   height: 100%; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|  | ||||
|   :global { | ||||
|     .p-inputswitch { | ||||
|       height: 1rem; | ||||
|       width: 2rem; | ||||
|       &.p-inputswitch-checked { | ||||
|         .p-inputswitch-slider::before { | ||||
|           transform: translateX(1rem); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       &.p-highlight .p-inputswitch-slider:before { | ||||
|         transform: translateX(1rem); | ||||
|       } | ||||
|  | ||||
|       .p-inputswitch-slider::before { | ||||
|         width: 0.8rem; | ||||
|         height: 0.8rem; | ||||
|         margin-top: -0.4rem; | ||||
|         margin-left: -3px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,175 @@ | ||||
| import styles from './MapSettings.module.scss'; | ||||
| import { Dialog } from 'primereact/dialog'; | ||||
| import { useCallback, useMemo, useState } from 'react'; | ||||
| import { TabPanel, TabView } from 'primereact/tabview'; | ||||
| import { PrettySwitchbox } from './components'; | ||||
| import { InterfaceStoredSettings, InterfaceStoredSettingsProps, useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { OutCommand } from '@/hooks/Mapper/types'; | ||||
|  | ||||
| export enum UserSettingsRemoteProps { | ||||
|   link_signature_on_splash = 'link_signature_on_splash', | ||||
|   select_on_spash = 'select_on_spash', | ||||
|   delete_connection_with_sigs = 'delete_connection_with_sigs', | ||||
| } | ||||
|  | ||||
| export const DEFAULT_REMOTE_SETTINGS = { | ||||
|   [UserSettingsRemoteProps.link_signature_on_splash]: false, | ||||
|   [UserSettingsRemoteProps.select_on_spash]: false, | ||||
|   [UserSettingsRemoteProps.delete_connection_with_sigs]: false, | ||||
| }; | ||||
|  | ||||
| export const UserSettingsRemoteList = [ | ||||
|   UserSettingsRemoteProps.link_signature_on_splash, | ||||
|   UserSettingsRemoteProps.select_on_spash, | ||||
|   UserSettingsRemoteProps.delete_connection_with_sigs, | ||||
| ]; | ||||
|  | ||||
| export type UserSettingsRemote = { | ||||
|   link_signature_on_splash: boolean; | ||||
|   select_on_spash: boolean; | ||||
|   delete_connection_with_sigs: boolean; | ||||
| }; | ||||
|  | ||||
| export type UserSettings = UserSettingsRemote & InterfaceStoredSettings; | ||||
|  | ||||
| export interface MapSettingsProps { | ||||
|   show: boolean; | ||||
|   onHide: () => void; | ||||
| } | ||||
|  | ||||
| type CheckboxesList = { | ||||
|   prop: keyof UserSettings; | ||||
|   label: string; | ||||
| }[]; | ||||
|  | ||||
| const COMMON_CHECKBOXES_PROPS: CheckboxesList = [ | ||||
|   { prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' }, | ||||
| ]; | ||||
|  | ||||
| const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [ | ||||
|   { prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' }, | ||||
|   { prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' }, | ||||
| ]; | ||||
|  | ||||
| const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [ | ||||
|   { prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' }, | ||||
| ]; | ||||
|  | ||||
| const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [ | ||||
|   { prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' }, | ||||
| ]; | ||||
|  | ||||
| const UI_CHECKBOXES_PROPS: CheckboxesList = [ | ||||
|   { prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' }, | ||||
| ]; | ||||
|  | ||||
| export const MapSettings = ({ show, onHide }: MapSettingsProps) => { | ||||
|   const [activeIndex, setActiveIndex] = useState(0); | ||||
|   const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState(); | ||||
|   const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({ ...DEFAULT_REMOTE_SETTINGS }); | ||||
|  | ||||
|   const mergedSettings = useMemo(() => { | ||||
|     return { | ||||
|       ...interfaceSettings, | ||||
|       ...userRemoteSettings, | ||||
|     }; | ||||
|   }, [userRemoteSettings, interfaceSettings]); | ||||
|  | ||||
|   const handleShow = async () => { | ||||
|     const { user_settings } = await outCommand({ | ||||
|       type: OutCommand.getUserSettings, | ||||
|       data: null, | ||||
|     }); | ||||
|  | ||||
|     setUserRemoteSettings({ | ||||
|       ...user_settings, | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   const handleChangeChecked = useCallback( | ||||
|     (prop: keyof UserSettings) => async (checked: boolean) => { | ||||
|       // @ts-ignore | ||||
|       if (UserSettingsRemoteList.includes(prop)) { | ||||
|         const newRemoteSettings = { | ||||
|           ...userRemoteSettings, | ||||
|           [prop]: checked, | ||||
|         }; | ||||
|  | ||||
|         await outCommand({ | ||||
|           type: OutCommand.updateUserSettings, | ||||
|           data: newRemoteSettings, | ||||
|         }); | ||||
|  | ||||
|         setUserRemoteSettings(newRemoteSettings); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       setInterfaceSettings({ | ||||
|         ...interfaceSettings, | ||||
|         [prop]: checked, | ||||
|       }); | ||||
|     }, | ||||
|     [interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings], | ||||
|   ); | ||||
|  | ||||
|   const renderCheckboxesList = (list: CheckboxesList) => { | ||||
|     return list.map(x => { | ||||
|       return ( | ||||
|         <PrettySwitchbox | ||||
|           key={x.prop} | ||||
|           label={x.label} | ||||
|           checked={mergedSettings[x.prop]} | ||||
|           setChecked={handleChangeChecked(x.prop)} | ||||
|         /> | ||||
|       ); | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <Dialog | ||||
|       header="Map settings" | ||||
|       visible={show} | ||||
|       draggable={false} | ||||
|       style={{ width: '550px' }} | ||||
|       onShow={handleShow} | ||||
|       onHide={() => { | ||||
|         if (!show) { | ||||
|           return; | ||||
|         } | ||||
|  | ||||
|         setActiveIndex(0); | ||||
|         onHide(); | ||||
|       }} | ||||
|     > | ||||
|       <div className="flex flex-col gap-3"> | ||||
|         <div className="flex flex-col gap-2"> | ||||
|           <div className={styles.verticalTabsContainer}> | ||||
|             <TabView | ||||
|               activeIndex={activeIndex} | ||||
|               onTabChange={e => setActiveIndex(e.index)} | ||||
|               className={styles.verticalTabView} | ||||
|             > | ||||
|               <TabPanel header="Common" headerClassName={styles.verticalTabHeader}> | ||||
|                 <div className="w-full h-full flex flex-col gap-1">{renderCheckboxesList(COMMON_CHECKBOXES_PROPS)}</div> | ||||
|               </TabPanel> | ||||
|               <TabPanel header="Systems" headerClassName={styles.verticalTabHeader}> | ||||
|                 <div className="w-full h-full flex flex-col gap-1"> | ||||
|                   {renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)} | ||||
|                 </div> | ||||
|               </TabPanel> | ||||
|               <TabPanel header="Connections" headerClassName={styles.verticalTabHeader}> | ||||
|                 {renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)} | ||||
|               </TabPanel> | ||||
|               <TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}> | ||||
|                 {renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)} | ||||
|               </TabPanel> | ||||
|               <TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}> | ||||
|                 {renderCheckboxesList(UI_CHECKBOXES_PROPS)} | ||||
|               </TabPanel> | ||||
|             </TabView> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </Dialog> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1,48 @@ | ||||
| .CheckboxContainer { | ||||
|   display: grid; | ||||
|   grid-template-columns: auto 1fr auto; | ||||
|   align-items: center; | ||||
|  | ||||
|   & > span:nth-child(1) { | ||||
|     color: var(--gray-200); | ||||
|     font-size: 13px; | ||||
|     user-select: none; | ||||
|   } | ||||
|  | ||||
|   & > :nth-child(2){ | ||||
|     border-bottom: 2px dotted #3f3f3f; | ||||
|     height: 1px; | ||||
|     margin: 0 12px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* Уменьшение размеров InputSwitch с использованием глобальных стилей */ | ||||
| .smallInputSwitch { | ||||
|   height: 100%; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|  | ||||
|   :global { | ||||
|     .p-inputswitch { | ||||
|       height: 1rem; | ||||
|       width: 2rem; | ||||
|       &.p-inputswitch-checked { | ||||
|         .p-inputswitch-slider::before { | ||||
|           transform: translateX(1rem); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       &.p-highlight .p-inputswitch-slider:before { | ||||
|         transform: translateX(1rem); | ||||
|       } | ||||
|  | ||||
|       .p-inputswitch-slider::before { | ||||
|         width: 0.8rem; | ||||
|         height: 0.8rem; | ||||
|         margin-top: -0.4rem; | ||||
|         margin-left: -3px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| import styles from './MapSettings.module.scss'; | ||||
| import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit'; | ||||
|  | ||||
| interface PrettySwitchboxProps { | ||||
|   checked: boolean; | ||||
|   setChecked: (checked: boolean) => void; | ||||
|   label: string; | ||||
| } | ||||
|  | ||||
| export const PrettySwitchbox = ({ checked, setChecked, label }: PrettySwitchboxProps) => { | ||||
|   return ( | ||||
|     <label className={styles.CheckboxContainer}> | ||||
|       <span>{label}</span> | ||||
|       <div /> | ||||
|       <div className={styles.smallInputSwitch}> | ||||
|         <WdCheckbox size="m" label={''} value={checked} onChange={e => setChecked(e.checked ?? false)} /> | ||||
|       </div> | ||||
|     </label> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './PrettySwitchbox'; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './PrettySwitchbox'; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './MapSettings'; | ||||
| @@ -8,9 +8,10 @@ import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit'; | ||||
|  | ||||
| interface RightBarProps { | ||||
|   onShowOnTheMap?: () => void; | ||||
|   onShowMapSettings?: () => void; | ||||
| } | ||||
|  | ||||
| export const RightBar = ({ onShowOnTheMap }: RightBarProps) => { | ||||
| export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => { | ||||
|   const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState(); | ||||
|  | ||||
|   const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap; | ||||
| @@ -59,7 +60,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => { | ||||
|             type="button" | ||||
|             onClick={handleAddCharacter} | ||||
|           > | ||||
|             <i className="pi pi-user-plus text-lg"></i> | ||||
|             <i className="pi pi-user-plus"></i> | ||||
|           </button> | ||||
|         </WdTooltipWrapper> | ||||
|  | ||||
| @@ -69,12 +70,22 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => { | ||||
|             type="button" | ||||
|             onClick={onShowOnTheMap} | ||||
|           > | ||||
|             <i className="pi pi-hashtag text-lg"></i> | ||||
|             <i className="pi pi-hashtag"></i> | ||||
|           </button> | ||||
|         </WdTooltipWrapper> | ||||
|       </div> | ||||
|  | ||||
|       <div className="flex flex-col items-center mb-2 gap-1"> | ||||
|         <WdTooltipWrapper content="User settings" position={TooltipPosition.left}> | ||||
|           <button | ||||
|             className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto" | ||||
|             type="button" | ||||
|             onClick={onShowMapSettings} | ||||
|           > | ||||
|             <i className="pi pi-cog"></i> | ||||
|           </button> | ||||
|         </WdTooltipWrapper> | ||||
|  | ||||
|         <WdTooltipWrapper | ||||
|           content={ | ||||
|             interfaceSettings.isShowKSpace ? 'Hide highlighting Imperial Space' : 'Show highlighting Imperial Space' | ||||
| @@ -86,11 +97,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => { | ||||
|             type="button" | ||||
|             onClick={toggleKSpace} | ||||
|           > | ||||
|             {interfaceSettings.isShowKSpace ? ( | ||||
|               <i className="pi pi-star-fill text-lg"></i> | ||||
|             ) : ( | ||||
|               <i className="pi pi-star text-lg"></i> | ||||
|             )} | ||||
|             <i className={interfaceSettings.isShowKSpace ? 'hero-cloud-solid' : 'hero-cloud'}></i> | ||||
|           </button> | ||||
|         </WdTooltipWrapper> | ||||
|  | ||||
| @@ -100,7 +107,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => { | ||||
|             type="button" | ||||
|             onClick={toggleMinimap} | ||||
|           > | ||||
|             {isShowMinimap ? <i className="pi pi-eye text-lg"></i> : <i className="pi pi-eye-slash text-lg"></i>} | ||||
|             <i className={isShowMinimap ? 'pi pi-eye' : 'pi pi-eye-slash'}></i> | ||||
|           </button> | ||||
|         </WdTooltipWrapper> | ||||
|  | ||||
| @@ -110,7 +117,7 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => { | ||||
|             type="button" | ||||
|             onClick={toggleMenu} | ||||
|           > | ||||
|             <i className="pi pi-window-minimize text-lg"></i> | ||||
|             <i className="pi pi-window-minimize"></i> | ||||
|           </button> | ||||
|         </WdTooltipWrapper> | ||||
|       </div> | ||||
|   | ||||
| @@ -0,0 +1,10 @@ | ||||
| import { createGenericContext } from '@/hooks/Mapper/utils/abstractContextProvider.tsx'; | ||||
|  | ||||
| export interface SystemsSettingsProvider { | ||||
|   systemId: string; | ||||
| } | ||||
|  | ||||
| const { Provider, useContextValue } = createGenericContext<SystemsSettingsProvider>(); | ||||
|  | ||||
| export const SystemsSettingsProvider = Provider; | ||||
| export const useSystemsSettingsProvider = useContextValue; | ||||
| @@ -0,0 +1,81 @@ | ||||
| .verticalTabsContainer { | ||||
|   width: 100%; | ||||
|   min-height: 300px; | ||||
| } | ||||
|  | ||||
| .verticalTabsContainer { | ||||
|   display: flex; | ||||
|   width: 100%; | ||||
|   min-height: 300px; | ||||
|  | ||||
|   :global { | ||||
|     .p-tabview { | ||||
|       width: 100%; | ||||
|       display: flex; | ||||
|       align-items: flex-start; | ||||
|     } | ||||
|  | ||||
|     .p-tabview-panels { | ||||
|       padding: 6px 1rem !important; | ||||
|       flex-grow: 1; | ||||
|     } | ||||
|  | ||||
|     .p-tabview-nav-container { | ||||
|       border-right: none; | ||||
|       height: 100%; | ||||
|     } | ||||
|  | ||||
|     .p-tabview-nav { | ||||
|       flex-direction: column; | ||||
|       width: 150px; | ||||
|       min-height: 100%; | ||||
|       border: none; | ||||
|  | ||||
|       li { | ||||
|         width: 100%; | ||||
|         border-right: 4px solid var(--surface-hover); | ||||
|         background-color: var(--surface-card); | ||||
|  | ||||
|         transition: background-color 200ms, border-right-color 200ms; | ||||
|  | ||||
|         &:hover { | ||||
|           background-color: var(--surface-hover); | ||||
|           border-right: 4px solid var(--surface-100); | ||||
|         } | ||||
|  | ||||
|         .p-tabview-nav-link { | ||||
|           transition: color 200ms; | ||||
|  | ||||
|           justify-content: flex-end; | ||||
|           padding: 10px; | ||||
|           background-color: initial; | ||||
|           border: none; | ||||
|           color: var(--gray-400); | ||||
|  | ||||
|           border-radius: initial; | ||||
|           font-weight: 400; | ||||
|           margin: 0; | ||||
|         } | ||||
|  | ||||
|         &.p-tabview-selected { | ||||
|           background-color: var(--surface-50); | ||||
|           border-right: 4px solid var(--primary-color); | ||||
|  | ||||
|           .p-tabview-nav-link { | ||||
|             font-weight: 600; | ||||
|             color: var(--primary-color); | ||||
|           } | ||||
|  | ||||
|           &:hover { | ||||
|             border-right: 4px solid var(--primary-color); | ||||
|           } | ||||
|  | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .p-tabview-panel { | ||||
|       flex-grow: 1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,171 @@ | ||||
| import { Dialog } from 'primereact/dialog'; | ||||
| import { useCallback, useEffect } from 'react'; | ||||
| // import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { Controller, FormProvider, useForm } from 'react-hook-form'; | ||||
| import { | ||||
|   SignatureGroupContent, | ||||
|   SignatureGroupSelect, | ||||
| } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components'; | ||||
| import { InputText } from 'primereact/inputtext'; | ||||
| import { SystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx'; | ||||
| import { Button } from 'primereact/button'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
|  | ||||
| type SystemSignaturePrepared = Omit<SystemSignature, 'linked_system'> & { linked_system: string }; | ||||
|  | ||||
| export interface MapSettingsProps { | ||||
|   systemId: string; | ||||
|   show: boolean; | ||||
|   onHide: () => void; | ||||
|   signatureData: SystemSignature | null; | ||||
| } | ||||
|  | ||||
| export const SignatureSettings = ({ systemId, show, onHide, signatureData }: MapSettingsProps) => { | ||||
|   const { outCommand } = useMapRootState(); | ||||
|  | ||||
|   const handleShow = async () => {}; | ||||
|   const form = useForm<Partial<SystemSignaturePrepared>>({}); | ||||
|  | ||||
|   const handleSave = useCallback(async () => { | ||||
|     if (!signatureData) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const { group, ...values } = form.getValues(); | ||||
|     let out = { ...signatureData }; | ||||
|  | ||||
|     switch (group) { | ||||
|       case SignatureGroup.Wormhole: | ||||
|         if (values.linked_system) { | ||||
|           await outCommand({ | ||||
|             type: OutCommand.linkSignatureToSystem, | ||||
|             data: { | ||||
|               signature_eve_id: signatureData.eve_id, | ||||
|               solar_system_source: systemId, | ||||
|               solar_system_target: values.linked_system, | ||||
|             }, | ||||
|           }); | ||||
|         } | ||||
|  | ||||
|         if (values.type != null) { | ||||
|           out = { ...out, type: values.type }; | ||||
|         } | ||||
|  | ||||
|         if (signatureData.group !== SignatureGroup.Wormhole) { | ||||
|           out = { ...out, name: '' }; | ||||
|         } | ||||
|  | ||||
|         break; | ||||
|       case SignatureGroup.CosmicSignature: | ||||
|         out = { ...out, type: '', name: '' }; | ||||
|         break; | ||||
|       default: | ||||
|         if (values.name != null) { | ||||
|           out = { ...out, name: values.name ?? '' }; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (values.description != null) { | ||||
|       out = { ...out, description: values.description }; | ||||
|     } | ||||
|  | ||||
|     // Note: when type of signature changed from WH to other type - we should drop name | ||||
|     if ( | ||||
|       group !== SignatureGroup.Wormhole && // new | ||||
|       signatureData.group === SignatureGroup.Wormhole && // prev | ||||
|       signatureData.linked_system | ||||
|     ) { | ||||
|       await outCommand({ | ||||
|         type: OutCommand.unlinkSignature, | ||||
|         data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId }, | ||||
|       }); | ||||
|  | ||||
|       out = { ...out, type: '' }; | ||||
|     } | ||||
|  | ||||
|     if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) { | ||||
|       await outCommand({ | ||||
|         type: OutCommand.unlinkSignature, | ||||
|         data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId }, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     // Note: despite groups have optional type - this will always set | ||||
|     out = { ...out, group: group! }; | ||||
|  | ||||
|     await outCommand({ | ||||
|       type: OutCommand.updateSignatures, | ||||
|       data: { | ||||
|         system_id: systemId, | ||||
|         added: [], | ||||
|         updated: [out], | ||||
|         removed: [], | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     form.reset(); | ||||
|     onHide(); | ||||
|   }, [form, onHide, outCommand, signatureData, systemId]); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (!signatureData) { | ||||
|       form.reset(); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const { linked_system, ...rest } = signatureData; | ||||
|  | ||||
|     form.reset({ | ||||
|       linked_system: linked_system?.solar_system_id.toString() ?? undefined, | ||||
|       ...rest, | ||||
|     }); | ||||
|   }, [form, signatureData]); | ||||
|  | ||||
|   return ( | ||||
|     <Dialog | ||||
|       header={`Signature Edit [${signatureData?.eve_id}]`} | ||||
|       visible={show} | ||||
|       draggable={false} | ||||
|       style={{ width: '390px' }} | ||||
|       onShow={handleShow} | ||||
|       onHide={() => { | ||||
|         if (!show) { | ||||
|           return; | ||||
|         } | ||||
|  | ||||
|         onHide(); | ||||
|       }} | ||||
|     > | ||||
|       <SystemsSettingsProvider initialValue={{ systemId }}> | ||||
|         <FormProvider {...form}> | ||||
|           <div className="flex flex-col gap-2 justify-between"> | ||||
|             <div className="w-full flex flex-col gap-1 p-1 min-h-[150px]"> | ||||
|               <label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]"> | ||||
|                 <span>Group:</span> | ||||
|                 <SignatureGroupSelect name="group" /> | ||||
|               </label> | ||||
|  | ||||
|               <SignatureGroupContent /> | ||||
|  | ||||
|               <label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]"> | ||||
|                 <span>Description:</span> | ||||
|                 <Controller | ||||
|                   name="description" | ||||
|                   control={form.control} | ||||
|                   render={({ field }) => ( | ||||
|                     <InputText placeholder="Type description" value={field.value} onChange={field.onChange} /> | ||||
|                   )} | ||||
|                 /> | ||||
|               </label> | ||||
|             </div> | ||||
|  | ||||
|             <div className="flex gap-2 justify-end"> | ||||
|               <Button onClick={handleSave} outlined size="small" label="Save"></Button> | ||||
|             </div> | ||||
|           </div> | ||||
|         </FormProvider> | ||||
|       </SystemsSettingsProvider> | ||||
|     </Dialog> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1,45 @@ | ||||
| import { Controller, useFormContext } from 'react-hook-form'; | ||||
| import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx'; | ||||
| import { SignatureGroupContentWormholes } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureGroupContentWormholes.tsx'; | ||||
| import { InputText } from 'primereact/inputtext'; | ||||
|  | ||||
| export interface SignatureGroupContentProps {} | ||||
|  | ||||
| export const SignatureGroupContent = ({}: SignatureGroupContentProps) => { | ||||
|   const { watch, control } = useFormContext<SystemSignature>(); | ||||
|   const group = watch('group'); | ||||
|  | ||||
|   const { | ||||
|     value: { systemId }, | ||||
|   } = useSystemsSettingsProvider(); | ||||
|  | ||||
|   if (!systemId) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   if (group === SignatureGroup.Wormhole) { | ||||
|     return ( | ||||
|       <> | ||||
|         <SignatureGroupContentWormholes /> | ||||
|       </> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (group === SignatureGroup.CosmicSignature) { | ||||
|     return <div></div>; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div> | ||||
|       <label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]"> | ||||
|         <span>Name:</span> | ||||
|         <Controller | ||||
|           name="name" | ||||
|           control={control} | ||||
|           render={({ field }) => <InputText placeholder="Name" value={field.value} onChange={field.onChange} />} | ||||
|         /> | ||||
|       </label> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './SignatureGroupContent'; | ||||
| @@ -0,0 +1,18 @@ | ||||
| import { SignatureWormholeTypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureWormholeTypeSelect'; | ||||
| import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect'; | ||||
|  | ||||
| export const SignatureGroupContentWormholes = () => { | ||||
|   return ( | ||||
|     <> | ||||
|       <label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]"> | ||||
|         <span>Type:</span> | ||||
|         <SignatureWormholeTypeSelect name="type" /> | ||||
|       </label> | ||||
|  | ||||
|       <label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]"> | ||||
|         <span>Leads To:</span> | ||||
|         <SignatureLeadsToSelect name="linked_system" /> | ||||
|       </label> | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1,59 @@ | ||||
| import { Dropdown } from 'primereact/dropdown'; | ||||
| import clsx from 'clsx'; | ||||
| import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { renderIcon } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders'; | ||||
| import { Controller, useFormContext } from 'react-hook-form'; | ||||
|  | ||||
| const signatureGroupOptions = Object.keys(SignatureGroup).map(x => ({ | ||||
|   value: SignatureGroup[x as keyof typeof SignatureGroup], | ||||
|   label: SignatureGroup[x as keyof typeof SignatureGroup], | ||||
| })); | ||||
|  | ||||
| // @ts-ignore | ||||
| const renderSignatureTemplate = option => { | ||||
|   if (!option) { | ||||
|     return 'No group selected'; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div className="flex gap-2 items-center"> | ||||
|       <span className="w-[20px] mt-[1px] flex justify-center items-center"> | ||||
|         {renderIcon( | ||||
|           { group: option.label } as SystemSignature, | ||||
|           option.label === SignatureGroup.CosmicSignature ? { w: 10, h: 10 } : { w: 16, h: 16 }, | ||||
|         )} | ||||
|       </span> | ||||
|       <span>{option.label}</span> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export interface SignatureGroupSelectProps { | ||||
|   name: string; | ||||
|   defaultValue?: string; | ||||
| } | ||||
|  | ||||
| export const SignatureGroupSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => { | ||||
|   const { control } = useFormContext(); | ||||
|   return ( | ||||
|     <Controller | ||||
|       name={name} | ||||
|       control={control} | ||||
|       defaultValue={defaultValue} | ||||
|       render={({ field }) => ( | ||||
|         <Dropdown | ||||
|           value={field.value} | ||||
|           onChange={field.onChange} | ||||
|           options={signatureGroupOptions} | ||||
|           optionLabel="label" | ||||
|           optionValue="value" | ||||
|           placeholder="Select group" | ||||
|           className={clsx('w-full')} | ||||
|           scrollHeight="240px" | ||||
|           itemTemplate={renderSignatureTemplate} | ||||
|           valueTemplate={renderSignatureTemplate} | ||||
|         /> | ||||
|       )} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './SignatureGroupSelect'; | ||||
| @@ -0,0 +1,3 @@ | ||||
| .SystemView { | ||||
|   font-size: 14px !important; | ||||
| } | ||||
| @@ -0,0 +1,108 @@ | ||||
| import { Dropdown } from 'primereact/dropdown'; | ||||
| import clsx from 'clsx'; | ||||
| import { Controller, useFormContext } from 'react-hook-form'; | ||||
| import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx'; | ||||
| import { useSystemInfo } from '@/hooks/Mapper/components/hooks'; | ||||
| import { useMemo } from 'react'; | ||||
| import { SystemView } from '@/hooks/Mapper/components/ui-kit'; | ||||
| import classes from './SignatureLeadsToSelect.module.scss'; | ||||
| import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts'; | ||||
| import { SystemSignature } from '@/hooks/Mapper/types'; | ||||
| import { WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID } from '@/hooks/Mapper/components/map/constants.ts'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
|  | ||||
| // @ts-ignore | ||||
| const renderLinkedSystemItem = (option: { value: string }) => { | ||||
|   if (option.value == null) { | ||||
|     return <div className="flex gap-2 items-center">No linked system</div>; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div className="flex gap-2 items-center"> | ||||
|       <SystemView systemId={option.value} className={classes.SystemView} /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| // @ts-ignore | ||||
| const renderLinkedSystemValue = (option: { value: string }) => { | ||||
|   if (!option) { | ||||
|     return 'Select Leads To system'; | ||||
|   } | ||||
|  | ||||
|   if (option.value == null) { | ||||
|     return 'Select Leads To system'; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div className="flex gap-2 items-center"> | ||||
|       <SystemView systemId={option.value} className={classes.SystemView} /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const renderLeadsToEmpty = () => <div className="flex items-center text-[14px]">No wormhole to select</div>; | ||||
|  | ||||
| export interface SignatureLeadsToSelectProps { | ||||
|   name: string; | ||||
|   defaultValue?: string; | ||||
| } | ||||
|  | ||||
| export const SignatureLeadsToSelect = ({ name, defaultValue = '' }: SignatureLeadsToSelectProps) => { | ||||
|   const { control, watch } = useFormContext<SystemSignature>(); | ||||
|   const group = watch('type'); | ||||
|  | ||||
|   const { | ||||
|     value: { systemId }, | ||||
|   } = useSystemsSettingsProvider(); | ||||
|  | ||||
|   const { leadsTo } = useSystemInfo({ systemId }); | ||||
|   const { systems: systemStatics } = useLoadSystemStatic({ systems: leadsTo }); | ||||
|   const { | ||||
|     data: { wormholes }, | ||||
|   } = useMapRootState(); | ||||
|  | ||||
|   const leadsToOptions = useMemo(() => { | ||||
|     return [ | ||||
|       { value: null }, | ||||
|       ...leadsTo | ||||
|         .filter(systemId => { | ||||
|           const systemStatic = systemStatics.get(parseInt(systemId)); | ||||
|           const whInfo = wormholes.find(x => x.name === group); | ||||
|  | ||||
|           if (!systemStatic || !whInfo || group === 'K162') { | ||||
|             return true; | ||||
|           } | ||||
|  | ||||
|           const { id: whType } = WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID[systemStatic.system_class]; | ||||
|           return whInfo.dest === whType; | ||||
|         }) | ||||
|         .map(x => ({ value: x })), | ||||
|     ]; | ||||
|   }, [group, leadsTo, systemStatics, wormholes]); | ||||
|  | ||||
|   return ( | ||||
|     <Controller | ||||
|       // @ts-ignore | ||||
|       name={name} | ||||
|       control={control} | ||||
|       defaultValue={defaultValue} | ||||
|       render={({ field }) => { | ||||
|         return ( | ||||
|           <Dropdown | ||||
|             value={field.value} | ||||
|             onChange={field.onChange} | ||||
|             options={leadsToOptions} | ||||
|             optionValue="value" | ||||
|             placeholder="Select Leads To wormhole" | ||||
|             className={clsx('w-full')} | ||||
|             scrollHeight="240px" | ||||
|             itemTemplate={renderLinkedSystemItem} | ||||
|             valueTemplate={renderLinkedSystemValue} | ||||
|             emptyMessage={renderLeadsToEmpty} | ||||
|           /> | ||||
|         ); | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './SignatureLeadsToSelect.tsx'; | ||||
| @@ -0,0 +1,134 @@ | ||||
| import { Dropdown } from 'primereact/dropdown'; | ||||
| import clsx from 'clsx'; | ||||
| import { Respawn, SolarSystemStaticInfoRaw, WormholeDataRaw } from '@/hooks/Mapper/types'; | ||||
| import { Controller, useFormContext } from 'react-hook-form'; | ||||
| import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx'; | ||||
| import { useSystemInfo } from '@/hooks/Mapper/components/hooks'; | ||||
| import { | ||||
|   SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS, | ||||
|   WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID, | ||||
| } from '@/hooks/Mapper/components/map/constants.ts'; | ||||
| import { useMemo } from 'react'; | ||||
| import { WHClassView } from '@/hooks/Mapper/components/ui-kit'; | ||||
|  | ||||
| const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes: WormholeDataRaw[]) => { | ||||
|   const { id: whType } = WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID[systemStatic.system_class]; | ||||
|  | ||||
|   // @ts-ignore | ||||
|   const spawnClassGroup = SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[whType]; | ||||
|   const possibleWHTypes = wormholes.filter(x => x.src.includes(spawnClassGroup)); | ||||
|  | ||||
|   return { | ||||
|     statics: possibleWHTypes | ||||
|       .filter(x => x.respawn.some(y => y === Respawn.static)) | ||||
|       .filter(x => systemStatic.statics.includes(x.name)), | ||||
|     k162: wormholes.find(x => x.name === 'K162')!, | ||||
|     wanderings: possibleWHTypes.filter(x => x.respawn.some(y => y === Respawn.wandering)), | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| // @ts-ignore | ||||
| const renderWHTypeGroupTemplate = option => { | ||||
|   return ( | ||||
|     <div className="flex gap-2 items-center"> | ||||
|       <span>{option.label}</span> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| // @ts-ignore | ||||
| const renderWHTypeTemplateValue = (option: { label: string; data: WormholeDataRaw }) => { | ||||
|   if (!option) { | ||||
|     return 'Select wormhole type'; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div className="flex gap-2 items-center"> | ||||
|       <WHClassView whClassName={option.data.name} noOffset useShortTitle /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| // @ts-ignore | ||||
| const renderWHTypeTemplate = (option: { label: string; data: WormholeDataRaw }) => { | ||||
|   return ( | ||||
|     <div className="flex gap-2 items-center ml-[1rem]"> | ||||
|       <WHClassView whClassName={option.data.name} noOffset useShortTitle /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export interface SignatureGroupSelectProps { | ||||
|   name: string; | ||||
|   defaultValue?: string; | ||||
| } | ||||
|  | ||||
| export const SignatureWormholeTypeSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => { | ||||
|   const { control } = useFormContext(); | ||||
|  | ||||
|   const { | ||||
|     data: { wormholes }, | ||||
|   } = useMapRootState(); | ||||
|  | ||||
|   const { | ||||
|     value: { systemId }, | ||||
|   } = useSystemsSettingsProvider(); | ||||
|  | ||||
|   const system = useSystemInfo({ systemId }); | ||||
|  | ||||
|   const possibleWormholesOptions = useMemo(() => { | ||||
|     const possibleWormholes = getPossibleWormholes(system.staticInfo, wormholes); | ||||
|  | ||||
|     return [ | ||||
|       { | ||||
|         label: 'Statics', | ||||
|         items: [ | ||||
|           ...possibleWormholes.statics.map(x => ({ | ||||
|             label: x.name, | ||||
|             value: x.name, | ||||
|             data: x, | ||||
|           })), | ||||
|           { | ||||
|             value: possibleWormholes.k162.name, | ||||
|             label: possibleWormholes.k162.name, | ||||
|             data: possibleWormholes.k162, | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|       { | ||||
|         label: 'Wanderings', | ||||
|         items: possibleWormholes.wanderings.map(x => ({ | ||||
|           label: x.name, | ||||
|           value: x.name, | ||||
|           data: x, | ||||
|         })), | ||||
|       }, | ||||
|     ]; | ||||
|   }, [system, wormholes]); | ||||
|  | ||||
|   return ( | ||||
|     <Controller | ||||
|       name={name} | ||||
|       control={control} | ||||
|       defaultValue={defaultValue} | ||||
|       render={({ field }) => ( | ||||
|         <Dropdown | ||||
|           value={field.value} | ||||
|           onChange={field.onChange} | ||||
|           options={possibleWormholesOptions} | ||||
|           optionLabel="label" | ||||
|           optionValue="value" | ||||
|           placeholder="Select wormhole type" | ||||
|           optionGroupLabel="label" | ||||
|           optionGroupChildren="items" | ||||
|           className={clsx('w-full')} | ||||
|           scrollHeight="240px" | ||||
|           optionGroupTemplate={renderWHTypeGroupTemplate} | ||||
|           itemTemplate={renderWHTypeTemplate} | ||||
|           valueTemplate={renderWHTypeTemplateValue} | ||||
|         /> | ||||
|       )} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './SignatureWormholeTypeSelect'; | ||||
| @@ -0,0 +1,2 @@ | ||||
| export * from './SignatureGroupSelect'; | ||||
| export * from './SignatureGroupContent'; | ||||
| @@ -0,0 +1 @@ | ||||
| export * from './SignatureSettings.tsx'; | ||||
| @@ -5,13 +5,20 @@ import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider'; | ||||
| import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts'; | ||||
| import isEqual from 'lodash.isequal'; | ||||
| import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts'; | ||||
| import { SystemCustomLabelDialog, SystemSettingsDialog } from '@/hooks/Mapper/components/mapInterface/components'; | ||||
| import { | ||||
|   SystemCustomLabelDialog, | ||||
|   SystemSettingsDialog, | ||||
|   SystemLinkSignatureDialog, | ||||
| } from '@/hooks/Mapper/components/mapInterface/components'; | ||||
| import classes from './MapWrapper.module.scss'; | ||||
| import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections'; | ||||
| import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple'; | ||||
| import { getSystemById } from '@/hooks/Mapper/helpers'; | ||||
| import { Node } from 'reactflow'; | ||||
|  | ||||
| import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
| import { useMapEventListener } from '@/hooks/Mapper/events'; | ||||
|  | ||||
| import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider'; | ||||
|  | ||||
| interface MapWrapperProps { | ||||
| @@ -53,6 +60,7 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => { | ||||
|   ); | ||||
|  | ||||
|   const [openSettings, setOpenSettings] = useState<string | null>(null); | ||||
|   const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null); | ||||
|   const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null); | ||||
|   const handleCommand: OutCommandHandler = useCallback( | ||||
|     event => { | ||||
| @@ -60,6 +68,9 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => { | ||||
|         case OutCommand.openSettings: | ||||
|           setOpenSettings(event.data.system_id); | ||||
|           break; | ||||
|         case OutCommand.linkSignatureToSystem: | ||||
|           setOpenLinkSignatures(event.data); | ||||
|           break; | ||||
|         default: | ||||
|           return outCommand(event); | ||||
|       } | ||||
| @@ -88,6 +99,14 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => { | ||||
|  | ||||
|   const handleConnectionDbClick = useCallback((e: SolarSystemConnection) => setSelectedConnection(e), []); | ||||
|  | ||||
|   useMapEventListener(event => { | ||||
|     switch (event.name) { | ||||
|       case Commands.linkSignatureToSystem: | ||||
|         setOpenLinkSignatures(event.data); | ||||
|         return true; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <Map | ||||
| @@ -103,19 +122,15 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => { | ||||
|       /> | ||||
|  | ||||
|       {openSettings != null && ( | ||||
|         <SystemSettingsDialog | ||||
|           systemId={openSettings} | ||||
|           visible={openSettings != null} | ||||
|           setVisible={() => setOpenSettings(null)} | ||||
|         /> | ||||
|         <SystemSettingsDialog systemId={openSettings} visible setVisible={() => setOpenSettings(null)} /> | ||||
|       )} | ||||
|  | ||||
|       {openCustomLabel != null && ( | ||||
|         <SystemCustomLabelDialog | ||||
|           systemId={openCustomLabel} | ||||
|           visible={openCustomLabel != null} | ||||
|           setVisible={() => setOpenCustomLabel(null)} | ||||
|         /> | ||||
|         <SystemCustomLabelDialog systemId={openCustomLabel} visible setVisible={() => setOpenCustomLabel(null)} /> | ||||
|       )} | ||||
|  | ||||
|       {openLinkSignatures != null && ( | ||||
|         <SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} /> | ||||
|       )} | ||||
|  | ||||
|       <Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} /> | ||||
|   | ||||
| @@ -37,7 +37,7 @@ export const CharacterCard = ({ | ||||
|   const { mapRef } = useMapRootState(); | ||||
|  | ||||
|   const handleSelect = useCallback(() => { | ||||
|     mapRef.current?.command(Commands.selectSystem, char?.location?.solar_system_id?.toString()); | ||||
|     mapRef.current?.command(Commands.centerSystem, char?.location?.solar_system_id?.toString()); | ||||
|   }, [mapRef, char]); | ||||
|  | ||||
|   return ( | ||||
|   | ||||
| @@ -5,6 +5,11 @@ | ||||
| .WHClassViewContent { | ||||
|   display: flex; | ||||
|   gap: 2px; | ||||
|  | ||||
|   &.NoOffset { | ||||
|     gap: 4px; | ||||
|     align-items: center; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .WHClassName { | ||||
| @@ -13,3 +18,12 @@ | ||||
|   font-weight: bold; | ||||
|   top: -2px; | ||||
| } | ||||
|  | ||||
| .NoOffset { | ||||
|   *.WHClassName { | ||||
|     position: relative; | ||||
|     font-size: 12px; | ||||
|     font-weight: initial !important; | ||||
|     top: initial !important; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -16,26 +16,42 @@ const prepareMass = (mass: number) => { | ||||
|  | ||||
| export interface WHClassViewProps { | ||||
|   whClassName: string; | ||||
|   noOffset?: boolean; | ||||
|   useShortTitle?: boolean; | ||||
|   hideWhClass?: boolean; | ||||
|   highlightName?: boolean; | ||||
|   className?: string; | ||||
|   classNameWh?: string; | ||||
| } | ||||
|  | ||||
| export const WHClassView = ({ whClassName }: WHClassViewProps) => { | ||||
| export const WHClassView = ({ | ||||
|   whClassName, | ||||
|   noOffset, | ||||
|   useShortTitle, | ||||
|   hideWhClass, | ||||
|   highlightName, | ||||
|   className, | ||||
|   classNameWh, | ||||
| }: WHClassViewProps) => { | ||||
|   const { | ||||
|     data: { wormholesData }, | ||||
|   } = useMapRootState(); | ||||
|  | ||||
|   const whData = useMemo(() => wormholesData[whClassName], [whClassName, wormholesData]); | ||||
|   const whClass = useMemo(() => WORMHOLES_ADDITIONAL_INFO[whData.dest], [whData.dest]); | ||||
|   const whClassStyle = WORMHOLE_CLASS_STYLES[whClass.wormholeClassID]; | ||||
|   const whClassStyle = WORMHOLE_CLASS_STYLES[whClass?.wormholeClassID] ?? ''; | ||||
|  | ||||
|   const uid = useMemo(() => new Date().getTime().toString(), []); | ||||
|  | ||||
|   return ( | ||||
|     <div className={classes.WHClassViewRoot}> | ||||
|     <div className={clsx(classes.WHClassViewRoot, className)}> | ||||
|       <Tooltip | ||||
|         target={`.wh-name${whClassName}`} | ||||
|         target={`.wh-name${whClassName}${uid}`} | ||||
|         position="right" | ||||
|         mouseTrack | ||||
|         mouseTrackLeft={20} | ||||
|         mouseTrackTop={30} | ||||
|         className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-70 " | ||||
|         className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 " | ||||
|       > | ||||
|         <div className="flex gap-3"> | ||||
|           <div className="flex flex-col gap-1"> | ||||
| @@ -49,9 +65,20 @@ export const WHClassView = ({ whClassName }: WHClassViewProps) => { | ||||
|         </div> | ||||
|       </Tooltip> | ||||
|  | ||||
|       <div className={clsx(classes.WHClassViewContent, 'wh-name select-none cursor-help', `wh-name${whClassName}`)}> | ||||
|         <span>{whClassName}</span> | ||||
|         <span className={clsx(classes.WHClassName, whClassStyle)}>{whClass.shortName}</span> | ||||
|       <div | ||||
|         className={clsx( | ||||
|           classes.WHClassViewContent, | ||||
|           { [classes.NoOffset]: noOffset }, | ||||
|           'wh-name select-none cursor-help', | ||||
|           `wh-name${whClassName}${uid}`, | ||||
|         )} | ||||
|       > | ||||
|         <span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span> | ||||
|         {!hideWhClass && whClass && ( | ||||
|           <span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}> | ||||
|             {useShortTitle ? whClass.shortTitle : whClass.shortName} | ||||
|           </span> | ||||
|         )} | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
|   | ||||
							
								
								
									
										12
									
								
								assets/js/hooks/Mapper/events/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/js/hooks/Mapper/events/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import { createEvent } from 'react-event-hook'; | ||||
|  | ||||
| import { Command, CommandData } from '@/hooks/Mapper/types/mapHandlers.ts'; | ||||
|  | ||||
| export interface MapEvent<T extends Command> { | ||||
|   name: T; | ||||
|   data: CommandData[T]; | ||||
| } | ||||
|  | ||||
| const { useMapEventListener, emitMapEvent } = createEvent('map-event')<MapEvent<Command>>(); | ||||
|  | ||||
| export { useMapEventListener, emitMapEvent }; | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures'; | ||||
| import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog'; | ||||
| import { SystemSignature } from '@/hooks/Mapper/types'; | ||||
|  | ||||
| export const parseSignatures = (value: string, availableKeys: string[]): SystemSignature[] => { | ||||
| @@ -19,6 +19,7 @@ export const parseSignatures = (value: string, availableKeys: string[]): SystemS | ||||
|       kind: availableKeys.includes(sigArrInfo[1]) ? sigArrInfo[1] : COSMIC_SIGNATURE, | ||||
|       group: sigArrInfo[2], | ||||
|       name: sigArrInfo[3], | ||||
|       type: '', | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,10 @@ import { WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constan | ||||
| import { WormholeDataRaw } from '@/hooks/Mapper/types'; | ||||
|  | ||||
| export const sortWHClasses = (wormholesData: Record<string, WormholeDataRaw>, statics: string[]) => { | ||||
|   if (!statics) { | ||||
|     return []; | ||||
|   } | ||||
|  | ||||
|   return statics | ||||
|     .map(x => wormholesData[x]) | ||||
|     .map(x => ({ name: x.name, ...WORMHOLES_ADDITIONAL_INFO[x.dest] })) | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| export * from './usePageVisibility'; | ||||
| export * from './useClipboard'; | ||||
| export * from './useHotkey'; | ||||
| export * from './useSkipContextMenu'; | ||||
|   | ||||
							
								
								
									
										15
									
								
								assets/js/hooks/Mapper/hooks/useSkipContextMenu.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								assets/js/hooks/Mapper/hooks/useSkipContextMenu.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| import { useEffect } from 'react'; | ||||
|  | ||||
| export const useSkipContextMenu = () => { | ||||
|   useEffect(() => { | ||||
|     function handleContextMenu(e) { | ||||
|       e.preventDefault(); | ||||
|     } | ||||
|  | ||||
|     window.addEventListener(`contextmenu`, handleContextMenu); | ||||
|  | ||||
|     return () => { | ||||
|       window.removeEventListener(`contextmenu`, handleContextMenu); | ||||
|     }; | ||||
|   }, []); | ||||
| }; | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { createRoot } from 'react-dom/client'; | ||||
| import Mapper from './MapRoot'; | ||||
| import { decompressToJson } from './utils'; | ||||
|  | ||||
| export default { | ||||
|   _rootEl: null, | ||||
| @@ -28,17 +27,12 @@ export default { | ||||
|  | ||||
|   handleEventWrapper(event: string, handler: (payload: any) => void) { | ||||
|     this.handleEvent(event, (body: any) => { | ||||
|       if (event === 'map_event') { | ||||
|         const { type, body: data } = body; | ||||
|         handler({ type, body: decompressToJson(data) }); | ||||
|       } else { | ||||
|         handler(body); | ||||
|       } | ||||
|       handler(body); | ||||
|     }); | ||||
|   }, | ||||
|  | ||||
|   reconnected() { | ||||
|     this.pushEvent('reconnected'); | ||||
|     this.pushEvent('ui_loaded'); | ||||
|   }, | ||||
|  | ||||
|   async pushEventAsync(event: string, payload: any) { | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import { MapHandlers, MapUnionTypes, OutCommandHandler, SolarSystemConnection } | ||||
| import { useMapRootHandlers } from '@/hooks/Mapper/mapRootProvider/hooks'; | ||||
| import { WithChildren } from '@/hooks/Mapper/types/common.ts'; | ||||
| import useLocalStorageState from 'use-local-storage-state'; | ||||
| import { DEFAULT_SETTINGS } from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx'; | ||||
|  | ||||
| export type MapRootData = MapUnionTypes & { | ||||
|   selectedSystems: string[]; | ||||
| @@ -13,6 +12,7 @@ export type MapRootData = MapUnionTypes & { | ||||
|  | ||||
| const INITIAL_DATA: MapRootData = { | ||||
|   wormholesData: {}, | ||||
|   wormholes: [], | ||||
|   effects: {}, | ||||
|   characters: [], | ||||
|   userCharacters: [], | ||||
| @@ -27,7 +27,13 @@ const INITIAL_DATA: MapRootData = { | ||||
|   selectedConnections: [], | ||||
| }; | ||||
|  | ||||
| type InterfaceStoredSettings = { | ||||
| export enum InterfaceStoredSettingsProps { | ||||
|   isShowMenu = 'isShowMenu', | ||||
|   isShowMinimap = 'isShowMinimap', | ||||
|   isShowKSpace = 'isShowKSpace', | ||||
| } | ||||
|  | ||||
| export type InterfaceStoredSettings = { | ||||
|   isShowMenu: boolean; | ||||
|   isShowMinimap: boolean; | ||||
|   isShowKSpace: boolean; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ export const useMapInit = () => { | ||||
|  | ||||
|       if (wormholes) { | ||||
|         updateData.wormholesData = wormholes.reduce((acc, x) => ({ ...acc, [x.name]: x }), {}); | ||||
|         updateData.wormholes = wormholes; | ||||
|       } | ||||
|  | ||||
|       if (effects) { | ||||
|   | ||||
| @@ -27,6 +27,8 @@ import { | ||||
|   useRoutes, | ||||
| } from './api'; | ||||
|  | ||||
| import { emitMapEvent } from '@/hooks/Mapper/events'; | ||||
|  | ||||
| export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => { | ||||
|   const mapInit = useMapInit(); | ||||
|   const { addSystems, removeSystems, updateSystems } = useCommandsSystems(); | ||||
| @@ -47,15 +49,25 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => { | ||||
|               break; | ||||
|             case Commands.addSystems: | ||||
|               addSystems(data as CommandAddSystems); | ||||
|               setTimeout(() => { | ||||
|                 emitMapEvent({ name: Commands.addSystems, data }); | ||||
|               }, 100); | ||||
|               break; | ||||
|             case Commands.updateSystems: | ||||
|               updateSystems(data as CommandUpdateSystems); | ||||
|               break; | ||||
|             case Commands.removeSystems: | ||||
|               removeSystems(data as CommandRemoveSystems); | ||||
|               setTimeout(() => { | ||||
|                 emitMapEvent({ name: Commands.removeSystems, data }); | ||||
|               }, 100); | ||||
|  | ||||
|               break; | ||||
|             case Commands.addConnections: | ||||
|               addConnections(data as CommandAddConnections); | ||||
|               setTimeout(() => { | ||||
|                 emitMapEvent({ name: Commands.addConnections, data }); | ||||
|               }, 100); | ||||
|               break; | ||||
|             case Commands.removeConnections: | ||||
|               removeConnections(data as CommandRemoveConnections); | ||||
| @@ -85,14 +97,30 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => { | ||||
|               mapRoutes(data as CommandRoutes); | ||||
|               break; | ||||
|  | ||||
|             case Commands.centerSystem: | ||||
|               // do nothing here | ||||
|               break; | ||||
|  | ||||
|             case Commands.selectSystem: | ||||
|               // do nothing here | ||||
|               break; | ||||
|  | ||||
|             case Commands.linkSignatureToSystem: | ||||
|               // TODO command data type lost | ||||
|               // @ts-ignore | ||||
|               emitMapEvent({ name: Commands.linkSignatureToSystem, data }); | ||||
|               break; | ||||
|  | ||||
|             case Commands.killsUpdated: | ||||
|               // do nothing here | ||||
|               break; | ||||
|  | ||||
|             case Commands.signaturesUpdated: | ||||
|               // TODO command data type lost | ||||
|               // @ts-ignore | ||||
|               emitMapEvent({ name: Commands.signaturesUpdated, data }); | ||||
|               break; | ||||
|  | ||||
|             default: | ||||
|               console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data); | ||||
|               break; | ||||
|   | ||||
| @@ -21,7 +21,10 @@ export enum Commands { | ||||
|   mapUpdated = 'map_updated', | ||||
|   killsUpdated = 'kills_updated', | ||||
|   routes = 'routes', | ||||
|   centerSystem = 'center_system', | ||||
|   selectSystem = 'select_system', | ||||
|   linkSignatureToSystem = 'link_signature_to_system', | ||||
|   signaturesUpdated = 'signatures_updated', | ||||
| } | ||||
|  | ||||
| export type Command = | ||||
| @@ -40,7 +43,10 @@ export type Command = | ||||
|   | Commands.mapUpdated | ||||
|   | Commands.killsUpdated | ||||
|   | Commands.routes | ||||
|   | Commands.selectSystem; | ||||
|   | Commands.selectSystem | ||||
|   | Commands.centerSystem | ||||
|   | Commands.linkSignatureToSystem | ||||
|   | Commands.signaturesUpdated; | ||||
|  | ||||
| export type CommandInit = { | ||||
|   systems: SolarSystemRawType[]; | ||||
| @@ -72,6 +78,12 @@ export type CommandMapUpdated = Partial<CommandInit>; | ||||
| export type CommandRoutes = RoutesList; | ||||
| export type CommandKillsUpdated = Kill[]; | ||||
| export type CommandSelectSystem = string | undefined; | ||||
| export type CommandCenterSystem = string | undefined; | ||||
| export type CommandLinkSignatureToSystem = { | ||||
|   solar_system_source: number; | ||||
|   solar_system_target: number; | ||||
| }; | ||||
| export type CommandLinkSignaturesUpdated = number; | ||||
|  | ||||
| export interface CommandData { | ||||
|   [Commands.init]: CommandInit; | ||||
| @@ -90,6 +102,9 @@ export interface CommandData { | ||||
|   [Commands.routes]: CommandRoutes; | ||||
|   [Commands.killsUpdated]: CommandKillsUpdated; | ||||
|   [Commands.selectSystem]: CommandSelectSystem; | ||||
|   [Commands.centerSystem]: CommandCenterSystem; | ||||
|   [Commands.linkSignatureToSystem]: CommandLinkSignatureToSystem; | ||||
|   [Commands.signaturesUpdated]: CommandLinkSignaturesUpdated; | ||||
| } | ||||
|  | ||||
| export interface MapHandlers { | ||||
| @@ -107,6 +122,7 @@ export enum OutCommand { | ||||
|   updateConnectionMassStatus = 'update_connection_mass_status', | ||||
|   updateConnectionShipSizeType = 'update_connection_ship_size_type', | ||||
|   updateConnectionLocked = 'update_connection_locked', | ||||
|   updateConnectionCustomInfo = 'update_connection_custom_info', | ||||
|   updateSignatures = 'update_signatures', | ||||
|   updateSystemName = 'update_system_name', | ||||
|   updateSystemDescription = 'update_system_description', | ||||
| @@ -123,10 +139,16 @@ export enum OutCommand { | ||||
|   setAutopilotWaypoint = 'set_autopilot_waypoint', | ||||
|   addSystem = 'add_system', | ||||
|   addCharacter = 'add_character', | ||||
|   openUserSettings = 'open_user_settings', | ||||
|   getPassages = 'get_passages', | ||||
|   linkSignatureToSystem = 'link_signature_to_system', | ||||
|  | ||||
|   // Only UI commands | ||||
|   openSettings = 'open_settings', | ||||
|  | ||||
|   getUserSettings = 'get_user_settings', | ||||
|   updateUserSettings = 'update_user_settings', | ||||
|   unlinkSignature = 'unlink_signature', | ||||
| } | ||||
|  | ||||
| export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>; | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import { SolarSystemConnection } from '@/hooks/Mapper/types/connection.ts'; | ||||
|  | ||||
| export type MapUnionTypes = { | ||||
|   wormholesData: Record<string, WormholeDataRaw>; | ||||
|   wormholes: WormholeDataRaw[]; | ||||
|   effects: Record<string, EffectRaw>; | ||||
|   characters: CharacterTypeRaw[]; | ||||
|   userCharacters: string[]; | ||||
|   | ||||
| @@ -1,20 +1,13 @@ | ||||
| export type SystemSignature = { | ||||
|   eve_id: string; | ||||
|   kind: string; | ||||
|   name: string; | ||||
|   description?: string; | ||||
|   group: string; | ||||
|   updated_at?: string; | ||||
| }; | ||||
| import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types'; | ||||
|  | ||||
| export enum SignatureGroup { | ||||
|   CosmicSignature = 'Cosmic Signature', | ||||
|   Wormhole = 'Wormhole', | ||||
|   GasSite = 'Gas Site', | ||||
|   RelicSite = 'Relic Site', | ||||
|   DataSite = 'Data Site', | ||||
|   OreSite = 'Ore Site', | ||||
|   CombatSite = 'Combat Site', | ||||
|   Wormhole = 'Wormhole', | ||||
|   CosmicSignature = 'Cosmic Signature', | ||||
| } | ||||
|  | ||||
| export type GroupType = { | ||||
| @@ -23,3 +16,14 @@ export type GroupType = { | ||||
|   w: number; | ||||
|   h: number; | ||||
| }; | ||||
|  | ||||
| export type SystemSignature = { | ||||
|   eve_id: string; | ||||
|   kind: string; | ||||
|   name: string; | ||||
|   description?: string; | ||||
|   group: SignatureGroup; | ||||
|   type: string; | ||||
|   linked_system?: SolarSystemStaticInfoRaw; | ||||
|   updated_at?: string; | ||||
| }; | ||||
|   | ||||
| @@ -1,3 +1,9 @@ | ||||
| export enum Respawn { | ||||
|   static = 'static', | ||||
|   wandering = 'wandering', | ||||
|   reverse = 'reverse', | ||||
| } | ||||
|  | ||||
| export type WormholeDataRaw = { | ||||
|   dest: string; | ||||
|   id: number; | ||||
| @@ -5,7 +11,7 @@ export type WormholeDataRaw = { | ||||
|   mass_regen: number; | ||||
|   max_mass_per_jump: number; | ||||
|   name: string; | ||||
|   sibling_groups: any; | ||||
|   respawn: Respawn[]; | ||||
|   src: string[]; | ||||
|   static: boolean; | ||||
|   total_mass: number; | ||||
|   | ||||
							
								
								
									
										26
									
								
								assets/js/hooks/Mapper/utils/abstractContextProvider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								assets/js/hooks/Mapper/utils/abstractContextProvider.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import { createContext, ReactNode, useContext, useState } from 'react'; | ||||
|  | ||||
| type ContextType<T> = { | ||||
|   value: T; | ||||
|   setValue: (newValue: T) => void; | ||||
| }; | ||||
|  | ||||
| export const createGenericContext = <T,>() => { | ||||
|   const context = createContext<ContextType<T> | undefined>(undefined); | ||||
|  | ||||
|   const Provider = ({ children, initialValue }: { children: ReactNode; initialValue: T }) => { | ||||
|     const [value, setValue] = useState<T>(initialValue); | ||||
|  | ||||
|     return <context.Provider value={{ value, setValue }}>{children}</context.Provider>; | ||||
|   }; | ||||
|  | ||||
|   const useContextValue = () => { | ||||
|     const contextValue = useContext(context); | ||||
|     if (!contextValue) { | ||||
|       throw new Error('useContextValue must be used within a Provider'); | ||||
|     } | ||||
|     return contextValue; | ||||
|   }; | ||||
|  | ||||
|   return { Provider, useContextValue }; | ||||
| }; | ||||
| @@ -1,14 +0,0 @@ | ||||
| import pako from 'pako'; | ||||
|  | ||||
| export const decompressToJson = (base64string: string) => { | ||||
|   const base64_decoded = atob(base64string); | ||||
|   const charData = base64_decoded.split('').map(function (x) { | ||||
|     return x.charCodeAt(0); | ||||
|   }); | ||||
|   const zlibData = new Uint8Array(charData); | ||||
|   const inflatedData = pako.inflate(zlibData, { | ||||
|     to: 'string', | ||||
|   }); | ||||
|  | ||||
|   return JSON.parse(inflatedData); | ||||
| }; | ||||
| @@ -1,3 +1,2 @@ | ||||
| export * from './contextStore'; | ||||
| export * from './decompressToJson'; | ||||
| export * from './getQueryVariable'; | ||||
|   | ||||
| @@ -65,7 +65,7 @@ export class LabelsManager { | ||||
|   } | ||||
|  | ||||
|   hasLabel(label: string) { | ||||
|     return this.parsedLabels.labels.includes(label); | ||||
|     return this.parsedLabels.labels?.includes(label); | ||||
|   } | ||||
|  | ||||
|   toggleLabel(label: string) { | ||||
|   | ||||
| @@ -3,7 +3,230 @@ import 'phoenix_html'; | ||||
|  | ||||
| import './live_reload.css'; | ||||
|  | ||||
| const animateBg = function (bgCanvas) { | ||||
|   const { TweenMax, _ } = window; | ||||
|   /** | ||||
|    * Utility function for returning a random integer in a given range | ||||
|    * @param {Int} max | ||||
|    * @param {Int} min | ||||
|    */ | ||||
|   const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min; | ||||
|   const BASE_SIZE = 1; | ||||
|   const VELOCITY_INC = 1.01; | ||||
|   const VELOCITY_INIT_INC = 0.525; | ||||
|   const JUMP_VELOCITY_INC = 0.55; | ||||
|   const JUMP_SIZE_INC = 1.15; | ||||
|   const SIZE_INC = 1.01; | ||||
|   const RAD = Math.PI / 180; | ||||
|   const WARP_COLORS = [ | ||||
|     [197, 239, 247], | ||||
|     [25, 181, 254], | ||||
|     [77, 5, 232], | ||||
|     [165, 55, 253], | ||||
|     [255, 255, 255], | ||||
|   ]; | ||||
|   /** | ||||
|    * Class for storing the particle metadata | ||||
|    * position, size, length, speed etc. | ||||
|    */ | ||||
|   class Star { | ||||
|     STATE = { | ||||
|       alpha: Math.random(), | ||||
|       angle: randomInRange(0, 360) * RAD, | ||||
|     }; | ||||
|     reset = () => { | ||||
|       const angle = randomInRange(0, 360) * (Math.PI / 180); | ||||
|       const vX = Math.cos(angle); | ||||
|       const vY = Math.sin(angle); | ||||
|       const travelled = | ||||
|         Math.random() > 0.5 | ||||
|           ? Math.random() * Math.max(window.innerWidth, window.innerHeight) + Math.random() * (window.innerWidth * 0.24) | ||||
|           : Math.random() * (window.innerWidth * 0.25); | ||||
|       this.STATE = { | ||||
|         ...this.STATE, | ||||
|         iX: undefined, | ||||
|         iY: undefined, | ||||
|         active: travelled ? true : false, | ||||
|         x: Math.floor(vX * travelled) + window.innerWidth / 2, | ||||
|         vX, | ||||
|         y: Math.floor(vY * travelled) + window.innerHeight / 2, | ||||
|         vY, | ||||
|         size: BASE_SIZE, | ||||
|       }; | ||||
|     }; | ||||
|     constructor() { | ||||
|       this.reset(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const generateStarPool = size => new Array(size).fill().map(() => new Star()); | ||||
|  | ||||
|   // Class for the actual app | ||||
|   // Not too much happens in here | ||||
|   // Initiate the drawing process and listen for user interactions 👍 | ||||
|   class JumpToHyperspace { | ||||
|     STATE = { | ||||
|       stars: generateStarPool(300), | ||||
|       bgAlpha: 0, | ||||
|       sizeInc: SIZE_INC, | ||||
|       velocity: VELOCITY_INC, | ||||
|     }; | ||||
|     canvas = null; | ||||
|     context = null; | ||||
|     constructor(canvas) { | ||||
|       this.canvas = canvas; | ||||
|       this.context = canvas.getContext('2d'); | ||||
|       this.bind(); | ||||
|       this.setup(); | ||||
|       this.render(); | ||||
|     } | ||||
|     render = () => { | ||||
|       const { | ||||
|         STATE: { bgAlpha, velocity, sizeInc, initiating, jumping, stars }, | ||||
|         context, | ||||
|         render, | ||||
|       } = this; | ||||
|       // Clear the canvas | ||||
|       context.clearRect(0, 0, window.innerWidth, window.innerHeight); | ||||
|       if (bgAlpha > 0) { | ||||
|         context.fillStyle = `rgba(31, 58, 157, ${bgAlpha})`; | ||||
|         context.fillRect(0, 0, window.innerWidth, window.innerHeight); | ||||
|       } | ||||
|       // 1. Shall we add a new star | ||||
|       const nonActive = stars.filter(s => !s.STATE.active); | ||||
|       if (!initiating && nonActive.length > 0) { | ||||
|         // Introduce a star | ||||
|         nonActive[0].STATE.active = true; | ||||
|       } | ||||
|       // 2. Update the stars and draw them. | ||||
|       for (const star of stars.filter(s => s.STATE.active)) { | ||||
|         const { active, x, y, iX, iY, iVX, iVY, size, vX, vY } = star.STATE; | ||||
|         // Check if the star needs deactivating | ||||
|         if ( | ||||
|           ((iX || x) < 0 || (iX || x) > window.innerWidth || (iY || y) < 0 || (iY || y) > window.innerHeight) && | ||||
|           active && | ||||
|           !initiating | ||||
|         ) { | ||||
|           star.reset(true); | ||||
|         } else if (active) { | ||||
|           const newIX = initiating ? iX : iX + iVX; | ||||
|           const newIY = initiating ? iY : iY + iVY; | ||||
|           const newX = x + vX; | ||||
|           const newY = y + vY; | ||||
|           // Just need to work out if it overtakes the original line that's all | ||||
|           const caught = | ||||
|             (vX < 0 && newIX < x) || (vX > 0 && newIX > x) || (vY < 0 && newIY < y) || (vY > 0 && newIY > y); | ||||
|           star.STATE = { | ||||
|             ...star.STATE, | ||||
|             iX: caught ? undefined : newIX, | ||||
|             iY: caught ? undefined : newIY, | ||||
|             iVX: caught ? undefined : iVX * VELOCITY_INIT_INC, | ||||
|             iVY: caught ? undefined : iVY * VELOCITY_INIT_INC, | ||||
|             x: newX, | ||||
|             vX: star.STATE.vX * velocity, | ||||
|             y: newY, | ||||
|             vY: star.STATE.vY * velocity, | ||||
|             size: initiating ? size : size * (iX || iY ? SIZE_INC : sizeInc), | ||||
|           }; | ||||
|           let color = `rgba(255, 255, 255, ${star.STATE.alpha})`; | ||||
|           if (jumping) { | ||||
|             const [r, g, b] = WARP_COLORS[randomInRange(0, WARP_COLORS.length)]; | ||||
|             color = `rgba(${r}, ${g}, ${b}, ${star.STATE.alpha})`; | ||||
|           } | ||||
|           context.strokeStyle = color; | ||||
|           context.lineWidth = size; | ||||
|           context.beginPath(); | ||||
|           context.moveTo(star.STATE.iX || x, star.STATE.iY || y); | ||||
|           context.lineTo(star.STATE.x, star.STATE.y); | ||||
|           context.stroke(); | ||||
|         } | ||||
|       } | ||||
|       requestAnimationFrame(render); | ||||
|     }; | ||||
|     initiate = () => { | ||||
|       if (this.STATE.jumping || this.STATE.initiating) return; | ||||
|       this.STATE = { | ||||
|         ...this.STATE, | ||||
|         initiating: true, | ||||
|         initiateTimestamp: new Date().getTime(), | ||||
|       }; | ||||
|       TweenMax.to(this.STATE, 0.25, { velocity: VELOCITY_INIT_INC, bgAlpha: 0.3 }); | ||||
|       // When we initiate, stop the XY origin from moving so that we draw | ||||
|       // longer lines until the jump | ||||
|       for (const star of this.STATE.stars.filter(s => s.STATE.active)) { | ||||
|         star.STATE = { | ||||
|           ...star.STATE, | ||||
|           iX: star.STATE.x, | ||||
|           iY: star.STATE.y, | ||||
|           iVX: star.STATE.vX, | ||||
|           iVY: star.STATE.vY, | ||||
|         }; | ||||
|       } | ||||
|     }; | ||||
|     jump = () => { | ||||
|       this.STATE = { | ||||
|         ...this.STATE, | ||||
|         bgAlpha: 0, | ||||
|         jumping: true, | ||||
|       }; | ||||
|       TweenMax.to(this.STATE, 0.25, { velocity: JUMP_VELOCITY_INC, bgAlpha: 0.75, sizeInc: JUMP_SIZE_INC }); | ||||
|       setTimeout(() => { | ||||
|         this.STATE = { | ||||
|           ...this.STATE, | ||||
|           jumping: false, | ||||
|         }; | ||||
|         TweenMax.to(this.STATE, 0.25, { bgAlpha: 0, velocity: VELOCITY_INC, sizeInc: SIZE_INC }); | ||||
|       }, 5000); | ||||
|     }; | ||||
|     enter = () => { | ||||
|       if (this.STATE.jumping) return; | ||||
|       const { initiateTimestamp } = this.STATE; | ||||
|       this.STATE = { | ||||
|         ...this.STATE, | ||||
|         initiating: false, | ||||
|         initiateTimestamp: undefined, | ||||
|       }; | ||||
|       if (new Date().getTime() - initiateTimestamp > 600) { | ||||
|         this.jump(); | ||||
|       } else { | ||||
|         TweenMax.to(this.STATE, 0.25, { velocity: VELOCITY_INC, bgAlpha: 0 }); | ||||
|       } | ||||
|     }; | ||||
|     bind = () => { | ||||
|       this.canvas.addEventListener('mousedown', this.initiate); | ||||
|       this.canvas.addEventListener('touchstart', this.initiate); | ||||
|       this.canvas.addEventListener('mouseup', this.enter); | ||||
|       this.canvas.addEventListener('touchend', this.enter); | ||||
|     }; | ||||
|     setup = () => { | ||||
|       this.context.lineCap = 'round'; | ||||
|       this.canvas.height = window.innerHeight; | ||||
|       this.canvas.width = window.innerWidth; | ||||
|     }; | ||||
|     reset = () => { | ||||
|       this.STATE = { | ||||
|         ...this.STATE, | ||||
|         stars: generateStarPool(300), | ||||
|       }; | ||||
|       this.setup(); | ||||
|     }; | ||||
|   } | ||||
|   window.myJump = new JumpToHyperspace(bgCanvas); | ||||
|   window.addEventListener( | ||||
|     'resize', | ||||
|     _.debounce(() => { | ||||
|       window.myJump.reset(); | ||||
|     }, 250), | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| document.addEventListener('DOMContentLoaded', function () { | ||||
|   // animage background | ||||
|   const canvas = document.getElementById('bg-canvas'); | ||||
|   if (canvas) { | ||||
|     animateBg(canvas); | ||||
|   } | ||||
|  | ||||
|   // Select all buttons with the 'share-link' class | ||||
|   const buttons = document.querySelectorAll('button.copy-link'); | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|     "live_select": "file:../deps/live_select", | ||||
|     "lodash.debounce": "^4.0.8", | ||||
|     "lodash.isequal": "^4.5.0", | ||||
|     "pako": "^2.1.0", | ||||
|     "phoenix": "file:../deps/phoenix", | ||||
|     "phoenix_html": "file:../deps/phoenix_html", | ||||
|     "phoenix_live_view": "file:../deps/phoenix_live_view", | ||||
| @@ -29,8 +28,10 @@ | ||||
|     "primeicons": "^7.0.0", | ||||
|     "primereact": "^10.6.5", | ||||
|     "react-error-boundary": "^4.0.13", | ||||
|     "react-event-hook": "^3.1.2", | ||||
|     "react-flow-renderer": "^10.3.17", | ||||
|     "react-grid-layout": "^1.3.4", | ||||
|     "react-hook-form": "^7.53.1", | ||||
|     "react-usestateref": "^1.0.9", | ||||
|     "reactflow": "^11.10.4", | ||||
|     "rxjs": "^7.8.1", | ||||
| @@ -44,7 +45,6 @@ | ||||
|     "@tailwindcss/typography": "^0.5.13", | ||||
|     "@types/lodash.debounce": "^4.0.9", | ||||
|     "@types/lodash.isequal": "^4.5.8", | ||||
|     "@types/pako": "^2.0.3", | ||||
|     "@types/react": "18.2.0", | ||||
|     "@types/react-dom": "18.2.1", | ||||
|     "@types/react-grid-layout": "^1.3.4", | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/static/images/amarr-180.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/images/amarr-180.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 187 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/static/images/caldaria-180.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/images/caldaria-180.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 109 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/static/images/gallente-180.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/images/gallente-180.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 205 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/static/images/mataria-180.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/images/mataria-180.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 262 KiB | 
							
								
								
									
										354
									
								
								assets/yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										354
									
								
								assets/yarn.lock
									
									
									
									
									
								
							| @@ -33,7 +33,7 @@ | ||||
|   resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz" | ||||
|   integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== | ||||
|  | ||||
| "@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.14.8", "@babel/core@^7.24.5": | ||||
| "@babel/core@^7.14.8", "@babel/core@^7.24.5": | ||||
|   version "7.24.5" | ||||
|   resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz" | ||||
|   integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== | ||||
| @@ -240,11 +240,121 @@ | ||||
|     "@babel/helper-validator-identifier" "^7.24.5" | ||||
|     to-fast-properties "^2.0.0" | ||||
|  | ||||
| "@esbuild/aix-ppc64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" | ||||
|   integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== | ||||
|  | ||||
| "@esbuild/android-arm64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" | ||||
|   integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== | ||||
|  | ||||
| "@esbuild/android-arm@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" | ||||
|   integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== | ||||
|  | ||||
| "@esbuild/android-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" | ||||
|   integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== | ||||
|  | ||||
| "@esbuild/darwin-arm64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz" | ||||
|   integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== | ||||
|  | ||||
| "@esbuild/darwin-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" | ||||
|   integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== | ||||
|  | ||||
| "@esbuild/freebsd-arm64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" | ||||
|   integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== | ||||
|  | ||||
| "@esbuild/freebsd-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" | ||||
|   integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== | ||||
|  | ||||
| "@esbuild/linux-arm64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" | ||||
|   integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== | ||||
|  | ||||
| "@esbuild/linux-arm@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" | ||||
|   integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== | ||||
|  | ||||
| "@esbuild/linux-ia32@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" | ||||
|   integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== | ||||
|  | ||||
| "@esbuild/linux-loong64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" | ||||
|   integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== | ||||
|  | ||||
| "@esbuild/linux-mips64el@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" | ||||
|   integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== | ||||
|  | ||||
| "@esbuild/linux-ppc64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" | ||||
|   integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== | ||||
|  | ||||
| "@esbuild/linux-riscv64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" | ||||
|   integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== | ||||
|  | ||||
| "@esbuild/linux-s390x@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" | ||||
|   integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== | ||||
|  | ||||
| "@esbuild/linux-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" | ||||
|   integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== | ||||
|  | ||||
| "@esbuild/netbsd-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" | ||||
|   integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== | ||||
|  | ||||
| "@esbuild/openbsd-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" | ||||
|   integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== | ||||
|  | ||||
| "@esbuild/sunos-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" | ||||
|   integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== | ||||
|  | ||||
| "@esbuild/win32-arm64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" | ||||
|   integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== | ||||
|  | ||||
| "@esbuild/win32-ia32@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" | ||||
|   integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== | ||||
|  | ||||
| "@esbuild/win32-x64@0.20.2": | ||||
|   version "0.20.2" | ||||
|   resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" | ||||
|   integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== | ||||
|  | ||||
| "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": | ||||
|   version "4.4.0" | ||||
|   resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" | ||||
| @@ -341,7 +451,7 @@ | ||||
|     "@nodelib/fs.stat" "2.0.5" | ||||
|     run-parallel "^1.1.9" | ||||
|  | ||||
| "@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": | ||||
| "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": | ||||
|   version "2.0.5" | ||||
|   resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" | ||||
|   integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== | ||||
| @@ -359,7 +469,7 @@ | ||||
|   resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz" | ||||
|   integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== | ||||
|  | ||||
| "@react-rxjs/core@^0.10.7", "@react-rxjs/core@>=0.1.0": | ||||
| "@react-rxjs/core@^0.10.7": | ||||
|   version "0.10.7" | ||||
|   resolved "https://registry.npmjs.org/@react-rxjs/core/-/core-0.10.7.tgz" | ||||
|   integrity sha512-dornp8pUs9OcdqFKKRh9+I2FVe21gWufNun6RYU1ddts7kUy9i4Thvl0iqcPFbGY61cJQMAJF7dxixWMSD/A/A== | ||||
| @@ -455,11 +565,91 @@ | ||||
|     estree-walker "^2.0.2" | ||||
|     picomatch "^2.3.1" | ||||
|  | ||||
| "@rollup/rollup-android-arm-eabi@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz#1a32112822660ee104c5dd3a7c595e26100d4c2d" | ||||
|   integrity sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ== | ||||
|  | ||||
| "@rollup/rollup-android-arm64@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz#5aeef206d65ff4db423f3a93f71af91b28662c5b" | ||||
|   integrity sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw== | ||||
|  | ||||
| "@rollup/rollup-darwin-arm64@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz" | ||||
|   integrity sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw== | ||||
|  | ||||
| "@rollup/rollup-darwin-x64@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz#f64fc51ed12b19f883131ccbcea59fc68cbd6c0b" | ||||
|   integrity sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ== | ||||
|  | ||||
| "@rollup/rollup-linux-arm-gnueabihf@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz#1a7641111be67c10111f7122d1e375d1226cbf14" | ||||
|   integrity sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A== | ||||
|  | ||||
| "@rollup/rollup-linux-arm-musleabihf@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz#c93fd632923e0fee25aacd2ae414288d0b7455bb" | ||||
|   integrity sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg== | ||||
|  | ||||
| "@rollup/rollup-linux-arm64-gnu@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz#fa531425dd21d058a630947527b4612d9d0b4a4a" | ||||
|   integrity sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A== | ||||
|  | ||||
| "@rollup/rollup-linux-arm64-musl@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz#8acc16f095ceea5854caf7b07e73f7d1802ac5af" | ||||
|   integrity sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA== | ||||
|  | ||||
| "@rollup/rollup-linux-powerpc64le-gnu@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz#94e69a8499b5cf368911b83a44bb230782aeb571" | ||||
|   integrity sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ== | ||||
|  | ||||
| "@rollup/rollup-linux-riscv64-gnu@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz#7ef1c781c7e59e85a6ce261cc95d7f1e0b56db0f" | ||||
|   integrity sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg== | ||||
|  | ||||
| "@rollup/rollup-linux-s390x-gnu@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz#f15775841c3232fca9b78cd25a7a0512c694b354" | ||||
|   integrity sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g== | ||||
|  | ||||
| "@rollup/rollup-linux-x64-gnu@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz#b521d271798d037ad70c9f85dd97d25f8a52e811" | ||||
|   integrity sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ== | ||||
|  | ||||
| "@rollup/rollup-linux-x64-gnu@4.9.5": | ||||
|   version "4.9.5" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz#85946ee4d068bd12197aeeec2c6f679c94978a49" | ||||
|   integrity sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA== | ||||
|  | ||||
| "@rollup/rollup-linux-x64-musl@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz#9254019cc4baac35800991315d133cc9fd1bf385" | ||||
|   integrity sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q== | ||||
|  | ||||
| "@rollup/rollup-win32-arm64-msvc@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz#27f65a89f6f52ee9426ec11e3571038e4671790f" | ||||
|   integrity sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA== | ||||
|  | ||||
| "@rollup/rollup-win32-ia32-msvc@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz#a2fbf8246ed0bb014f078ca34ae6b377a90cb411" | ||||
|   integrity sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ== | ||||
|  | ||||
| "@rollup/rollup-win32-x64-msvc@4.17.2": | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503" | ||||
|   integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w== | ||||
|  | ||||
| "@rx-state/core@0.1.4": | ||||
|   version "0.1.4" | ||||
|   resolved "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz" | ||||
| @@ -740,7 +930,7 @@ | ||||
|     "@types/d3-transition" "*" | ||||
|     "@types/d3-zoom" "*" | ||||
|  | ||||
| "@types/estree@*", "@types/estree@^1.0.0", "@types/estree@1.0.5": | ||||
| "@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": | ||||
|   version "1.0.5" | ||||
|   resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" | ||||
|   integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== | ||||
| @@ -774,11 +964,6 @@ | ||||
|   resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz" | ||||
|   integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== | ||||
|  | ||||
| "@types/pako@^2.0.3": | ||||
|   version "2.0.3" | ||||
|   resolved "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz" | ||||
|   integrity sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q== | ||||
|  | ||||
| "@types/prop-types@*": | ||||
|   version "15.7.11" | ||||
|   resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz" | ||||
| @@ -805,7 +990,7 @@ | ||||
|   dependencies: | ||||
|     "@types/react" "*" | ||||
|  | ||||
| "@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@>=16.8", "@types/react@18.2.0": | ||||
| "@types/react@*", "@types/react@18.2.0": | ||||
|   version "18.2.0" | ||||
|   resolved "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz" | ||||
|   integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== | ||||
| @@ -846,7 +1031,7 @@ | ||||
|     semver "^7.5.4" | ||||
|     ts-api-utils "^1.0.1" | ||||
|  | ||||
| "@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.21.0": | ||||
| "@typescript-eslint/parser@^6.21.0": | ||||
|   version "6.21.0" | ||||
|   resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz" | ||||
|   integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== | ||||
| @@ -947,7 +1132,7 @@ acorn-jsx@^5.3.2: | ||||
|   resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" | ||||
|   integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== | ||||
|  | ||||
| "acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.4.0, acorn@^8.9.0: | ||||
| acorn@^8.4.0, acorn@^8.9.0: | ||||
|   version "8.11.3" | ||||
|   resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" | ||||
|   integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== | ||||
| @@ -1147,7 +1332,7 @@ braces@^3.0.2, braces@~3.0.2: | ||||
|   dependencies: | ||||
|     fill-range "^7.0.1" | ||||
|  | ||||
| browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4.21.0": | ||||
| browserslist@^4.22.2, browserslist@^4.23.0: | ||||
|   version "4.23.0" | ||||
|   resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz" | ||||
|   integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== | ||||
| @@ -1205,7 +1390,7 @@ child_process@^1.0.2: | ||||
|   resolved "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz" | ||||
|   integrity sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g== | ||||
|  | ||||
| chokidar@^3.3.0, chokidar@^3.5.3, "chokidar@>=3.0.0 <4.0.0": | ||||
| "chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.5.3: | ||||
|   version "3.6.0" | ||||
|   resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" | ||||
|   integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== | ||||
| @@ -1258,16 +1443,16 @@ color-convert@^2.0.1: | ||||
|   dependencies: | ||||
|     color-name "~1.1.4" | ||||
|  | ||||
| color-name@~1.1.4: | ||||
|   version "1.1.4" | ||||
|   resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" | ||||
|   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== | ||||
|  | ||||
| color-name@1.1.3: | ||||
|   version "1.1.3" | ||||
|   resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" | ||||
|   integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== | ||||
|  | ||||
| color-name@~1.1.4: | ||||
|   version "1.1.4" | ||||
|   resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" | ||||
|   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== | ||||
|  | ||||
| commander@^4.0.0: | ||||
|   version "4.1.1" | ||||
|   resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" | ||||
| @@ -1325,7 +1510,7 @@ culori@^3: | ||||
|   resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz" | ||||
|   integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== | ||||
|  | ||||
| d3-drag@^3.0.0, "d3-drag@2 - 3": | ||||
| "d3-drag@2 - 3", d3-drag@^3.0.0: | ||||
|   version "3.0.0" | ||||
|   resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz" | ||||
|   integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== | ||||
| @@ -1345,7 +1530,7 @@ d3-drag@^3.0.0, "d3-drag@2 - 3": | ||||
|   dependencies: | ||||
|     d3-color "1 - 3" | ||||
|  | ||||
| d3-selection@^3.0.0, "d3-selection@2 - 3", d3-selection@3: | ||||
| "d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: | ||||
|   version "3.0.0" | ||||
|   resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz" | ||||
|   integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== | ||||
| @@ -1663,7 +1848,7 @@ escape-string-regexp@^4.0.0: | ||||
|   resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" | ||||
|   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== | ||||
|  | ||||
| eslint-config-prettier@*, eslint-config-prettier@^9.1.0: | ||||
| eslint-config-prettier@^9.1.0: | ||||
|   version "9.1.0" | ||||
|   resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz" | ||||
|   integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== | ||||
| @@ -1723,7 +1908,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 | ||||
|   resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" | ||||
|   integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== | ||||
|  | ||||
| "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.57.0, eslint@>=7, eslint@>=7.0.0, eslint@>=8.0.0: | ||||
| eslint@^8.57.0: | ||||
|   version "8.57.0" | ||||
|   resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" | ||||
|   integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== | ||||
| @@ -1795,12 +1980,7 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: | ||||
|   resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" | ||||
|   integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== | ||||
|  | ||||
| estree-walker@^2.0.1: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" | ||||
|   integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== | ||||
|  | ||||
| estree-walker@^2.0.2: | ||||
| estree-walker@^2.0.1, estree-walker@^2.0.2: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" | ||||
|   integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== | ||||
| @@ -2010,18 +2190,6 @@ glob-parent@^6.0.2: | ||||
|   dependencies: | ||||
|     is-glob "^4.0.3" | ||||
|  | ||||
| glob@^7.1.3: | ||||
|   version "7.2.3" | ||||
|   resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" | ||||
|   integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== | ||||
|   dependencies: | ||||
|     fs.realpath "^1.0.0" | ||||
|     inflight "^1.0.4" | ||||
|     inherits "2" | ||||
|     minimatch "^3.1.1" | ||||
|     once "^1.3.0" | ||||
|     path-is-absolute "^1.0.0" | ||||
|  | ||||
| glob@7.1.6: | ||||
|   version "7.1.6" | ||||
|   resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" | ||||
| @@ -2034,6 +2202,18 @@ glob@7.1.6: | ||||
|     once "^1.3.0" | ||||
|     path-is-absolute "^1.0.0" | ||||
|  | ||||
| glob@^7.1.3: | ||||
|   version "7.2.3" | ||||
|   resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" | ||||
|   integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== | ||||
|   dependencies: | ||||
|     fs.realpath "^1.0.0" | ||||
|     inflight "^1.0.4" | ||||
|     inherits "2" | ||||
|     minimatch "^3.1.1" | ||||
|     once "^1.3.0" | ||||
|     path-is-absolute "^1.0.0" | ||||
|  | ||||
| globals@^11.1.0: | ||||
|   version "11.12.0" | ||||
|   resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" | ||||
| @@ -2141,14 +2321,7 @@ hasown@^2.0.0: | ||||
|   dependencies: | ||||
|     function-bind "^1.1.2" | ||||
|  | ||||
| hasown@^2.0.1: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" | ||||
|   integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== | ||||
|   dependencies: | ||||
|     function-bind "^1.1.2" | ||||
|  | ||||
| hasown@^2.0.2: | ||||
| hasown@^2.0.1, hasown@^2.0.2: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" | ||||
|   integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== | ||||
| @@ -2420,7 +2593,7 @@ iterator.prototype@^1.1.2: | ||||
|     reflect.getprototypeof "^1.0.4" | ||||
|     set-function-name "^2.0.1" | ||||
|  | ||||
| jiti@^1.19.1, jiti@>=1.21.0: | ||||
| jiti@^1.19.1: | ||||
|   version "1.21.0" | ||||
|   resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz" | ||||
|   integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== | ||||
| @@ -2518,7 +2691,6 @@ lines-and-columns@^1.1.6: | ||||
|  | ||||
| "live_select@file:../deps/live_select": | ||||
|   version "1.4.2" | ||||
|   resolved "file:../deps/live_select" | ||||
|  | ||||
| locate-path@^6.0.0: | ||||
|   version "6.0.0" | ||||
| @@ -2612,13 +2784,6 @@ mini-svg-data-uri@^1.2.3: | ||||
|   resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz" | ||||
|   integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== | ||||
|  | ||||
| minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: | ||||
|   version "3.1.2" | ||||
|   resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" | ||||
|   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== | ||||
|   dependencies: | ||||
|     brace-expansion "^1.1.7" | ||||
|  | ||||
| minimatch@9.0.3: | ||||
|   version "9.0.3" | ||||
|   resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" | ||||
| @@ -2626,6 +2791,13 @@ minimatch@9.0.3: | ||||
|   dependencies: | ||||
|     brace-expansion "^2.0.1" | ||||
|  | ||||
| minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: | ||||
|   version "3.1.2" | ||||
|   resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" | ||||
|   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== | ||||
|   dependencies: | ||||
|     brace-expansion "^1.1.7" | ||||
|  | ||||
| ms@2.1.2: | ||||
|   version "2.1.2" | ||||
|   resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" | ||||
| @@ -2770,11 +2942,6 @@ p-locate@^5.0.0: | ||||
|   dependencies: | ||||
|     p-limit "^3.0.2" | ||||
|  | ||||
| pako@^2.1.0: | ||||
|   version "2.1.0" | ||||
|   resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" | ||||
|   integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== | ||||
|  | ||||
| parent-module@^1.0.0: | ||||
|   version "1.0.1" | ||||
|   resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" | ||||
| @@ -2812,17 +2979,14 @@ path-type@^5.0.0: | ||||
|   resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz" | ||||
|   integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== | ||||
|  | ||||
| "phoenix@file:../deps/phoenix": | ||||
|   version "1.7.14" | ||||
|  | ||||
| "phoenix_html@file:../deps/phoenix_html": | ||||
|   version "4.1.0" | ||||
|   resolved "file:../deps/phoenix_html" | ||||
|  | ||||
| "phoenix_live_view@file:../deps/phoenix_live_view": | ||||
|   version "0.20.17" | ||||
|   resolved "file:../deps/phoenix_live_view" | ||||
|  | ||||
| "phoenix@file:../deps/phoenix": | ||||
|   version "1.7.14" | ||||
|   resolved "file:../deps/phoenix" | ||||
|  | ||||
| picocolors@^1, picocolors@^1.0.0: | ||||
|   version "1.0.0" | ||||
| @@ -2923,14 +3087,6 @@ postcss-reporter@^7.0.0: | ||||
|     picocolors "^1.0.0" | ||||
|     thenby "^1.3.4" | ||||
|  | ||||
| postcss-selector-parser@^6.0.11: | ||||
|   version "6.0.13" | ||||
|   resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" | ||||
|   integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== | ||||
|   dependencies: | ||||
|     cssesc "^3.0.0" | ||||
|     util-deprecate "^1.0.2" | ||||
|  | ||||
| postcss-selector-parser@6.0.10: | ||||
|   version "6.0.10" | ||||
|   resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz" | ||||
| @@ -2939,12 +3095,20 @@ postcss-selector-parser@6.0.10: | ||||
|     cssesc "^3.0.0" | ||||
|     util-deprecate "^1.0.2" | ||||
|  | ||||
| postcss-selector-parser@^6.0.11: | ||||
|   version "6.0.13" | ||||
|   resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" | ||||
|   integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== | ||||
|   dependencies: | ||||
|     cssesc "^3.0.0" | ||||
|     util-deprecate "^1.0.2" | ||||
|  | ||||
| postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: | ||||
|   version "4.2.0" | ||||
|   resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" | ||||
|   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== | ||||
|  | ||||
| postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.38, postcss@>=8.0.9: | ||||
| postcss@^8.4.23, postcss@^8.4.38: | ||||
|   version "8.4.38" | ||||
|   resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz" | ||||
|   integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== | ||||
| @@ -2965,7 +3129,7 @@ prettier-linter-helpers@^1.0.0: | ||||
|   dependencies: | ||||
|     fast-diff "^1.1.2" | ||||
|  | ||||
| prettier@^3.2.5, prettier@>=3.0.0: | ||||
| prettier@^3.2.5: | ||||
|   version "3.2.5" | ||||
|   resolved "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz" | ||||
|   integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== | ||||
| @@ -2993,7 +3157,7 @@ primereact@^10.6.5: | ||||
|     "@types/react-transition-group" "^4.4.1" | ||||
|     react-transition-group "^4.4.1" | ||||
|  | ||||
| prop-types@^15.6.2, prop-types@^15.8.1, prop-types@15.x: | ||||
| prop-types@15.x, prop-types@^15.6.2, prop-types@^15.8.1: | ||||
|   version "15.8.1" | ||||
|   resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" | ||||
|   integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== | ||||
| @@ -3012,7 +3176,7 @@ queue-microtask@^1.2.2: | ||||
|   resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" | ||||
|   integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== | ||||
|  | ||||
| "react-dom@^17.0.0 || ^18.0.0", "react-dom@>= 16.3.0", react-dom@>=16.6.0, react-dom@>=17, react-dom@>=18, "react-dom@16 || 17 || 18", react-dom@18.2.0: | ||||
| react-dom@18.2.0: | ||||
|   version "18.2.0" | ||||
|   resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" | ||||
|   integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== | ||||
| @@ -3035,6 +3199,11 @@ react-error-boundary@^4.0.13: | ||||
|   dependencies: | ||||
|     "@babel/runtime" "^7.12.5" | ||||
|  | ||||
| react-event-hook@^3.1.2: | ||||
|   version "3.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/react-event-hook/-/react-event-hook-3.1.2.tgz#445e8f3b751f6abe4ef199f31bff47593c4c13d4" | ||||
|   integrity sha512-qQ9LXLdxmWRRZPlnqVjqlw7jovSvDosQEOyQ9cjPHhtDv8JIszjj0td1PuHJHrVW0LS8a1XeJhLe6i7S5u9SbQ== | ||||
|  | ||||
| react-flow-renderer@^10.3.17: | ||||
|   version "10.3.17" | ||||
|   resolved "https://registry.npmjs.org/react-flow-renderer/-/react-flow-renderer-10.3.17.tgz" | ||||
| @@ -3061,6 +3230,11 @@ react-grid-layout@^1.3.4: | ||||
|     react-resizable "^3.0.5" | ||||
|     resize-observer-polyfill "^1.5.1" | ||||
|  | ||||
| react-hook-form@^7.53.1: | ||||
|   version "7.53.1" | ||||
|   resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.1.tgz#3f2cd1ed2b3af99416a4ac674da2d526625add67" | ||||
|   integrity sha512-6aiQeBda4zjcuaugWvim9WsGqisoUk+etmFEsSUMm451/Ic8L/UAb7sRtMj3V+Hdzm6mMjU1VhiSzYUZeBm0Vg== | ||||
|  | ||||
| react-is@^16.13.1: | ||||
|   version "16.13.1" | ||||
|   resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" | ||||
| @@ -3099,7 +3273,7 @@ react-usestateref@^1.0.9: | ||||
|   resolved "https://registry.npmjs.org/react-usestateref/-/react-usestateref-1.0.9.tgz" | ||||
|   integrity sha512-t8KLsI7oje0HzfzGhxFXzuwbf1z9vhBM1ptHLUIHhYqZDKFuI5tzdhEVxSNzUkYxwF8XdpOErzHlKxvP7sTERw== | ||||
|  | ||||
| "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.2.0, "react@>= 16.3", "react@>= 16.3.0", react@>=16.13.1, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=17, react@>=18, react@>16.0.0, "react@16 || 17 || 18", react@18.2.0: | ||||
| react@18.2.0: | ||||
|   version "18.2.0" | ||||
|   resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" | ||||
|   integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== | ||||
| @@ -3215,7 +3389,7 @@ rollup-plugin-external-globals@^0.10.0: | ||||
|     is-reference "^3.0.2" | ||||
|     magic-string "^0.30.5" | ||||
|  | ||||
| rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, "rollup@^2.25.0 || ^3.3.0 || ^4.1.4", rollup@^4.13.0: | ||||
| rollup@^4.13.0: | ||||
|   version "4.17.2" | ||||
|   resolved "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz" | ||||
|   integrity sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ== | ||||
| @@ -3247,7 +3421,7 @@ run-parallel@^1.1.9: | ||||
|   dependencies: | ||||
|     queue-microtask "^1.2.2" | ||||
|  | ||||
| rxjs@^7.8.1, rxjs@>=6, rxjs@>=7: | ||||
| rxjs@^7.8.1: | ||||
|   version "7.8.1" | ||||
|   resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz" | ||||
|   integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== | ||||
| @@ -3280,7 +3454,7 @@ sass-loader@^14.2.1: | ||||
|   dependencies: | ||||
|     neo-async "^2.6.2" | ||||
|  | ||||
| sass@*, sass@^1.3.0, sass@^1.77.2: | ||||
| sass@^1.77.2: | ||||
|   version "1.77.2" | ||||
|   resolved "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz" | ||||
|   integrity sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA== | ||||
| @@ -3362,7 +3536,7 @@ slash@^5.0.0, slash@^5.1.0: | ||||
|   resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz" | ||||
|   integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== | ||||
|  | ||||
| source-map-js@^1.2.0, "source-map-js@>=0.6.2 <2.0.0": | ||||
| "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0: | ||||
|   version "1.2.0" | ||||
|   resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz" | ||||
|   integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== | ||||
| @@ -3479,7 +3653,7 @@ synckit@^0.8.6: | ||||
|     "@pkgr/core" "^0.1.0" | ||||
|     tslib "^2.6.2" | ||||
|  | ||||
| tailwindcss@^3.3.6, "tailwindcss@>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1", "tailwindcss@>=3.0.0 || >= 3.0.0-alpha.1", "tailwindcss@>=3.0.0 || insiders": | ||||
| tailwindcss@^3.3.6: | ||||
|   version "3.3.6" | ||||
|   resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.6.tgz" | ||||
|   integrity sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw== | ||||
| @@ -3619,7 +3793,7 @@ typed-array-length@^1.0.6: | ||||
|     is-typed-array "^1.1.13" | ||||
|     possible-typed-array-names "^1.0.0" | ||||
|  | ||||
| typescript@^5.2.2, typescript@>=4.2.0: | ||||
| typescript@^5.2.2: | ||||
|   version "5.4.5" | ||||
|   resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz" | ||||
|   integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== | ||||
| @@ -3664,7 +3838,7 @@ use-local-storage-state@^19.3.1: | ||||
|   resolved "https://registry.npmjs.org/use-local-storage-state/-/use-local-storage-state-19.3.1.tgz" | ||||
|   integrity sha512-y3Z1dODXvZXZB4qtLDNN8iuXbsYD6TAxz61K58GWB9/yKwrNG9ynI0GzCTHi/Je1rMiyOwMimz0oyFsZn+Kj7Q== | ||||
|  | ||||
| use-sync-external-store@^1.0.0, use-sync-external-store@1.2.0: | ||||
| use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0: | ||||
|   version "1.2.0" | ||||
|   resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" | ||||
|   integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== | ||||
| @@ -3692,7 +3866,7 @@ vite-plugin-externals@^0.6.2: | ||||
|     fs-extra "^10.0.0" | ||||
|     magic-string "^0.25.7" | ||||
|  | ||||
| "vite@^4.2.0 || ^5.0.0", vite@^5.0.5, vite@>=2.0.0: | ||||
| vite@^5.0.5: | ||||
|   version "5.2.11" | ||||
|   resolved "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz" | ||||
|   integrity sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ== | ||||
|   | ||||
| @@ -60,15 +60,7 @@ config :dart_sass, :version, "1.54.5" | ||||
|  | ||||
| config :tailwind, :version, "3.2.7" | ||||
|  | ||||
| config :wanderer_app, WandererApp.PromEx, | ||||
|   manual_metrics_start_delay: :no_delay, | ||||
|   metrics_server: [ | ||||
|     port: 4021, | ||||
|     path: "/metrics", | ||||
|     protocol: :http, | ||||
|     pool_size: 5, | ||||
|     cowboy_opts: [ip: {0, 0, 0, 0}] | ||||
|   ] | ||||
| config :wanderer_app, WandererApp.PromEx, manual_metrics_start_delay: :no_delay | ||||
|  | ||||
| config :wanderer_app, | ||||
|   grafana_datasource_id: "wanderer" | ||||
|   | ||||
| @@ -55,7 +55,6 @@ config :wanderer_app, WandererAppWeb.Endpoint, | ||||
| config :wanderer_app, WandererAppWeb.Endpoint, | ||||
|   live_reload: [ | ||||
|     interval: 1000, | ||||
|     web_console_logger: true, | ||||
|     patterns: [ | ||||
|       ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", | ||||
|       ~r"priv/gettext/.*(po)$", | ||||
|   | ||||
| @@ -55,11 +55,11 @@ map_subscriptions_enabled = | ||||
|  | ||||
| map_subscription_characters_limit = | ||||
|   config_dir | ||||
|   |> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 100) | ||||
|   |> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 10_000) | ||||
|  | ||||
| map_subscription_hubs_limit = | ||||
|   config_dir | ||||
|   |> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 10) | ||||
|   |> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 100) | ||||
|  | ||||
| wallet_tracking_enabled = | ||||
|   config_dir | ||||
| @@ -77,7 +77,7 @@ config :wanderer_app, | ||||
|   web_app_url: web_app_url, | ||||
|   git_sha: System.get_env("GIT_SHA", "111"), | ||||
|   custom_route_base_url: System.get_env("CUSTOM_ROUTE_BASE_URL"), | ||||
|   invites: System.get_env("WANDERER_INVITES", "false") == "true", | ||||
|   invites: System.get_env("WANDERER_INVITES", "false") |> String.to_existing_atom(), | ||||
|   admin_username: System.get_env("WANDERER_ADMIN_USERNAME", "admin"), | ||||
|   admin_password: System.get_env("WANDERER_ADMIN_PASSWORD"), | ||||
|   admins: admins, | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user