From 0a44a1403e236de35e89f2a5b303c19fcc07801f Mon Sep 17 00:00:00 2001 From: Drew Bonasera Date: Fri, 1 May 2026 06:57:17 -0400 Subject: [PATCH] Adds an option to ignore return wormholes for auto-indexing and use a custom symbol instead. This also fixes chained index formatting for system auto-tags and labels. - feat(bookmarks): Add settings to ignore return holes and use a custom symbol for auto-indexing - feat(settings): Add a `dependsOn` property to conditionally render settings - fix(systems): Correctly apply chained index formats for auto-tag and custom label - refactor(bookmarks): Pass target system info to `handleAutoBookmark` for return hole detection --- .../hooks/useLinkSignature.ts | 121 +++++++++--------- .../MapSettings/MapSettingsProvider.tsx | 7 + .../components/MapSettings/constants.ts | 15 +++ .../components/MapSettings/types.ts | 5 + .../SignatureSettings/SignatureSettings.tsx | 5 + .../Mapper/helpers/bookmarkFormatHelper.ts | 72 +++++++++-- .../repositories/map_user_settings_repo.ex | 4 +- .../event_handlers/map_core_event_handler.ex | 4 +- 8 files changed, 163 insertions(+), 70 deletions(-) diff --git a/assets/js/hooks/Mapper/components/mapInterface/components/SystemLinkSignatureDialog/hooks/useLinkSignature.ts b/assets/js/hooks/Mapper/components/mapInterface/components/SystemLinkSignatureDialog/hooks/useLinkSignature.ts index 937aff0f..bdb9a211 100644 --- a/assets/js/hooks/Mapper/components/mapInterface/components/SystemLinkSignatureDialog/hooks/useLinkSignature.ts +++ b/assets/js/hooks/Mapper/components/mapInterface/components/SystemLinkSignatureDialog/hooks/useLinkSignature.ts @@ -36,6 +36,10 @@ export const useLinkSignature = ({ data, targetSystemClassGroup }: UseLinkSignat const sourceSystem = systems.find((s: any) => s.system_static_info?.solar_system_id === data.solar_system_source); const systemUuid = sourceSystem?.id || data.solar_system_source.toString(); + const targetSystem = systems.find((s: any) => s.system_static_info?.solar_system_id === data.solar_system_target); + const targetSystemUuid = targetSystem?.id; + const targetSolarSystemIdStr = data.solar_system_target?.toString(); + const signatureToLink = { ...signature, group: SignatureGroup.Wormhole }; const { updatedSignature, shouldUpdate } = await handleAutoBookmark( @@ -46,6 +50,8 @@ export const useLinkSignature = ({ data, targetSystemClassGroup }: UseLinkSignat data.solar_system_source.toString(), wormholesData, targetSystemClassGroup, + targetSystemUuid, + targetSolarSystemIdStr, ); if (shouldUpdate) { @@ -73,69 +79,70 @@ export const useLinkSignature = ({ data, targetSystemClassGroup }: UseLinkSignat if (systemAutoTag || systemCustomLabelName) { const info = parseSignatureCustomInfo(updatedSignature.custom_info); - const bIndex = info.bookmark_index ?? 0; - const startAtZero = userSettings?.bookmark_wormholes_start_at_zero; - const letter = numberToLetters(bIndex, startAtZero); - const targetSystem = systems.find( - (s: any) => s.system_static_info?.solar_system_id === data.solar_system_target, - ); + if (info.bookmark_index !== undefined) { + const bIndex = info.bookmark_index; + const startAtZero = userSettings?.bookmark_wormholes_start_at_zero; + const letter = numberToLetters(bIndex, startAtZero); - if (targetSystem) { - if (systemAutoTag) { - let tagValue = ''; - switch (systemAutoTag) { - case 'index': - case 'chain_index': - tagValue = bIndex.toString(); - break; - case 'index_letter': - tagValue = letter; - break; - case 'chain_index_letters': - tagValue = info.bookmark_index_chained_letters === letter ? letter : bIndex.toString(); - break; + if (targetSystem) { + if (systemAutoTag) { + let tagValue = ''; + switch (systemAutoTag) { + case 'index': + tagValue = bIndex.toString(); + break; + case 'chain_index': + tagValue = (info.bookmark_index_chained as string) || bIndex.toString(); + break; + case 'index_letter': + tagValue = letter; + break; + case 'chain_index_letters': + tagValue = (info.bookmark_index_chained_letters as string) || letter; + break; + } + + if (tagValue) { + await outCommand({ + type: OutCommand.updateSystemTag, + data: { + system_id: targetSystem.id, + value: tagValue, + }, + }); + } } - if (tagValue) { - await outCommand({ - type: OutCommand.updateSystemTag, - data: { - system_id: targetSystem.id, - value: tagValue, - }, - }); - } - } + if (systemCustomLabelName) { + let labelValue = ''; + switch (systemCustomLabelName) { + case 'index': + labelValue = bIndex.toString(); + break; + case 'index_letter': + labelValue = letter; + break; + case 'chain_index': + labelValue = (info.bookmark_index_chained as string) || bIndex.toString(); + break; + case 'chain_index_letters': + labelValue = (info.bookmark_index_chained_letters as string) || letter; + break; + } - if (systemCustomLabelName) { - let labelValue = ''; - switch (systemCustomLabelName) { - case 'index': - labelValue = bIndex.toString(); - break; - case 'index_letter': - labelValue = letter; - break; - case 'chain_index': - labelValue = (info.bookmark_index_chained as string) || bIndex.toString(); - break; - case 'chain_index_letters': - labelValue = (info.bookmark_index_chained_letters as string) || letter; - break; - } + if (labelValue) { + const outLabel = new LabelsManager(targetSystem.labels ?? ''); + outLabel.updateCustomLabel(labelValue); - if (labelValue) { - const outLabel = new LabelsManager(targetSystem.labels ?? ''); - outLabel.updateCustomLabel(labelValue); - - await outCommand({ - type: OutCommand.updateSystemLabels, - data: { - system_id: targetSystem.id, - value: outLabel.toString(), - }, - }); + await outCommand({ + type: OutCommand.updateSystemLabels, + data: { + system_id: targetSystem.id, + value: outLabel.toString(), + }, + }); + } } } } diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/MapSettingsProvider.tsx b/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/MapSettingsProvider.tsx index df106003..2a05f55b 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/MapSettingsProvider.tsx +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/MapSettingsProvider.tsx @@ -80,6 +80,13 @@ export const MapSettingsProvider = ({ children }: WithChildren) => { const renderSettingItem = useCallback( (item: SettingsListItem) => { + if (item.dependsOn) { + const dependsOnValue = refVars.current.mergedSettings[item.dependsOn]; + if (!dependsOnValue) { + return null; + } + } + const currentValue = refVars.current.mergedSettings[item.prop]; if (item.type === 'checkbox') { diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/constants.ts b/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/constants.ts index 47ea2191..078a08b9 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/constants.ts +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/constants.ts @@ -13,6 +13,8 @@ export const DEFAULT_REMOTE_SETTINGS = { [UserSettingsRemoteProps.bookmark_auto_temp_name]: '', [UserSettingsRemoteProps.system_auto_tag]: '', [UserSettingsRemoteProps.system_custom_label_name]: '', + [UserSettingsRemoteProps.bookmark_return_hole_ignore]: false, + [UserSettingsRemoteProps.bookmark_return_hole_symbol]: '', }; export const AUTO_FORMAT_OPTIONS = [ @@ -34,6 +36,8 @@ export const UserSettingsRemoteList = [ UserSettingsRemoteProps.bookmark_auto_temp_name, UserSettingsRemoteProps.system_auto_tag, UserSettingsRemoteProps.system_custom_label_name, + UserSettingsRemoteProps.bookmark_return_hole_ignore, + UserSettingsRemoteProps.bookmark_return_hole_symbol, ]; // export const COMMON_CHECKBOXES_PROPS: SettingsListItem[] = [ @@ -81,6 +85,17 @@ export const BOOKMARKS_SETTINGS_PROPS: SettingsListItem[] = [ label: 'Start wormhole indices at 0', type: 'checkbox', }, + { + prop: UserSettingsRemoteProps.bookmark_return_hole_ignore, + label: 'Ignore return hole when creating indexes', + type: 'checkbox', + }, + { + prop: UserSettingsRemoteProps.bookmark_return_hole_symbol, + label: 'Return hole symbol (use space for empty)', + type: 'text', + dependsOn: UserSettingsRemoteProps.bookmark_return_hole_ignore, + }, { prop: UserSettingsRemoteProps.bookmark_auto_temp_name, label: 'Auto-fill wormhole temporary name', diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/types.ts b/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/types.ts index 64d9c679..196814fb 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/types.ts +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/MapSettings/types.ts @@ -11,6 +11,8 @@ export enum UserSettingsRemoteProps { bookmark_auto_temp_name = 'bookmark_auto_temp_name', system_auto_tag = 'system_auto_tag', system_custom_label_name = 'system_custom_label_name', + bookmark_return_hole_ignore = 'bookmark_return_hole_ignore', + bookmark_return_hole_symbol = 'bookmark_return_hole_symbol', } export type UserSettingsRemote = { @@ -24,6 +26,8 @@ export type UserSettingsRemote = { bookmark_auto_temp_name: string; system_auto_tag: string; system_custom_label_name: string; + bookmark_return_hole_ignore: boolean; + bookmark_return_hole_symbol: string; }; export type UserSettings = UserSettingsRemote & InterfaceStoredSettings; @@ -35,4 +39,5 @@ export type SettingsListItem = { options?: { label: string; value: string }[]; placeholder?: string; helperText?: string; + dependsOn?: keyof UserSettings; }; diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/SignatureSettings/SignatureSettings.tsx b/assets/js/hooks/Mapper/components/mapRootContent/components/SignatureSettings/SignatureSettings.tsx index 84399140..0c36f236 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/SignatureSettings/SignatureSettings.tsx +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/SignatureSettings/SignatureSettings.tsx @@ -129,6 +129,9 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map const targetSystemClassGroup = targetSystem?.system_static_info ? getSystemClassGroup(targetSystem.system_static_info.system_class) : null; + const targetSystemUuid = targetSystem?.id; + const targetSolarSystemIdStr = + targetSystem?.system_static_info?.solar_system_id?.toString() || values.linked_system; const currentSystem = systems.find((s: any) => s.id === systemId); const solarSystemIdStr = currentSystem?.system_static_info?.solar_system_id?.toString() || systemId; @@ -141,6 +144,8 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map solarSystemIdStr, wormholesData, targetSystemClassGroup, + targetSystemUuid, + targetSolarSystemIdStr, ); out = updatedSignature; } diff --git a/assets/js/hooks/Mapper/helpers/bookmarkFormatHelper.ts b/assets/js/hooks/Mapper/helpers/bookmarkFormatHelper.ts index e260328e..6849a536 100644 --- a/assets/js/hooks/Mapper/helpers/bookmarkFormatHelper.ts +++ b/assets/js/hooks/Mapper/helpers/bookmarkFormatHelper.ts @@ -110,6 +110,10 @@ export const calculateBookmarkIndex = ( const parentSigs = sigs.filter(sig => sig.linked_system?.solar_system_id?.toString() === currentSolarSystemId); for (const parentSig of parentSigs) { const parentInfo = parseSignatureCustomInfo(parentSig.custom_info); + + // Return holes have their bookmark_index deleted, so we skip them to avoid hijacking the chain + if (parentInfo.bookmark_index === undefined) continue; + if (parentInfo.bookmark_index_chained != null) { if (!parentBookmarkIndex || String(parentInfo.bookmark_index_chained).length < parentBookmarkIndex.length) { parentBookmarkIndex = String(parentInfo.bookmark_index_chained); @@ -162,7 +166,7 @@ export const formatBookmarkName = ( formatStr: string, signature: SystemSignature, destSystemClass: string | null, - bookmarkIndex: number, + bookmarkIndex: number | string, wormholesData: Record = {}, startAtZero: boolean = false, mapping?: Record, @@ -180,12 +184,17 @@ export const formatBookmarkName = ( result = result.replace(/\{chain_index\}/g, () => info.bookmark_index_chained || bookmarkIndex.toString()); // Replace {index_letter} - result = result.replace(/\{index_letter\}/g, () => numberToLetters(bookmarkIndex, startAtZero)); + result = result.replace(/\{index_letter\}/g, () => + typeof bookmarkIndex === 'number' ? numberToLetters(bookmarkIndex, startAtZero) : bookmarkIndex.toString(), + ); // Replace {chain_index_letters} result = result.replace( /\{chain_index_letters\}/g, - () => info.bookmark_index_chained_letters || info.bookmark_index_chained || bookmarkIndex.toString(), + () => + info.bookmark_index_chained_letters || + info.bookmark_index_chained || + (typeof bookmarkIndex === 'number' ? numberToLetters(bookmarkIndex, startAtZero) : bookmarkIndex.toString()), ); // Replace {sig_letters} (first 3 chars of eve_id) @@ -365,6 +374,8 @@ export const handleAutoBookmark = async ( currentSolarSystemId: string, wormholesData: Record, targetSystemClassGroup: string | null, + targetSystemUuid?: string, + targetSolarSystemId?: string, ): Promise<{ updatedSignature: SystemSignature; shouldUpdate: boolean }> => { let updatedSignature = signature; let shouldUpdate = false; @@ -378,8 +389,39 @@ export const handleAutoBookmark = async ( const info = parseSignatureCustomInfo(signature.custom_info); let bookmarkIndex = info.bookmark_index; + let bookmarkIndexToUse: number | string = bookmarkIndex != null ? bookmarkIndex : ''; - if (bookmarkIndex == null) { + let isReturnHole = false; + let symbol = ''; + + if (currentSettings?.bookmark_return_hole_ignore && (targetSystemUuid || targetSolarSystemId)) { + const targetSigsRaw = [ + ...(targetSystemUuid ? systemSignatures[targetSystemUuid] || [] : []), + ...(targetSolarSystemId ? systemSignatures[targetSolarSystemId] || [] : []), + ]; + + const uniqueTargetSigs = Array.from(new Map(targetSigsRaw.map(sig => [sig.eve_id, sig])).values()); + + isReturnHole = uniqueTargetSigs.some( + sig => sig.linked_system?.solar_system_id?.toString() === currentSolarSystemId.toString(), + ); + + if (isReturnHole) { + symbol = currentSettings.bookmark_return_hole_symbol || ''; + if (symbol === ' ') symbol = ''; + } + } + + if (isReturnHole) { + if (info.bookmark_index !== undefined) { + delete info.bookmark_index; + } + info.bookmark_index_chained = symbol; + info.bookmark_index_chained_letters = symbol; + bookmarkIndexToUse = symbol; + updatedSignature = { ...signature, custom_info: JSON.stringify(info) }; + shouldUpdate = true; + } else if (bookmarkIndex == null) { const separator = currentSettings?.bookmark_custom_mapping?.chain_separator || ''; const calculated = calculateBookmarkIndex( systemSignatures, @@ -393,27 +435,35 @@ export const handleAutoBookmark = async ( info.bookmark_index = calculated.index; info.bookmark_index_chained = calculated.chained; info.bookmark_index_chained_letters = calculated.chainedLetters; + bookmarkIndexToUse = calculated.index; updatedSignature = { ...signature, custom_info: JSON.stringify(info) }; shouldUpdate = true; } - if (currentSettings?.bookmark_auto_temp_name && !updatedSignature.temporary_name) { + const needsTempNameUpdate = + !updatedSignature.temporary_name || + (isReturnHole && updatedSignature.temporary_name !== symbol && currentSettings?.bookmark_auto_temp_name); + + if (currentSettings?.bookmark_auto_temp_name && needsTempNameUpdate) { let autoName = ''; switch (currentSettings.bookmark_auto_temp_name) { case 'index': - autoName = bookmarkIndex.toString(); + autoName = bookmarkIndexToUse.toString(); break; case 'index_letter': - autoName = numberToLetters(bookmarkIndex, currentSettings.bookmark_wormholes_start_at_zero); + autoName = + typeof bookmarkIndexToUse === 'number' + ? numberToLetters(bookmarkIndexToUse, currentSettings.bookmark_wormholes_start_at_zero) + : bookmarkIndexToUse.toString(); break; case 'chain_index': - autoName = info.bookmark_index_chained || bookmarkIndex.toString(); + autoName = info.bookmark_index_chained || bookmarkIndexToUse.toString(); break; case 'chain_index_letters': - autoName = info.bookmark_index_chained_letters || info.bookmark_index_chained || bookmarkIndex.toString(); + autoName = info.bookmark_index_chained_letters || info.bookmark_index_chained || bookmarkIndexToUse.toString(); break; } - if (autoName) { + if (autoName !== '' || isReturnHole) { updatedSignature = { ...updatedSignature, temporary_name: autoName }; shouldUpdate = true; } @@ -424,7 +474,7 @@ export const handleAutoBookmark = async ( currentSettings.bookmark_name_format, updatedSignature, targetSystemClassGroup, - bookmarkIndex, + bookmarkIndexToUse, wormholesData, currentSettings.bookmark_wormholes_start_at_zero, currentSettings.bookmark_custom_mapping, diff --git a/lib/wanderer_app/repositories/map_user_settings_repo.ex b/lib/wanderer_app/repositories/map_user_settings_repo.ex index 3dfe9f88..babbec42 100644 --- a/lib/wanderer_app/repositories/map_user_settings_repo.ex +++ b/lib/wanderer_app/repositories/map_user_settings_repo.ex @@ -9,7 +9,9 @@ defmodule WandererApp.MapUserSettingsRepo do "bookmark_name_format" => "", "bookmark_custom_mapping" => %{}, "system_auto_tag" => "", - "system_custom_label_name" => "" + "system_custom_label_name" => "", + "bookmark_return_hole_ignore" => false, + "bookmark_return_hole_symbol" => "" } def get(map_id, user_id) do diff --git a/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex b/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex index 5bc278c4..60db6be6 100644 --- a/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex +++ b/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex @@ -242,7 +242,9 @@ defmodule WandererAppWeb.MapCoreEventHandler do "bookmark_auto_copy", "bookmark_auto_temp_name", "system_auto_tag", - "system_custom_label_name" + "system_custom_label_name", + "bookmark_return_hole_ignore", + "bookmark_return_hole_symbol" ]) |> Jason.encode!()