mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-30 21:13:31 +00:00
Compare commits
1 Commits
v1.32.2
...
minimap-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2809959056 |
36
CHANGELOG.md
36
CHANGELOG.md
@@ -2,42 +2,6 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.32.2](https://github.com/wanderer-industries/wanderer/compare/v1.32.1...v1.32.2) (2025-01-02)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.32.1](https://github.com/wanderer-industries/wanderer/compare/v1.32.0...v1.32.1) (2024-12-25)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.32.0](https://github.com/wanderer-industries/wanderer/compare/v1.31.0...v1.32.0) (2024-12-24)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Add search & update manual adding systems API
|
||||
|
||||
* Map: Add search & update manual adding systems API
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Added ability to add new system to routes via routes widget
|
||||
|
||||
* Map: Reworked add system to map
|
||||
|
||||
## [v1.31.0](https://github.com/wanderer-industries/wanderer/compare/v1.30.2...v1.31.0) (2024-12-20)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Show tracking for new users by default. Auto link characters to account fix. Add character loading indicators.
|
||||
|
||||
## [v1.30.2](https://github.com/wanderer-industries/wanderer/compare/v1.30.1...v1.30.2) (2024-12-17)
|
||||
|
||||
|
||||
|
||||
@@ -108,7 +108,3 @@
|
||||
.p-dropdown-empty-message {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
useContextMenuConnectionHandlers,
|
||||
useContextMenuRootHandlers,
|
||||
} from './components';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||
import { OnMapSelectionChange } from './map.types';
|
||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
@@ -92,7 +92,6 @@ interface MapCompProps {
|
||||
onSelectionChange: OnMapSelectionChange;
|
||||
onManualDelete(systems: string[]): void;
|
||||
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
||||
minimapClasses?: string;
|
||||
isShowMinimap?: boolean;
|
||||
@@ -117,7 +116,6 @@ const MapComp = ({
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
onAddSystem,
|
||||
}: MapCompProps) => {
|
||||
const { getNode } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||
@@ -125,7 +123,7 @@ const MapComp = ({
|
||||
|
||||
useMapHandlers(refn, onSelectionChange);
|
||||
useUpdateNodes(nodes);
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
|
||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||
const { update } = useMapState();
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { useReactFlow, XYPosition } from 'reactflow';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { useMapState } from '../../MapProvider.tsx';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { OnMapAddSystemCallback } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
|
||||
type UseContextMenuRootHandlers = {
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
};
|
||||
|
||||
export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHandlers = {}) => {
|
||||
export const useContextMenuRootHandlers = () => {
|
||||
const rf = useReactFlow();
|
||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||
const { outCommand } = useMapState();
|
||||
const [position, setPosition] = useState<XYPosition | null>(null);
|
||||
|
||||
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -20,17 +18,14 @@ export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHa
|
||||
contextMenuRef.current?.show(e);
|
||||
};
|
||||
|
||||
const ref = useRef({ onAddSystem, position });
|
||||
ref.current = { onAddSystem, position };
|
||||
|
||||
const onAddSystemCallback = useCallback(() => {
|
||||
ref.current.onAddSystem?.({ coordinates: position });
|
||||
}, [position]);
|
||||
const onAddSystem = () => {
|
||||
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
|
||||
};
|
||||
|
||||
return {
|
||||
handleRootContext,
|
||||
|
||||
contextMenuRef,
|
||||
onAddSystem: onAddSystemCallback,
|
||||
onAddSystem,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
stroke-width: 2px;
|
||||
|
||||
&.MassVerge:not(&.Frigate) {
|
||||
stroke: #af0000;
|
||||
stroke: #af2900;
|
||||
}
|
||||
|
||||
&.MassHalf:not(&.Frigate) {
|
||||
stroke: #ffd700;
|
||||
stroke: #a85f00;
|
||||
}
|
||||
|
||||
&.Frigate {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { XYPosition } from 'reactflow';
|
||||
|
||||
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
|
||||
|
||||
@@ -8,5 +7,3 @@ export type OnMapSelectionChange = (event: {
|
||||
systems: string[];
|
||||
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
||||
}) => void;
|
||||
|
||||
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
.SearchItem {
|
||||
& > * {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.SearchItemEffect {
|
||||
font-weight: initial !important;
|
||||
}
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
import { AutoComplete } from 'primereact/autocomplete';
|
||||
import { OutCommand, SearchSystemItem } from '@/hooks/Mapper/types';
|
||||
import { SystemViewStandalone, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import classes from './AddSystemDialog.module.scss';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
|
||||
export type SearchOnSubmitCallback = (item: SearchSystemItem) => void;
|
||||
|
||||
interface AddSystemDialogProps {
|
||||
title?: string;
|
||||
visible: boolean;
|
||||
setVisible: (visible: boolean) => void;
|
||||
onSubmit?: SearchOnSubmitCallback;
|
||||
excludedSystems?: number[];
|
||||
}
|
||||
|
||||
export const AddSystemDialog = ({
|
||||
title = 'Add system',
|
||||
visible,
|
||||
setVisible,
|
||||
onSubmit,
|
||||
excludedSystems = [],
|
||||
}: AddSystemDialogProps) => {
|
||||
const {
|
||||
outCommand,
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const inputRef = useRef<any>();
|
||||
const onShow = useCallback(() => {
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const [filteredItems, setFilteredItems] = useState<SearchSystemItem[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<SearchSystemItem[] | null>(null);
|
||||
|
||||
const searchItems = useCallback(
|
||||
async (event: { query: string }) => {
|
||||
if (event.query.length < 2) {
|
||||
setFilteredItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const query = event.query;
|
||||
|
||||
if (query.length === 0) {
|
||||
setFilteredItems([]);
|
||||
} else {
|
||||
try {
|
||||
const result = await outCommand({
|
||||
type: OutCommand.searchSystems,
|
||||
data: {
|
||||
text: query,
|
||||
},
|
||||
});
|
||||
|
||||
let prepared = (result.systems as SearchSystemItem[]).sort((a, b) => {
|
||||
const amatch = a.label.indexOf(query);
|
||||
const bmatch = b.label.indexOf(query);
|
||||
return amatch - bmatch;
|
||||
});
|
||||
|
||||
if (excludedSystems) {
|
||||
prepared = prepared.filter(x => !excludedSystems.includes(x.system_static_info.solar_system_id));
|
||||
}
|
||||
|
||||
setFilteredItems(prepared);
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
setFilteredItems([]);
|
||||
}
|
||||
}
|
||||
},
|
||||
[excludedSystems, outCommand],
|
||||
);
|
||||
|
||||
const ref = useRef({ onSubmit, selectedItem });
|
||||
ref.current = { onSubmit, selectedItem };
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const { onSubmit, selectedItem } = ref.current;
|
||||
setFilteredItems([]);
|
||||
setSelectedItem([]);
|
||||
|
||||
if (!selectedItem) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmit?.(selectedItem[0]);
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header={title}
|
||||
visible={visible}
|
||||
draggable={false}
|
||||
style={{ width: '520px' }}
|
||||
onShow={onShow}
|
||||
onHide={() => {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3 px-1.5">
|
||||
<div className="flex flex-col gap-2 py-3.5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<IconField>
|
||||
<AutoComplete
|
||||
ref={inputRef}
|
||||
multiple
|
||||
showEmptyMessage
|
||||
scrollHeight="300px"
|
||||
value={selectedItem}
|
||||
suggestions={filteredItems}
|
||||
completeMethod={searchItems}
|
||||
onChange={e => {
|
||||
setSelectedItem(e.value.length < 2 ? e.value : [e.value[e.value.length - 1]]);
|
||||
}}
|
||||
emptyMessage="Not found any system..."
|
||||
placeholder="Type here..."
|
||||
field="label"
|
||||
id="value"
|
||||
className="w-full"
|
||||
itemTemplate={(item: SearchSystemItem) => {
|
||||
const { security, system_class, effect_power, effect_name, statics } = item.system_static_info;
|
||||
const sortedStatics = sortWHClasses(wormholesData, statics);
|
||||
const isWH = isWormholeSpace(system_class);
|
||||
|
||||
return (
|
||||
<div className={clsx('flex gap-1.5', classes.SearchItem)}>
|
||||
<SystemViewStandalone
|
||||
security={security}
|
||||
system_class={system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
|
||||
{effect_name && isWH && (
|
||||
<WHEffectView
|
||||
effectName={effect_name}
|
||||
effectPower={effect_power}
|
||||
className={classes.SearchItemEffect}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isWH && (
|
||||
<div className="flex gap-1 grow justify-between">
|
||||
<div></div>
|
||||
<div className="flex gap-1">
|
||||
{sortedStatics.map(x => (
|
||||
<WHClassView key={x} whClassName={x} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
selectedItemTemplate={(item: SearchSystemItem) => (
|
||||
<SystemViewStandalone
|
||||
security={item.system_static_info.security}
|
||||
system_class={item.system_static_info.system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</IconField>
|
||||
|
||||
<span className="text-[12px] text-stone-400 ml-1">*to search type at least 2 symbols.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
outlined
|
||||
disabled={!selectedItem || selectedItem.length !== 1}
|
||||
size="small"
|
||||
label="Submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './AddSystemDialog';
|
||||
@@ -21,11 +21,6 @@ import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
|
||||
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
|
||||
const sortByDist = (a: Route, b: Route) => {
|
||||
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
|
||||
@@ -168,12 +163,6 @@ export const RoutesWidgetContent = () => {
|
||||
export const RoutesWidgetComp = () => {
|
||||
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
|
||||
const { data, update } = useRouteProvider();
|
||||
const {
|
||||
data: { hubs = [] },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const preparedHubs = useMemo(() => hubs.map(x => parseInt(x)), [hubs]);
|
||||
|
||||
const isSecure = data.path_type === 'secure';
|
||||
const handleSecureChange = useCallback(() => {
|
||||
@@ -185,23 +174,6 @@ export const RoutesWidgetComp = () => {
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 155);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<boolean>(false);
|
||||
|
||||
const onAddSystem = useCallback(() => setOpenAddSystem(true), []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (preparedHubs.includes(item.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.addHub,
|
||||
data: { system_id: item.value },
|
||||
});
|
||||
},
|
||||
[hubs, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
@@ -209,14 +181,6 @@ export const RoutesWidgetComp = () => {
|
||||
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
|
||||
<span className="select-none">Routes</span>
|
||||
<LayoutEventBlocker className="flex items-center gap-2">
|
||||
<WdImgButton
|
||||
className={PrimeIcons.PLUS_CIRCLE}
|
||||
onClick={onAddSystem}
|
||||
tooltip={{
|
||||
content: 'Click here to add new system to routes',
|
||||
}}
|
||||
/>
|
||||
|
||||
<WdTooltipWrapper content="Show shortest route">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
@@ -227,26 +191,13 @@ export const RoutesWidgetComp = () => {
|
||||
classNameLabel={clsx('text-red-400')}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
<WdImgButton
|
||||
className={PrimeIcons.SLIDERS_H}
|
||||
onClick={() => setRouteSettingsVisible(true)}
|
||||
tooltip={{
|
||||
content: 'Click here to open Routes settings',
|
||||
}}
|
||||
/>
|
||||
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
|
||||
</LayoutEventBlocker>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<RoutesWidgetContent />
|
||||
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
|
||||
|
||||
<AddSystemDialog
|
||||
title="Add system to routes"
|
||||
visible={openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(false)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@ type CheckboxesList = {
|
||||
|
||||
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
|
||||
{ prop: InterfaceStoredSettingsProps.isStickMinimapToLeft, label: 'Stick Minimap to left' },
|
||||
];
|
||||
|
||||
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.MiniMap {
|
||||
right: 3.5rem !important;
|
||||
|
||||
&--left {
|
||||
left: 3.5rem !important;
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { useCallback, useRef, useState, useMemo } from 'react';
|
||||
import { OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import {
|
||||
@@ -14,18 +14,14 @@ 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, XYPosition } from 'reactflow';
|
||||
import { Node } from 'reactflow';
|
||||
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { emitMapEvent, useMapEventListener } from '@/hooks/Mapper/events';
|
||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||
|
||||
import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { useCommonMapEventProcessor } from '@/hooks/Mapper/components/mapWrapper/hooks/useCommonMapEventProcessor.ts';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
|
||||
// TODO: INFO - this component needs for abstract work with Map instance
|
||||
export const MapWrapper = () => {
|
||||
@@ -36,6 +32,7 @@ export const MapWrapper = () => {
|
||||
interfaceSettings: {
|
||||
isShowMenu,
|
||||
isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap,
|
||||
isStickMinimapToLeft = STORED_INTERFACE_DEFAULT_VALUES.isStickMinimapToLeft,
|
||||
isShowKSpace,
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
@@ -51,7 +48,6 @@ export const MapWrapper = () => {
|
||||
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
||||
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
|
||||
const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<XYPosition | null>(null);
|
||||
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
|
||||
|
||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems });
|
||||
@@ -67,6 +63,18 @@ export const MapWrapper = () => {
|
||||
runCommand(event);
|
||||
});
|
||||
|
||||
const minimapClasses = useMemo(() => {
|
||||
if (isStickMinimapToLeft) {
|
||||
return classes['MiniMap--left'];
|
||||
}
|
||||
|
||||
if (!isShowMenu) {
|
||||
return classes.MiniMap;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [isShowMenu, isStickMinimapToLeft]);
|
||||
|
||||
const onSelectionChange: OnMapSelectionChange = useCallback(
|
||||
({ systems, connections }) => {
|
||||
const { selectedConnections, selectedSystems } = ref.current;
|
||||
@@ -128,28 +136,6 @@ export const MapWrapper = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onAddSystem: OnMapAddSystemCallback = useCallback(({ coordinates }) => {
|
||||
setOpenAddSystem(coordinates);
|
||||
}, []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (ref.current.systems.some(x => x.system_static_info.solar_system_id === item.value)) {
|
||||
emitMapEvent({
|
||||
name: Commands.centerSystem,
|
||||
data: item.value.toString(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.manualAddSystem,
|
||||
data: { coordinates: openAddSystem, solar_system_id: item.value },
|
||||
});
|
||||
},
|
||||
[openAddSystem, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
@@ -159,14 +145,13 @@ export const MapWrapper = () => {
|
||||
onConnectionInfoClick={handleConnectionDbClick}
|
||||
onSystemContextMenu={handleSystemContextMenu}
|
||||
onSelectionContextMenu={handleSystemMultipleContext}
|
||||
minimapClasses={!isShowMenu ? classes.MiniMap : undefined}
|
||||
minimapClasses={minimapClasses}
|
||||
isShowMinimap={isShowMinimap}
|
||||
showKSpaceBG={isShowKSpace}
|
||||
onManualDelete={handleManualDelete}
|
||||
isThickConnections={isThickConnections}
|
||||
isShowBackgroundPattern={isShowBackgroundPattern}
|
||||
isSoftBackground={isSoftBackground}
|
||||
onAddSystem={onAddSystem}
|
||||
/>
|
||||
|
||||
{openSettings != null && (
|
||||
@@ -181,12 +166,6 @@ export const MapWrapper = () => {
|
||||
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
||||
)}
|
||||
|
||||
<AddSystemDialog
|
||||
visible={!!openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(null)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
|
||||
<Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} />
|
||||
|
||||
<ContextMenuSystem
|
||||
|
||||
@@ -31,15 +31,12 @@ const prepareEffects = (effects: Record<string, EffectRaw>, effectName: string,
|
||||
return out;
|
||||
};
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export interface WHEffectViewProps {
|
||||
effectName: string;
|
||||
effectPower: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WHEffectView = ({ effectName, effectPower, className }: WHEffectViewProps) => {
|
||||
export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) => {
|
||||
const {
|
||||
data: { effects },
|
||||
} = useMapRootState();
|
||||
@@ -52,7 +49,7 @@ export const WHEffectView = ({ effectName, effectPower, className }: WHEffectVie
|
||||
[effectName, effectPower, effects],
|
||||
);
|
||||
|
||||
const targetClass = useMemo(() => `wh-effect-name${effectInfo.id}-${counter++}`, []);
|
||||
const targetClass = `wh-effect-name${effectInfo.id}`;
|
||||
|
||||
return (
|
||||
<div className={classes.WHEffectViewRoot}>
|
||||
@@ -87,7 +84,7 @@ export const WHEffectView = ({ effectName, effectPower, className }: WHEffectVie
|
||||
</div>
|
||||
</FixedTooltip>
|
||||
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass, className)}>
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass)}>
|
||||
{effectName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,7 @@ const INITIAL_DATA: MapRootData = {
|
||||
export enum InterfaceStoredSettingsProps {
|
||||
isShowMenu = 'isShowMenu',
|
||||
isShowMinimap = 'isShowMinimap',
|
||||
isStickMinimapToLeft = 'isStickMinimapToLeft',
|
||||
isShowKSpace = 'isShowKSpace',
|
||||
isThickConnections = 'isThickConnections',
|
||||
isShowUnsplashedSignatures = 'isShowUnsplashedSignatures',
|
||||
@@ -42,6 +43,7 @@ export enum InterfaceStoredSettingsProps {
|
||||
export type InterfaceStoredSettings = {
|
||||
isShowMenu: boolean;
|
||||
isShowMinimap: boolean;
|
||||
isStickMinimapToLeft: boolean;
|
||||
isShowKSpace: boolean;
|
||||
isThickConnections: boolean;
|
||||
isShowUnsplashedSignatures: boolean;
|
||||
@@ -52,6 +54,7 @@ export type InterfaceStoredSettings = {
|
||||
export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
isShowMenu: false,
|
||||
isShowMinimap: true,
|
||||
isStickMinimapToLeft: false,
|
||||
isShowKSpace: false,
|
||||
isThickConnections: false,
|
||||
isShowUnsplashedSignatures: false,
|
||||
|
||||
@@ -153,7 +153,6 @@ export enum OutCommand {
|
||||
getUserSettings = 'get_user_settings',
|
||||
updateUserSettings = 'update_user_settings',
|
||||
unlinkSignature = 'unlink_signature',
|
||||
searchSystems = 'search_systems',
|
||||
}
|
||||
|
||||
export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>;
|
||||
|
||||
@@ -120,13 +120,3 @@ export type SolarSystemRawType = {
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
system_signatures: SystemSignature[];
|
||||
};
|
||||
|
||||
export type SearchSystemItem = {
|
||||
class_title: string;
|
||||
constellation_name: string;
|
||||
label: string;
|
||||
region_name: string;
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
value: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,24 +14,31 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
define(:create, action: :create)
|
||||
define(:destroy, action: :destroy)
|
||||
|
||||
define(:read_by_map, action: :read_by_map)
|
||||
define(:by_map_filtered, action: :by_map_filtered)
|
||||
define(:tracked_by_map_filtered, action: :tracked_by_map_filtered)
|
||||
define(:tracked_by_map_all, action: :tracked_by_map_all)
|
||||
define(:read_by_map,
|
||||
action: :read_by_map
|
||||
)
|
||||
|
||||
define(:by_map_filtered,
|
||||
action: :by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_filtered,
|
||||
action: :tracked_by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_all,
|
||||
action: :tracked_by_map_all
|
||||
)
|
||||
|
||||
define(:track, action: :track)
|
||||
define(:untrack, action: :untrack)
|
||||
|
||||
define(:follow, action: :follow)
|
||||
define(:unfollow, action: :unfollow)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:map_id,
|
||||
:character_id,
|
||||
:tracked,
|
||||
:followed
|
||||
:tracked
|
||||
]
|
||||
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
@@ -69,14 +76,6 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
update :untrack do
|
||||
change(set_attribute(:tracked, false))
|
||||
end
|
||||
|
||||
update :follow do
|
||||
change(set_attribute(:followed, true))
|
||||
end
|
||||
|
||||
update :unfollow do
|
||||
change(set_attribute(:followed, false))
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -87,11 +86,6 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :followed, :boolean do
|
||||
default false
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -489,7 +489,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
defp maybe_update_location(
|
||||
%{
|
||||
character_id: character_id,
|
||||
character_id: character_id
|
||||
} =
|
||||
state,
|
||||
location
|
||||
|
||||
@@ -15,12 +15,13 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: track_character,
|
||||
followed: false
|
||||
tracked: track_character
|
||||
}),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :added], %{count: 1})
|
||||
|
||||
:ok
|
||||
else
|
||||
_error ->
|
||||
@@ -381,6 +382,7 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
{:character_location, %{solar_system_id: solar_system_id},
|
||||
%{solar_system_id: old_solar_system_id}}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
|
||||
@@ -333,7 +333,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
|
||||
Impl.broadcast!(map_id, :add_connection, connection)
|
||||
@@ -346,20 +346,9 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, :already_exists} ->
|
||||
# Still broadcast location change in case of followed character
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@@ -370,30 +359,33 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
def can_add_location(:none, _solar_system_id), do: false
|
||||
|
||||
def can_add_location(scope, solar_system_id) do
|
||||
{:ok, system_static_info} = get_system_static_info(solar_system_id)
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not is_prohibited_system_class?(system_static_info.system_class) and
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (@prohibited_systems |> Enum.member?(solar_system_id)) and
|
||||
@wh_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:stargates ->
|
||||
not is_prohibited_system_class?(system_static_info.system_class) and
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
@known_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:all ->
|
||||
not is_prohibited_system_class?(system_static_info.system_class)
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class))
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def is_prohibited_system_class?(system_class) do
|
||||
@prohibited_system_classes |> Enum.member?(system_class)
|
||||
end
|
||||
|
||||
def is_connection_exist(map_id, from_solar_system_id, to_solar_system_id),
|
||||
do:
|
||||
not is_nil(
|
||||
@@ -422,21 +414,24 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
current_system_id: to_solar_system_id
|
||||
})
|
||||
|
||||
{:ok, from_system_static_info} = get_system_static_info(from_solar_system_id)
|
||||
{:ok, to_system_static_info} = get_system_static_info(to_solar_system_id)
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(to_solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not is_prohibited_system_class?(from_system_static_info.system_class) and
|
||||
not is_prohibited_system_class?(to_system_static_info.system_class) and
|
||||
not (@prohibited_systems |> Enum.member?(from_solar_system_id)) and
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (@prohibited_systems |> Enum.member?(to_solar_system_id)) and
|
||||
known_jumps |> Enum.empty?() and to_solar_system_id != @jita and
|
||||
from_solar_system_id != @jita
|
||||
|
||||
:stargates ->
|
||||
not is_prohibited_system_class?(from_system_static_info.system_class) and
|
||||
not is_prohibited_system_class?(to_system_static_info.system_class) and
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (known_jumps |> Enum.empty?())
|
||||
end
|
||||
end
|
||||
@@ -452,16 +447,6 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_system_static_info(solar_system_id) do
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
{:ok, system_static_info}
|
||||
|
||||
_ ->
|
||||
{:ok, %{system_class: nil}}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(map_id, location, old_location)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
|
||||
@@ -84,22 +84,20 @@ defmodule WandererApp.Maps do
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: false,
|
||||
followed: false
|
||||
tracked: false
|
||||
}
|
||||
|
||||
def map_character(
|
||||
%{name: name, id: id, eve_id: eve_id, corporation_ticker: corporation_ticker} =
|
||||
_character,
|
||||
%{tracked: tracked, followed: followed} = _character_settings
|
||||
%{tracked: tracked} = _character_settings
|
||||
),
|
||||
do: %{
|
||||
name: name,
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: tracked,
|
||||
followed: followed
|
||||
tracked: tracked
|
||||
}
|
||||
|
||||
@decorate cacheable(
|
||||
|
||||
@@ -24,31 +24,11 @@ defmodule WandererApp.MapCharacterSettingsRepo do
|
||||
def get_tracked_by_map_all(map_id),
|
||||
do: WandererApp.Api.MapCharacterSettings.tracked_by_map_all(%{map_id: map_id})
|
||||
|
||||
def get_by_map(map_id, character_id) do
|
||||
case get_by_map_filtered(map_id, [character_id]) do
|
||||
{:ok, [setting | _]} ->
|
||||
{:ok, setting}
|
||||
|
||||
{:ok, []} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def track(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track()
|
||||
def untrack(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack()
|
||||
|
||||
def track!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track!()
|
||||
def untrack!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack!()
|
||||
|
||||
def follow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow()
|
||||
def unfollow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow()
|
||||
|
||||
def follow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow!()
|
||||
def unfollow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow!()
|
||||
|
||||
|
||||
def destroy!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.destroy!()
|
||||
end
|
||||
|
||||
@@ -86,8 +86,7 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: selected_map.id,
|
||||
tracked: true,
|
||||
followed: false
|
||||
tracked: true
|
||||
})
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
@@ -111,8 +111,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: true,
|
||||
followed: false
|
||||
tracked: true
|
||||
})
|
||||
|
||||
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
|
||||
@@ -191,92 +190,6 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"toggle_follow",
|
||||
%{"character-id" => clicked_char_id},
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id,
|
||||
current_user: current_user
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
|
||||
{:ok, all_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
# Find and filter user's characters
|
||||
{:ok, user_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
user_char_ids = Enum.map(user_characters, & &1.id)
|
||||
|
||||
my_settings =
|
||||
all_settings
|
||||
|> Enum.filter(fn s ->
|
||||
s.character_id in user_char_ids
|
||||
end)
|
||||
|
||||
existing = Enum.find(my_settings, &(&1.character_id == clicked_char_id))
|
||||
|
||||
{:ok, target_setting} =
|
||||
if existing do
|
||||
{:ok, existing}
|
||||
else
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: clicked_char_id,
|
||||
map_id: map_id,
|
||||
tracked: true,
|
||||
followed: true
|
||||
})
|
||||
end
|
||||
|
||||
# If the target_setting is already followed => unfollow it
|
||||
if target_setting.followed do
|
||||
{:ok, updated} = WandererApp.MapCharacterSettingsRepo.unfollow(target_setting)
|
||||
else
|
||||
# Only unfollow other rows from the current user
|
||||
for s <- my_settings, s.id != target_setting.id, s.followed == true do
|
||||
WandererApp.MapCharacterSettingsRepo.unfollow!(s)
|
||||
end
|
||||
|
||||
# Ensure the new followed char is tracked
|
||||
if not target_setting.tracked do
|
||||
WandererApp.MapCharacterSettingsRepo.track!(target_setting)
|
||||
|
||||
char = target_setting |> Ash.load!(:character) |> Map.get(:character)
|
||||
:ok = track_characters([char], map_id, true)
|
||||
:ok = add_characters([char], map_id, true)
|
||||
end
|
||||
|
||||
{:ok, updated} = WandererApp.MapCharacterSettingsRepo.follow(target_setting)
|
||||
end
|
||||
|
||||
# re-fetch or re-map to confirm final results in UI
|
||||
%{result: characters} = socket.assigns.characters
|
||||
|
||||
{:ok, tracked_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
user_eve_ids = Enum.map(tracked_characters, & &1.eve_id)
|
||||
|
||||
{:ok, final_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
updated_chars =
|
||||
characters
|
||||
|> Enum.map(fn c ->
|
||||
s = Enum.find(final_settings, &(&1.character_id == c.id))
|
||||
WandererApp.Maps.map_character(c, s)
|
||||
end)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(user_characters: user_eve_ids)
|
||||
|> assign(has_tracked_characters?: has_tracked_characters?(user_eve_ids))
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, %{characters: updated_chars}}
|
||||
end)
|
||||
|> MapEventHandler.push_map_event("init", %{user_characters: user_eve_ids, reset: false})
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
||||
def handle_ui_event("hide_tracking", _, socket),
|
||||
do: {:noreply, socket |> assign(show_tracking?: false)}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
use Phoenix.Component
|
||||
require Logger
|
||||
|
||||
alias WandererAppWeb.{MapEventHandler, MapCharactersEventHandler, MapSystemsEventHandler}
|
||||
alias WandererAppWeb.{MapEventHandler, MapCharactersEventHandler}
|
||||
|
||||
def handle_server_event(:update_permissions, socket) do
|
||||
DebounceAndThrottle.Debounce.apply(
|
||||
@@ -147,7 +147,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
options =
|
||||
WandererApp.Api.MapSolarSystem.find_by_name!(%{name: text})
|
||||
|> Enum.take(100)
|
||||
|> Enum.map(&MapSystemsEventHandler.map_system/1)
|
||||
|> Enum.map(&map_system/1)
|
||||
|
||||
send_update(LiveSelect.Component, options: options, id: id)
|
||||
|
||||
@@ -162,14 +162,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event("toggle_follow_" <> character_id, _, socket),
|
||||
do:
|
||||
MapCharactersEventHandler.handle_ui_event(
|
||||
"toggle_follow",
|
||||
%{"character-id" => character_id},
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event(
|
||||
"get_user_settings",
|
||||
_,
|
||||
@@ -572,4 +564,21 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
user_character_eve_ids |> Enum.member?(character.eve_id)
|
||||
end)
|
||||
end
|
||||
|
||||
defp map_system(
|
||||
%{
|
||||
solar_system_name: solar_system_name,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
solar_system_id: solar_system_id,
|
||||
class_title: class_title
|
||||
} = _system
|
||||
),
|
||||
do: %{
|
||||
label: solar_system_name,
|
||||
value: solar_system_id,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
class_title: class_title
|
||||
}
|
||||
end
|
||||
|
||||
@@ -21,57 +21,32 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
|> MapEventHandler.push_map_event("remove_systems", solar_system_ids)
|
||||
|
||||
def handle_server_event(
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: character_id,
|
||||
solar_system_id: solar_system_id
|
||||
}
|
||||
},
|
||||
%{assigns: %{current_user: current_user, map_id: map_id, map_user_settings: map_user_settings}} = socket
|
||||
) do
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: character_id,
|
||||
solar_system_id: solar_system_id
|
||||
}
|
||||
},
|
||||
%{assigns: %{current_user: current_user, map_user_settings: map_user_settings}} = socket
|
||||
) do
|
||||
is_user_character =
|
||||
current_user.characters |> Enum.map(& &1.id) |> Enum.member?(character_id)
|
||||
|
||||
is_user_character =
|
||||
current_user.characters
|
||||
|> Enum.map(& &1.id)
|
||||
|> Enum.member?(character_id)
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
|
||||
is_followed =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character_id) do
|
||||
{:ok, setting} -> setting.followed == true
|
||||
_ -> false
|
||||
end
|
||||
|
||||
must_select? = is_user_character && (is_select_on_spash || is_followed)
|
||||
if not must_select? do
|
||||
(is_user_character && is_select_on_spash)
|
||||
|> case do
|
||||
true ->
|
||||
socket
|
||||
else
|
||||
# Check if we already selected this exact system for this char:
|
||||
last_selected =
|
||||
WandererApp.Cache.lookup!(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
nil
|
||||
)
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
|
||||
if last_selected == solar_system_id do
|
||||
# same system => skip
|
||||
socket
|
||||
else
|
||||
# new system => update cache + push event
|
||||
WandererApp.Cache.put(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
end
|
||||
end
|
||||
false ->
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
def handle_server_event(%{event: :kills_updated, payload: kills}, socket) do
|
||||
@@ -90,32 +65,55 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
do: MapCoreEventHandler.handle_server_event(event, socket)
|
||||
|
||||
def handle_ui_event(
|
||||
"manual_add_system",
|
||||
%{"solar_system_id" => solar_system_id, "coordinates" => coordinates} = _event,
|
||||
"add_system",
|
||||
%{"system_id" => [solar_system_id]} = _event,
|
||||
%{
|
||||
assigns: %{
|
||||
current_user: current_user,
|
||||
has_tracked_characters?: true,
|
||||
map_id: map_id,
|
||||
tracked_character_ids: tracked_character_ids,
|
||||
user_permissions: %{add_system: true}
|
||||
}
|
||||
} =
|
||||
socket
|
||||
) do
|
||||
assigns:
|
||||
%{
|
||||
map_id: map_id,
|
||||
map_slug: map_slug,
|
||||
current_user: current_user,
|
||||
tracked_character_ids: tracked_character_ids,
|
||||
user_permissions: %{add_system: true}
|
||||
} = assigns
|
||||
} = socket
|
||||
)
|
||||
when is_binary(solar_system_id) and solar_system_id != "" do
|
||||
coordinates = Map.get(assigns, :coordinates)
|
||||
|
||||
WandererApp.Map.Server.add_system(
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: solar_system_id,
|
||||
solar_system_id: solar_system_id |> String.to_integer(),
|
||||
coordinates: coordinates
|
||||
},
|
||||
current_user.id,
|
||||
tracked_character_ids |> List.first()
|
||||
)
|
||||
|
||||
{:noreply, socket}
|
||||
{:noreply,
|
||||
socket
|
||||
|> push_patch(to: ~p"/#{map_slug}")}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"manual_add_system",
|
||||
%{"coordinates" => coordinates} = _event,
|
||||
%{
|
||||
assigns: %{
|
||||
has_tracked_characters?: true,
|
||||
map_slug: map_slug,
|
||||
user_permissions: %{add_system: true}
|
||||
}
|
||||
} =
|
||||
socket
|
||||
),
|
||||
do:
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(coordinates: coordinates)
|
||||
|> push_patch(to: ~p"/#{map_slug}/add-system")}
|
||||
|
||||
def handle_ui_event(
|
||||
"add_hub",
|
||||
%{"system_id" => solar_system_id} = _event,
|
||||
@@ -282,25 +280,6 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
{:reply, %{system_static_infos: system_static_infos}, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"search_systems",
|
||||
%{"text" => text} = _event,
|
||||
socket
|
||||
) do
|
||||
systems =
|
||||
WandererApp.Api.MapSolarSystem.find_by_name!(%{name: text})
|
||||
|> Enum.take(100)
|
||||
|> Enum.map(&map_system/1)
|
||||
|> Enum.filter(fn system ->
|
||||
not is_nil(system) && not is_nil(system.system_static_info) &&
|
||||
not WandererApp.Map.Server.ConnectionsImpl.is_prohibited_system_class?(
|
||||
system.system_static_info.system_class
|
||||
)
|
||||
end)
|
||||
|
||||
{:reply, %{systems: systems}, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"delete_systems",
|
||||
solar_system_ids,
|
||||
@@ -328,27 +307,6 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
def handle_ui_event(event, body, socket),
|
||||
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
|
||||
|
||||
def map_system(
|
||||
%{
|
||||
solar_system_name: solar_system_name,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
solar_system_id: solar_system_id,
|
||||
class_title: class_title
|
||||
} = _system
|
||||
) do
|
||||
system_static_info = MapEventHandler.get_system_static_info(solar_system_id)
|
||||
|
||||
%{
|
||||
label: solar_system_name,
|
||||
value: solar_system_id,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
class_title: class_title,
|
||||
system_static_info: system_static_info
|
||||
}
|
||||
end
|
||||
|
||||
defp can_update_system?(:locked, %{lock_system: false} = _user_permissions), do: false
|
||||
defp can_update_system?(_key, _user_permissions), do: true
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
@map_characters_ui_events [
|
||||
"add_character",
|
||||
"toggle_track",
|
||||
"toggle_follow",
|
||||
"hide_tracking"
|
||||
]
|
||||
|
||||
@@ -39,10 +38,10 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
@map_system_ui_events [
|
||||
"add_hub",
|
||||
"delete_hub",
|
||||
"add_system",
|
||||
"delete_systems",
|
||||
"get_system_static_infos",
|
||||
"manual_add_system",
|
||||
"search_systems",
|
||||
"get_system_static_infos",
|
||||
"update_system_position",
|
||||
"update_system_positions",
|
||||
"update_system_name",
|
||||
|
||||
@@ -102,6 +102,13 @@ defmodule WandererAppWeb.MapLive do
|
||||
|> assign(:active_page, :map)
|
||||
end
|
||||
|
||||
defp apply_action(socket, :add_system, _params) do
|
||||
socket
|
||||
|> assign(:active_page, :map)
|
||||
|> assign(:page_title, "Add System")
|
||||
|> assign(:add_system_form, to_form(%{"system_id" => nil}))
|
||||
end
|
||||
|
||||
def character_item(assigns) do
|
||||
~H"""
|
||||
<div class="flex items-center gap-3">
|
||||
|
||||
@@ -31,20 +31,74 @@
|
||||
</.link>
|
||||
</div>
|
||||
|
||||
<.modal
|
||||
:if={@live_action in [:add_system] && not is_nil(assigns |> Map.get(:map_slug)) && @map_loaded?}
|
||||
id="add-system-modal"
|
||||
class="!w-[400px]"
|
||||
title="Add System"
|
||||
show
|
||||
on_cancel={JS.patch(~p"/#{@map_slug}")}
|
||||
>
|
||||
<.form :let={f} for={@add_system_form} phx-submit="add_system">
|
||||
<.live_select
|
||||
label="Search system"
|
||||
field={f[:system_id]}
|
||||
update_min_len={2}
|
||||
available_option_class="w-full text-sm"
|
||||
debounce={200}
|
||||
mode={:tags}
|
||||
>
|
||||
<:option :let={option}>
|
||||
<div class="gap-1 w-full flex flex-align-center p-autocomplete-item text-sm">
|
||||
<div class="eve-wh-type-color-c1 text-gray-400 w-8"><%= option.class_title %></div>
|
||||
<div class="text-white w-16"><%= option.label %></div>
|
||||
<div class="text-gray-600 w-20"><%= option.constellation_name %></div>
|
||||
<div class="text-gray-600"><%= option.region_name %></div>
|
||||
</div>
|
||||
</:option>
|
||||
</.live_select>
|
||||
<div class="mt-2 bg-neutral text-neutral-content rounded-md p-1 text-xs w-full">
|
||||
* Start search system. You should type at least 2 symbols.
|
||||
</div>
|
||||
<div class="modal-action mt-0">
|
||||
<.button class="mt-2" type="submit">Add</.button>
|
||||
</div>
|
||||
</.form>
|
||||
</.modal>
|
||||
|
||||
<.modal
|
||||
:if={assigns |> Map.get(:show_activity?, false)}
|
||||
id="map-activity-modal"
|
||||
title="Activity of Characters"
|
||||
class="!w-[500px]"
|
||||
show
|
||||
on_cancel={JS.push("hide_activity")}
|
||||
>
|
||||
<.table
|
||||
:if={not (assigns |> Map.get(:character_activity) |> is_nil())}
|
||||
class="!max-h-[80vh] !overflow-y-auto"
|
||||
id="activity-tbl"
|
||||
rows={@character_activity.jumps}
|
||||
>
|
||||
<:col :let={activity} label="Character">
|
||||
<.character_item character={activity.character} />
|
||||
</:col>
|
||||
<:col :let={activity} label="Passages">
|
||||
<%= activity.count %>
|
||||
</:col>
|
||||
</.table>
|
||||
</.modal>
|
||||
|
||||
<.modal
|
||||
:if={assigns |> Map.get(:show_tracking?, false)}
|
||||
id="map-tracking-modal"
|
||||
title="Track and Follow Characters"
|
||||
title="Track Characters"
|
||||
show
|
||||
on_cancel={JS.push("hide_tracking")}
|
||||
>
|
||||
<.async_result :let={characters} assign={@characters}>
|
||||
<:loading>
|
||||
<span class="loading loading-dots loading-xs" />
|
||||
</:loading>
|
||||
<:failed :let={reason}>
|
||||
<%= reason %>
|
||||
</:failed>
|
||||
<:loading><span class="loading loading-dots loading-xs" /></:loading>
|
||||
<:failed :let={reason}><%= reason %></:failed>
|
||||
|
||||
<.table
|
||||
:if={characters}
|
||||
@@ -53,7 +107,7 @@
|
||||
rows={characters}
|
||||
>
|
||||
<:col :let={character} label="Track">
|
||||
<label class="flex items-center gap-2 justify-center">
|
||||
<label class="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
@@ -62,33 +116,16 @@
|
||||
id={"character-track-#{character.id}"}
|
||||
checked={character.tracked}
|
||||
/>
|
||||
</label>
|
||||
</:col>
|
||||
<:col :let={character} label="Follow">
|
||||
<label class="flex items-center gap-2 justify-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="followed_character"
|
||||
class="radio"
|
||||
phx-click="toggle_follow"
|
||||
phx-value-character-id={character.id}
|
||||
checked={Map.get(character, :followed, false)}
|
||||
/>
|
||||
</label>
|
||||
</:col>
|
||||
<:col :let={character} label="Character">
|
||||
<div class="flex items-center gap-3">
|
||||
<.avatar url={member_icon_url(character.eve_id)} label={character.name} />
|
||||
<div>
|
||||
<div class="font-bold">
|
||||
<%= character.name %>
|
||||
<span class="ml-1 text-gray-400">
|
||||
[<%= character.corporation_ticker %>]
|
||||
</span>
|
||||
<div class="flex items-center gap-3">
|
||||
<.avatar url={member_icon_url(character.eve_id)} label={character.name} />
|
||||
<div>
|
||||
<div class="font-bold">
|
||||
<%= character.name %><span class="ml-1 text-gray-400">[<%= character.corporation_ticker %>]</span>
|
||||
</div>
|
||||
<div class="text-sm opacity-50"></div>
|
||||
</div>
|
||||
<div class="text-sm opacity-50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</:col>
|
||||
</.table>
|
||||
</.async_result>
|
||||
|
||||
@@ -197,6 +197,7 @@ defmodule WandererAppWeb.Router do
|
||||
live("/profile/deposit", ProfileLive, :deposit)
|
||||
live("/profile/subscribe", ProfileLive, :subscribe)
|
||||
live("/:slug/audit", MapAuditLive, :index)
|
||||
live("/:slug/add-system", MapLive, :add_system)
|
||||
live("/:slug", MapLive, :index)
|
||||
end
|
||||
end
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -2,7 +2,7 @@ defmodule WandererApp.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
@version "1.32.2"
|
||||
@version "1.30.2"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
defmodule WandererApp.Repo.Migrations.MigrateResources1 do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_character_settings_v1) do
|
||||
add :followed, :boolean, default: false
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_character_settings_v1) do
|
||||
remove :followed
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user