Compare commits

..

3 Commits

Author SHA1 Message Date
Dmitry Popov
d38c828600 chore: release version v1.3.1 2024-10-09 13:12:57 +04:00
Dmitry Popov
24196efbac fix(Core): Add system "true security" correction 2024-10-09 12:55:39 +04:00
Dmitry Popov
83c76e3a67 chore: release version v1.3.1 2024-10-08 02:35:33 +04:00
44 changed files with 311 additions and 1436 deletions

View File

@@ -2,57 +2,6 @@
<!-- changelog -->
## [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.4.0](https://github.com/wanderer-industries/wanderer/compare/v1.3.6...v1.4.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.5](https://github.com/wanderer-industries/wanderer/compare/v1.3.4...v1.3.5) (2024-10-09)
### Bug Fixes:
* Signatures: Signatures update fixes
## [v1.3.4](https://github.com/wanderer-industries/wanderer/compare/v1.3.3...v1.3.4) (2024-10-09)
## [v1.3.3](https://github.com/wanderer-industries/wanderer/compare/v1.3.2...v1.3.3) (2024-10-08)
## [v1.3.2](https://github.com/wanderer-industries/wanderer/compare/v1.3.1...v1.3.2) (2024-10-07)
## [v1.3.1](https://github.com/wanderer-industries/wanderer/compare/v1.3.0...v1.3.1) (2024-10-07)

View File

@@ -28,12 +28,6 @@ body {
font-weight: 500;
}
#bg-canvas {
position: absolute;
width: 100vw;
height: 100vh;
}
.ccp-font {
font-family: 'Shentox', 'Rogan', sans-serif !important;
}

View File

@@ -11,7 +11,7 @@ const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
const handleSelect = useCallback(
(character: CharacterTypeRaw) => {
mapRef.current?.command(Commands.centerSystem, character?.location?.solar_system_id?.toString());
mapRef.current?.command(Commands.selectSystem, character?.location?.solar_system_id?.toString());
},
[mapRef],
);

View File

@@ -48,19 +48,19 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: U
}, []);
const onAddSystem = useCallback(() => {
const { system: solarSystemId, outCommand, mapRef } = ref.current;
if (!solarSystemId) {
const { system, outCommand, mapRef } = ref.current;
if (!system) {
return;
}
outCommand({
type: OutCommand.addSystem,
data: {
system_id: solarSystemId,
system_id: system,
},
});
setTimeout(() => {
mapRef.current?.command(Commands.centerSystem, solarSystemId);
mapRef.current?.command(Commands.selectSystem, system);
setSystem(undefined);
}, 200);
}, []);

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import { ForwardedRef, useImperativeHandle, useRef } from 'react';
import { ForwardedRef, useImperativeHandle } from 'react';
import {
CommandAddConnections,
CommandAddSystems,
@@ -26,7 +26,6 @@ import {
useMapInit,
useMapRemoveSystems,
useMapUpdateSystems,
useCenterSystem,
useSelectSystem,
} from './api';
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
@@ -36,12 +35,8 @@ 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 } =
@@ -96,22 +91,8 @@ 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:
setTimeout(() => {
const systemId = `${data}`;
selectRef.current.onSelectionChange({
systems: [systemId],
connections: [],
});
selectSystem(systemId as CommandSelectSystem);
}, 100);
selectSystem(data as CommandSelectSystem);
break;
case Commands.routes:

View File

@@ -91,7 +91,7 @@ export const RoutesList = ({ data, onContextMenu }: RoutesListProps) => {
const { mapRef } = useMapRootState();
const handleClick = useCallback(
(systemId: number) => mapRef.current?.command(Commands.centerSystem, systemId.toString()),
(systemId: number) => mapRef.current?.command(Commands.selectSystem, systemId.toString()),
[mapRef],
);

View File

@@ -48,8 +48,6 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
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 [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
@@ -88,33 +86,12 @@ 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[], updateOnly: boolean) => {
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly);
async (newSignatures: SystemSignature[]) => {
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures);
const { signatures: updatedSignatures } = await outCommand({
type: OutCommand.updateSignatures,
@@ -137,26 +114,13 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
return;
}
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
await handleUpdateSignatures(
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
false,
);
await handleUpdateSignatures(signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)));
}, [handleUpdateSignatures, 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]);
useHotkey(true, ['a'], handleSelectAll);
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
@@ -166,25 +130,17 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
return;
}
const newSignatures = parseSignatures(
const signatures = parseSignatures(
clipboardContent,
settings.map(x => x.key),
);
const { removed } = getActualSigs(signaturesRef.current, newSignatures, false);
if (!signaturesRef.current || !signaturesRef.current.length || !removed.length) {
handleUpdateSignatures(newSignatures, false);
} else {
setParsedSignatures(newSignatures);
setAskUser(true);
}
handleUpdateSignatures(signatures);
}, [clipboardContent]);
useEffect(() => {
if (!systemId) {
setSignatures([]);
setAskUser(false);
return;
}
@@ -228,120 +184,98 @@ export const SystemSignaturesContent = ({ systemId, settings }: SystemSignatures
// };
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>
) : (
<>
<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');
}
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={renderIcon}
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="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*/}
{/* 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}
/>
{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
</span>
</button>
</div>
</div>
</div>
</div>
)}
</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}
/>
</div>
);
};

View File

@@ -5,7 +5,6 @@ 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[] = [];
@@ -21,9 +20,7 @@ export const getActualSigs = (
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
}
} else {
if (!updateOnly) {
removed.push(oldSig);
}
removed.push(oldSig);
}
});

View File

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

View File

@@ -6,7 +6,6 @@ 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';
export interface MapRootContentProps {}
@@ -20,8 +19,6 @@ export const MapRootContent = ({}: MapRootContentProps) => {
const handleShowOnTheMap = useCallback(() => setShowOnTheMap(true), []);
useSkipContextMenu();
return (
<Layout map={<MapWrapper refn={mapRef} />}>
{!isShowMenu ? (

View File

@@ -22,13 +22,6 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
});
}, [outCommand]);
const handleOpenUserSettings = useCallback(() => {
outCommand({
type: OutCommand.openUserSettings,
data: null,
});
}, [outCommand]);
const toggleMinimap = useCallback(() => {
setInterfaceSettings(x => ({
...x,
@@ -70,16 +63,6 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
</button>
</WdTooltipWrapper>
<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={handleOpenUserSettings}
>
<i className="pi pi-cog text-lg"></i>
</button>
</WdTooltipWrapper>
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
@@ -104,9 +87,9 @@ export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
onClick={toggleKSpace}
>
{interfaceSettings.isShowKSpace ? (
<i className="pi pi-heart-fill text-lg"></i>
<i className="pi pi-star-fill text-lg"></i>
) : (
<i className="pi pi-heart text-lg"></i>
<i className="pi pi-star text-lg"></i>
)}
</button>
</WdTooltipWrapper>

View File

@@ -37,7 +37,7 @@ export const CharacterCard = ({
const { mapRef } = useMapRootState();
const handleSelect = useCallback(() => {
mapRef.current?.command(Commands.centerSystem, char?.location?.solar_system_id?.toString());
mapRef.current?.command(Commands.selectSystem, char?.location?.solar_system_id?.toString());
}, [mapRef, char]);
return (

View File

@@ -1,4 +1,3 @@
export * from './usePageVisibility';
export * from './useClipboard';
export * from './useHotkey';
export * from './useSkipContextMenu';

View File

@@ -1,15 +0,0 @@
import { useEffect } from 'react';
export const useSkipContextMenu = () => {
useEffect(() => {
function handleContextMenu(e) {
e.preventDefault();
}
window.addEventListener(`contextmenu`, handleContextMenu);
return () => {
window.removeEventListener(`contextmenu`, handleContextMenu);
};
}, []);
};

View File

@@ -4,6 +4,7 @@ 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[];

View File

@@ -85,10 +85,6 @@ 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;

View File

@@ -21,7 +21,6 @@ export enum Commands {
mapUpdated = 'map_updated',
killsUpdated = 'kills_updated',
routes = 'routes',
centerSystem = 'center_system',
selectSystem = 'select_system',
}
@@ -41,8 +40,7 @@ export type Command =
| Commands.mapUpdated
| Commands.killsUpdated
| Commands.routes
| Commands.selectSystem
| Commands.centerSystem;
| Commands.selectSystem;
export type CommandInit = {
systems: SolarSystemRawType[];
@@ -74,7 +72,6 @@ export type CommandMapUpdated = Partial<CommandInit>;
export type CommandRoutes = RoutesList;
export type CommandKillsUpdated = Kill[];
export type CommandSelectSystem = string | undefined;
export type CommandCenterSystem = string | undefined;
export interface CommandData {
[Commands.init]: CommandInit;
@@ -93,7 +90,6 @@ export interface CommandData {
[Commands.routes]: CommandRoutes;
[Commands.killsUpdated]: CommandKillsUpdated;
[Commands.selectSystem]: CommandSelectSystem;
[Commands.centerSystem]: CommandCenterSystem;
}
export interface MapHandlers {
@@ -127,7 +123,6 @@ export enum OutCommand {
setAutopilotWaypoint = 'set_autopilot_waypoint',
addSystem = 'add_system',
addCharacter = 'add_character',
openUserSettings = 'open_user_settings',
getPassages = 'get_passages',
// Only UI commands

View File

@@ -3,230 +3,7 @@ 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');

View File

@@ -60,7 +60,15 @@ config :dart_sass, :version, "1.54.5"
config :tailwind, :version, "3.2.7"
config :wanderer_app, WandererApp.PromEx, manual_metrics_start_delay: :no_delay
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,
grafana_datasource_id: "wanderer"

View File

@@ -19,7 +19,6 @@ defmodule WandererApp.Api do
resource WandererApp.Api.MapCharacterSettings
resource WandererApp.Api.MapSubscription
resource WandererApp.Api.MapTransaction
resource WandererApp.Api.MapUserSettings
resource WandererApp.Api.User
resource WandererApp.Api.ShipTypeInfo
resource WandererApp.Api.UserActivity

View File

@@ -18,7 +18,6 @@ defmodule WandererApp.Api.Map do
define(:update, action: :update)
define(:update_acls, action: :update_acls)
define(:update_hubs, action: :update_hubs)
define(:update_options, action: :update_options)
define(:assign_owner, action: :assign_owner)
define(:mark_as_deleted, action: :mark_as_deleted)
@@ -113,10 +112,6 @@ defmodule WandererApp.Api.Map do
accept [:hubs]
end
update :update_options do
accept [:options]
end
update :mark_as_deleted do
accept([])
@@ -172,10 +167,6 @@ defmodule WandererApp.Api.Map do
allow_nil?(true)
end
attribute :options, :string do
allow_nil? true
end
create_timestamp(:inserted_at)
update_timestamp(:updated_at)
end

View File

@@ -1,54 +0,0 @@
defmodule WandererApp.Api.MapUserSettings do
@moduledoc false
use Ash.Resource,
domain: WandererApp.Api,
data_layer: AshPostgres.DataLayer
postgres do
repo(WandererApp.Repo)
table("map_user_settings_v1")
end
code_interface do
define(:create, action: :create)
define(:by_user_id,
get_by: [:map_id, :user_id],
action: :read
)
define(:update_settings, action: :update_settings)
end
actions do
default_accept [
:map_id,
:user_id,
:settings
]
defaults [:create, :read, :update, :destroy]
update :update_settings do
accept [:settings]
end
end
attributes do
uuid_primary_key :id
attribute :settings, :string do
allow_nil? true
end
end
relationships do
belongs_to :map, WandererApp.Api.Map, primary_key?: true, allow_nil?: false
belongs_to :user, WandererApp.Api.User, primary_key?: true, allow_nil?: false
end
identities do
identity :uniq_map_user, [:map_id, :user_id]
end
end

View File

@@ -6,7 +6,7 @@ defmodule WandererApp.Character do
@read_character_wallet_scope "esi-wallet.read_character_wallet.v1"
@read_corp_wallet_scope "esi-wallet.read_corporation_wallets.v1"
def get_character(character_id) when not is_nil(character_id) do
def get_character(character_id) do
case Cachex.get(:character_cache, character_id) do
{:ok, nil} ->
case WandererApp.Api.Character.by_id(character_id) do
@@ -23,8 +23,6 @@ defmodule WandererApp.Character do
end
end
def get_character(_character_id), do: {:ok, nil}
def get_character!(character_id) do
case get_character(character_id) do
{:ok, character} ->

View File

@@ -52,15 +52,6 @@ defmodule WandererApp.Map do
end
end
def get_map_options!(map) do
map
|> Map.get(:options)
|> case do
nil -> %{"layout" => "left_to_right"}
options -> Jason.decode!(options)
end
end
def update_map(map_id, map_update) do
Cachex.get_and_update(:map_cache, map_id, fn map ->
case map do

View File

@@ -19,47 +19,46 @@ defmodule WandererApp.Map.PositionCalculator do
def get_system_bounding_rect(_system), do: [{0, 0}, {0, 0}]
def get_new_system_position(nil, rtree_name, opts) do
{:ok, {x, y}} = rtree_name |> check_system_available_positions(@start_x, @start_y, 1, opts)
def get_new_system_position(nil, rtree_name) do
{:ok, {x, y}} = rtree_name |> _check_system_available_positions(@start_x, @start_y, 1)
%{x: x, y: y}
end
def get_new_system_position(
%{position_x: start_x, position_y: start_y} = _old_system,
rtree_name,
opts
rtree_name
) do
{:ok, {x, y}} = rtree_name |> check_system_available_positions(start_x, start_y, 1, opts)
{:ok, {x, y}} = rtree_name |> _check_system_available_positions(start_x, start_y, 1)
%{x: x, y: y}
end
defp check_system_available_positions(_rtree_name, _start_x, _start_y, 100, _opts),
do: {:ok, {@start_x, @start_y}}
defp _check_system_available_positions(_rtree_name, _start_x, _start_y, 100) do
{:ok, {@start_x, @start_y}}
end
defp check_system_available_positions(rtree_name, start_x, start_y, level, opts) do
possible_positions = get_available_positions(level, start_x, start_y, opts)
defp _check_system_available_positions(rtree_name, start_x, start_y, level) do
possible_positions = _get_available_positions(level, start_x, start_y)
case get_available_position(possible_positions, rtree_name) do
case _get_available_position(possible_positions, rtree_name) do
{:ok, nil} ->
rtree_name |> check_system_available_positions(start_x, start_y, level + 1, opts)
rtree_name |> _check_system_available_positions(start_x, start_y, level + 1)
{:ok, position} ->
{:ok, position}
end
end
defp get_available_position([], _rtree_name), do: {:ok, nil}
defp _get_available_position([], _rtree_name), do: {:ok, nil}
defp get_available_position([position | rest], rtree_name) do
if is_available_position(position, rtree_name) do
defp _get_available_position([position | rest], rtree_name) do
if _is_available_position(position, rtree_name) do
{:ok, position}
else
get_available_position(rest, rtree_name)
_get_available_position(rest, rtree_name)
end
end
defp is_available_position({x, y} = _position, rtree_name) do
defp _is_available_position({x, y} = _position, rtree_name) do
case DDRT.query(get_system_bounding_rect(%{position_x: x, position_y: y}), rtree_name) do
{:ok, []} ->
true
@@ -72,10 +71,9 @@ defmodule WandererApp.Map.PositionCalculator do
end
end
def get_available_positions(level, x, y, opts),
do: adjusted_coordinates(1 + level * 2, x, y, opts)
def _get_available_positions(level, x, y), do: _adjusted_coordinates(1 + level * 2, x, y)
defp edge_coordinates(n, opts) when n > 1 do
defp _edge_coordinates(n) when n > 1 do
min = -div(n, 2)
max = div(n, 2)
# Top edge
@@ -92,20 +90,16 @@ defmodule WandererApp.Map.PositionCalculator do
|> Enum.uniq()
end
defp sorted_edge_coordinates(n, opts) when n > 1 do
coordinates = edge_coordinates(n, opts)
start_index = get_start_index(n, opts[:layout])
defp _sorted_edge_coordinates(n) when n > 1 do
coordinates = _edge_coordinates(n)
middle_right_index = div(n, 2)
Enum.slice(coordinates, start_index, length(coordinates) - start_index) ++
Enum.slice(coordinates, 0, start_index)
Enum.slice(coordinates, middle_right_index, length(coordinates) - middle_right_index) ++
Enum.slice(coordinates, 0, middle_right_index)
end
defp get_start_index(n, "left_to_right"), do: div(n, 2)
defp get_start_index(n, "top_to_bottom"), do: div(n, 2) + n - 1
defp adjusted_coordinates(n, start_x, start_y, opts) when n > 1 do
sorted_coords = sorted_edge_coordinates(n, opts)
defp _adjusted_coordinates(n, start_x, start_y) when n > 1 do
sorted_coords = _sorted_edge_coordinates(n)
Enum.map(sorted_coords, fn {x, y} ->
{

View File

@@ -11,8 +11,7 @@ defmodule WandererApp.Map.Server.Impl do
defstruct [
:map_id,
:rtree_name,
map: nil,
map_opts: []
map: nil
]
# @ccp1 -1
@@ -800,9 +799,6 @@ defmodule WandererApp.Map.Server.Impl do
}
end
def handle_event({:options_updated, options}, %{map: map, map_id: map_id} = state),
do: %{state | map_opts: [layout: options.layout]}
def handle_event({ref, _result}, %{map_id: _map_id} = state) do
Process.demonitor(ref, [:flush])
@@ -842,12 +838,12 @@ defmodule WandererApp.Map.Server.Impl do
character_id,
location,
old_location,
%{map: map, map_id: map_id, rtree_name: rtree_name, map_opts: map_opts} = _state
%{map: map, map_id: map_id, rtree_name: rtree_name} = _state
) do
case is_nil(old_location.solar_system_id) and
_can_add_location(map.scope, location.solar_system_id) do
true ->
:ok = maybe_add_system(map_id, location, nil, rtree_name, map_opts)
:ok = maybe_add_system(map_id, location, nil, rtree_name)
_ ->
case _is_connection_valid(
@@ -856,9 +852,10 @@ defmodule WandererApp.Map.Server.Impl do
location.solar_system_id
) do
true ->
:ok = maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
:ok = maybe_add_system(map_id, old_location, location, rtree_name, map_opts)
:ok = maybe_add_connection(map_id, location, old_location, character_id)
{:ok, character} = WandererApp.Character.get_character(character_id)
:ok = maybe_add_system(map_id, location, old_location, rtree_name)
:ok = maybe_add_system(map_id, old_location, location, rtree_name)
:ok = maybe_add_connection(map_id, location, old_location, character)
_ ->
:ok
@@ -1104,7 +1101,7 @@ defmodule WandererApp.Map.Server.Impl do
end)}
defp _add_system(
%{map_id: map_id, map_opts: map_opts, rtree_name: rtree_name} = state,
%{map_id: map_id, rtree_name: rtree_name} = state,
%{
solar_system_id: solar_system_id,
coordinates: coordinates
@@ -1120,7 +1117,7 @@ defmodule WandererApp.Map.Server.Impl do
_ ->
%{x: x, y: y} =
WandererApp.Map.PositionCalculator.get_new_system_position(nil, rtree_name, map_opts)
WandererApp.Map.PositionCalculator.get_new_system_position(nil, rtree_name)
%{"x" => x, "y" => y}
end
@@ -1265,22 +1262,20 @@ defmodule WandererApp.Map.Server.Impl do
defp _init_map(
state,
%{characters: characters} = initial_map,
%{characters: characters} = map,
subscription_settings,
systems,
connections
) do
map =
initial_map
map
|> WandererApp.Map.new()
|> WandererApp.Map.update_subscription_settings!(subscription_settings)
|> WandererApp.Map.add_systems!(systems)
|> WandererApp.Map.add_connections!(connections)
|> WandererApp.Map.add_characters!(characters)
map_options = WandererApp.Map.get_map_options!(initial_map)
%{state | map: map, map_opts: [layout: map_options |> Map.get("layout")]}
%{state | map: map}
end
defp _init_map_systems(state, [] = _systems), do: state
@@ -1589,33 +1584,26 @@ defmodule WandererApp.Map.Server.Impl do
defp maybe_remove_connection(_map_id, _location, _old_location), do: :ok
defp maybe_add_connection(map_id, location, old_location, character_id)
defp maybe_add_connection(map_id, location, old_location, character)
when not is_nil(location) and not is_nil(old_location) and
not is_nil(old_location.solar_system_id) and
location.solar_system_id != old_location.solar_system_id do
character_id
|> WandererApp.Character.get_character!()
|> case do
case character do
nil ->
:ok
character ->
_ ->
:telemetry.execute([:wanderer_app, :map, :character, :jump], %{count: 1}, %{})
{:ok, _} =
WandererApp.Api.MapChainPassages.new(%{
map_id: map_id,
character_id: character_id,
character_id: character.id,
ship_type_id: character.ship,
ship_name: character.ship_name,
solar_system_source_id: old_location.solar_system_id,
solar_system_target_id: location.solar_system_id
})
broadcast!(map_id, :maybe_select_system, %{
character_id: character_id,
solar_system_id: location.solar_system_id
})
end
case WandererApp.Map.check_connection(map_id, location, old_location) do
@@ -1636,13 +1624,13 @@ defmodule WandererApp.Map.Server.Impl do
end
end
defp maybe_add_connection(_map_id, _location, _old_location, _character_id), do: :ok
defp maybe_add_connection(_map_id, _location, _old_location, _character), do: :ok
defp maybe_add_system(map_id, location, old_location, rtree_name, opts)
defp maybe_add_system(map_id, location, old_location, rtree_name)
when not is_nil(location) do
case WandererApp.Map.check_location(map_id, location) do
{:ok, location} ->
{:ok, position} = calc_new_system_position(map_id, old_location, rtree_name, opts)
{:ok, position} = calc_new_system_position(map_id, old_location, rtree_name)
case WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(
map_id,
@@ -1712,14 +1700,14 @@ defmodule WandererApp.Map.Server.Impl do
end
end
defp maybe_add_system(_map_id, _location, _old_location, _rtree_name, _opts), do: :ok
defp maybe_add_system(_map_id, _location, _old_location, _rtree_name), do: :ok
defp calc_new_system_position(map_id, old_location, rtree_name, opts),
do:
{:ok,
map_id
|> WandererApp.Map.find_system_by_location(old_location)
|> WandererApp.Map.PositionCalculator.get_new_system_position(rtree_name, opts)}
defp calc_new_system_position(map_id, old_location, rtree_name) do
{:ok,
map_id
|> WandererApp.Map.find_system_by_location(old_location)
|> WandererApp.Map.PositionCalculator.get_new_system_position(rtree_name)}
end
defp _broadcast_acl_updates(
{:ok,

View File

@@ -1,49 +0,0 @@
defmodule WandererApp.MapUserSettingsRepo do
use WandererApp, :repository
@default_form_data %{"select_on_spash" => "false"}
def get(map_id, user_id) do
map_id
|> WandererApp.Api.MapUserSettings.by_user_id(user_id)
|> case do
{:ok, settings} ->
{:ok, settings}
_ ->
{:ok, nil}
end
end
def get!(map_id, user_id) do
WandererApp.Api.MapUserSettings.by_user_id(map_id, user_id)
|> case do
{:ok, user_settings} -> user_settings
_ -> nil
end
end
def create_or_update(map_id, user_id, settings) do
get!(map_id, user_id)
|> case do
user_settings when not is_nil(user_settings) ->
user_settings
|> WandererApp.Api.MapUserSettings.update_settings(%{settings: settings})
_ ->
WandererApp.Api.MapUserSettings.create(%{
map_id: map_id,
user_id: user_id,
settings: settings
})
end
end
def to_form_data(nil), do: {:ok, @default_form_data}
def to_form_data(%{settings: settings} = _user_settings), do: {:ok, Jason.decode!(settings)}
def to_form_data!(user_settings) do
{:ok, data} = to_form_data(user_settings)
data
end
end

View File

@@ -377,7 +377,7 @@ defmodule WandererAppWeb.CoreComponents do
~H"""
<div phx-feedback-for={@name} class="form-control mt-8">
<label class="label cursor-pointer gap-2">
<label class="label cursor-pointer">
<span class="label-text"><%= @label %></span>
<input type="hidden" name={@name} value="false" />
<input

View File

@@ -43,20 +43,6 @@
>
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"
crossorigin="anonymous"
referrerpolicy="no-referrer"
>
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"
crossorigin="anonymous"
referrerpolicy="no-referrer"
>
</script>
<script defer phx-track-static type="module" src={~p"/assets/app.js"} crossorigin="anonymous">
</script>
<!-- Appzi: Capture Insightful Feedback -->

View File

@@ -1,5 +1,4 @@
<section class="prose prose-lg max-w-full w-full leading-normal tracking-normal text-indigo-400 bg-cover bg-fixed flex items-center justify-center">
<canvas id="bg-canvas"></canvas>
<div class="h-full w-full flex flex-col items-center">
<!--Main-->
<div class="artboard artboard-horizontal phone-3 pt-10 !h-40">
@@ -12,17 +11,9 @@
</div>
<!--Right Col-->
<div :if={@invite_token_valid} class="overflow-hidden">
<div class="!z-100 relative group alert items-center fade-in-scale text-white w-[224px] h-[44px] rounded p-px overflow-hidden">
<div class="group animate-rotate absolute inset-0 h-full w-full rounded-full bg-[conic-gradient(#0ea5e9_20deg,transparent_120deg)] group-hover:bg-[#0ea5e9]" />
<div class="!bg-black rounded w-[220px] h-[40px] flex items-center justify-center relative z-20">
<.link navigate={~p"/auth/eve?invite=#{@invite_token}"} class="opacity-100">
<img
src="https://web.ccpgamescdn.com/eveonlineassets/developers/eve-sso-login-black-large.png"
class="w-[220px] h-[40px]"
/>
</.link>
</div>
</div>
<.link navigate={~p"/auth/eve?invite=#{@invite_token}"}>
<img src="https://web.ccpgamescdn.com/eveonlineassets/developers/eve-sso-login-black-large.png" />
</.link>
</div>
</div>
</div>

View File

@@ -319,13 +319,11 @@ defmodule WandererAppWeb.AccessListsLive do
@impl true
def handle_info({:search, text}, socket) do
active_character_id =
socket.assigns.current_user.characters
|> Enum.filter(fn character -> not is_nil(character.refresh_token) end)
|> Enum.map(& &1.id)
first_character_id =
socket.assigns.user_character_ids
|> Enum.at(0)
{:ok, options} = search(active_character_id, text)
{:ok, options} = search(first_character_id, text)
send_update(LiveSelect.Component, options: options, id: socket.assigns.member_search_id)
{:noreply, socket |> assign(member_search_options: options)}

View File

@@ -113,45 +113,45 @@ defmodule WandererAppWeb.MapLive do
}
} = socket
) do
on_map_started(map_id, current_user, user_permissions)
_on_map_started(map_id, current_user, user_permissions)
{:noreply, socket}
end
@impl true
def handle_info(:character_token_invalid, socket),
do:
{:noreply,
socket
|> _put_invalid_token_message()}
def handle_info(:character_token_invalid, socket) do
{:noreply,
socket
|> _put_invalid_token_message()}
end
@impl true
def handle_info(%{event: :add_system, payload: system}, socket),
do:
{:noreply,
socket
|> push_map_event("add_systems", [map_ui_system(system)])}
def handle_info(%{event: :add_system, payload: system}, socket) do
{:noreply,
socket
|> push_map_event("add_systems", [map_ui_system(system)])}
end
@impl true
def handle_info(%{event: :update_system, payload: system}, socket),
do:
{:noreply,
socket
|> push_map_event("update_systems", [map_ui_system(system)])}
def handle_info(%{event: :update_system, payload: system}, socket) do
{:noreply,
socket
|> push_map_event("update_systems", [map_ui_system(system)])}
end
@impl true
def handle_info(%{event: :update_connection, payload: connection}, socket),
do:
{:noreply,
socket
|> push_map_event("update_connection", map_ui_connection(connection))}
def handle_info(%{event: :update_connection, payload: connection}, socket) do
{:noreply,
socket
|> push_map_event("update_connection", map_ui_connection(connection))}
end
@impl true
def handle_info(%{event: :systems_removed, payload: solar_system_ids}, socket),
do:
{:noreply,
socket
|> push_map_event("remove_systems", solar_system_ids)}
def handle_info(%{event: :systems_removed, payload: solar_system_ids}, socket) do
{:noreply,
socket
|> push_map_event("remove_systems", solar_system_ids)}
end
@impl true
def handle_info(%{event: :remove_connections, payload: connections}, socket) do
@@ -177,40 +177,6 @@ defmodule WandererAppWeb.MapLive do
)}
end
@impl true
def handle_info(
%{
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)
select_on_spash? =
map_user_settings
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|> Map.get("select_on_spash", "false")
|> String.to_existing_atom()
socket =
(is_user_character? && select_on_spash?)
|> case do
true ->
socket
|> push_map_event("select_system", solar_system_id)
false ->
socket
end
{:noreply, socket}
end
@impl true
def handle_info(%{event: :update_map, payload: map_diff}, socket) do
{:noreply,
@@ -454,7 +420,6 @@ defmodule WandererAppWeb.MapLive do
{:map_start,
%{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
@@ -501,24 +466,20 @@ defmodule WandererAppWeb.MapLive do
{:map_loaded,
%{
map_id: map_id,
user_characters: user_character_eve_ids,
initial_data: initial_data
}},
10
)
{:noreply,
socket
|> assign(
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
has_tracked_characters?: _has_tracked_characters?(user_character_eve_ids)
)}
{:noreply, socket}
end
def handle_info(
{:map_loaded,
%{
map_id: map_id,
user_characters: user_character_eve_ids,
initial_data: initial_data
} = _loaded_data},
socket
@@ -527,7 +488,11 @@ defmodule WandererAppWeb.MapLive do
{:noreply,
socket
|> assign(map_loaded?: true)
|> assign(
map_loaded?: true,
user_characters: user_character_eve_ids,
has_tracked_characters?: _has_tracked_characters?(user_character_eve_ids)
)
|> push_map_event(
"init",
initial_data |> Map.put(:characters, map_characters |> Enum.map(&map_ui_character/1))
@@ -1506,39 +1471,6 @@ defmodule WandererAppWeb.MapLive do
)}
end
@impl true
def handle_event(
"open_user_settings",
_,
%{assigns: %{map_id: map_id, current_user: current_user}} = socket
) do
{:ok, user_settings_form} =
WandererApp.MapUserSettingsRepo.get!(map_id, current_user.id)
|> WandererApp.MapUserSettingsRepo.to_form_data()
{:noreply,
socket
|> assign(
show_user_settings?: true,
user_settings_form: user_settings_form |> to_form()
)}
end
@impl true
def handle_event(
"update_user_settings",
user_settings_form,
%{assigns: %{map_id: map_id, current_user: current_user}} = socket
) do
settings = user_settings_form |> Map.take(["select_on_spash"]) |> Jason.encode!()
{:ok, user_settings} =
WandererApp.MapUserSettingsRepo.create_or_update(map_id, current_user.id, settings)
{:noreply,
socket |> assign(user_settings_form: user_settings_form, map_user_settings: user_settings)}
end
@impl true
def handle_event("noop", _, socket), do: {:noreply, socket}
@@ -1563,10 +1495,6 @@ defmodule WandererAppWeb.MapLive do
def handle_event("hide_tracking", _, socket),
do: {:noreply, socket |> assign(show_tracking?: false)}
@impl true
def handle_event("hide_user_settings", _, socket),
do: {:noreply, socket |> assign(show_user_settings?: false)}
@impl true
def handle_event(
"log_map_error",
@@ -1591,110 +1519,106 @@ defmodule WandererAppWeb.MapLive do
{:noreply, socket}
end
defp on_map_started(
map_id,
current_user,
%{view_system: true, track_character: track_character} = user_permissions
) do
with {:ok, _} <- current_user |> WandererApp.Api.User.update_last_map(%{last_map_id: map_id}),
{:ok, map_user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user.id),
{:ok, tracked_map_characters} <- _get_tracked_map_characters(map_id, current_user),
{:ok, characters_limit} <- map_id |> WandererApp.Map.get_characters_limit(),
{:ok, present_character_ids} <-
WandererApp.Cache.lookup("map_#{map_id}:presence_character_ids", []),
{:ok, kills} <- WandererApp.Cache.lookup("map_#{map_id}:zkb_kills", Map.new()) do
user_character_eve_ids = tracked_map_characters |> Enum.map(& &1.eve_id)
defp _on_map_started(map_id, current_user, user_permissions) do
case user_permissions do
%{view_system: true, track_character: track_character} ->
{:ok, _} = current_user |> WandererApp.Api.User.update_last_map(%{last_map_id: map_id})
events =
case tracked_map_characters |> Enum.any?(&(&1.access_token == nil)) do
true ->
[:invalid_token_message]
{:ok, tracked_map_characters} = _get_tracked_map_characters(map_id, current_user)
_ ->
[]
end
user_character_eve_ids = tracked_map_characters |> Enum.map(& &1.eve_id)
events =
case tracked_map_characters |> Enum.empty?() do
true ->
events ++ [:empty_tracked_characters]
events =
case tracked_map_characters |> Enum.any?(&(&1.access_token == nil)) do
true ->
[:invalid_token_message]
_ ->
events
end
_ ->
[]
end
events =
case present_character_ids |> Enum.count() < characters_limit do
true ->
events ++ [{:track_characters, tracked_map_characters, track_character}]
events =
case tracked_map_characters |> Enum.empty?() do
true ->
events ++ [:empty_tracked_characters]
_ ->
events ++ [:map_character_limit]
end
_ ->
events
end
initial_data =
map_id
|> _get_map_data()
|> Map.merge(%{
kills:
kills
|> Enum.filter(fn {_, kills} -> kills > 0 end)
|> Enum.map(&map_ui_kill/1),
present_characters:
present_character_ids
|> WandererApp.Character.get_character_eve_ids!(),
user_characters: user_character_eve_ids,
user_permissions: user_permissions,
system_static_infos: nil,
wormhole_types: nil,
effects: nil,
reset: false
})
{:ok, characters_limit} = map_id |> WandererApp.Map.get_characters_limit()
system_static_infos =
map_id
|> WandererApp.Map.list_systems!()
|> Enum.map(&WandererApp.CachedInfo.get_system_static_info!(&1.solar_system_id))
|> Enum.map(&map_ui_system_static_info/1)
{:ok, present_character_ids} =
WandererApp.Cache.lookup("map_#{map_id}:presence_character_ids", [])
initial_data =
initial_data
|> Map.put(
:wormholes,
WandererApp.CachedInfo.get_wormhole_types!()
events =
case present_character_ids |> Enum.count() < characters_limit do
true ->
events ++ [{:track_characters, tracked_map_characters, track_character}]
_ ->
events ++ [:map_character_limit]
end
{:ok, kills} = WandererApp.Cache.lookup("map_#{map_id}:zkb_kills", Map.new())
initial_data =
map_id
|> _get_map_data()
|> Map.merge(%{
kills:
kills
|> Enum.filter(fn {_, kills} -> kills > 0 end)
|> Enum.map(&map_ui_kill/1),
present_characters:
present_character_ids
|> WandererApp.Character.get_character_eve_ids!(),
user_characters: user_character_eve_ids,
user_permissions: user_permissions,
system_static_infos: nil,
wormhole_types: nil,
effects: nil,
reset: false
})
system_static_infos =
map_id
|> WandererApp.Map.list_systems!()
|> Enum.map(&WandererApp.CachedInfo.get_system_static_info!(&1.solar_system_id))
initial_data =
initial_data
|> Map.put(
:wormholes,
WandererApp.CachedInfo.get_wormhole_types!()
)
|> Map.put(
:effects,
WandererApp.CachedInfo.get_effects!()
)
|> Map.put(
:system_static_infos,
system_static_infos |> Enum.map(&map_ui_system_static_info/1)
)
|> Map.put(:reset, true)
Process.send_after(
self(),
{:map_start,
%{
map_id: map_id,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
}},
10
)
|> Map.put(
:effects,
WandererApp.CachedInfo.get_effects!()
)
|> Map.put(
:system_static_infos,
system_static_infos
)
|> Map.put(:reset, true)
Process.send_after(
self(),
{:map_start,
%{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
}},
10
)
else
error ->
Logger.error(fn -> "map_start_error: #{error}" end)
_ ->
Process.send_after(self(), :no_access, 10)
end
end
defp on_map_started(_map_id, _current_user, _user_permissions),
do: Process.send_after(self(), :no_access, 10)
defp _set_autopilot_waypoint(
current_user,
character_eve_id,

View File

@@ -149,20 +149,3 @@
</.table>
</.async_result>
</.modal>
<.modal
:if={assigns |> Map.get(:show_user_settings?, false)}
id="map-user-settings-modal"
title="Map user settings"
show
on_cancel={JS.push("hide_user_settings")}
>
<.form
:let={f}
:if={assigns |> Map.get(:user_settings_form, false)}
for={@user_settings_form}
phx-change="update_user_settings"
>
<.input type="checkbox" field={f[:select_on_spash]} label="Auto select splashed systems" />
</.form>
</.modal>

View File

@@ -5,8 +5,6 @@ defmodule WandererAppWeb.MapsLive do
alias BetterNumber, as: Number
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
@impl true
def mount(_params, %{"user_id" => user_id} = _session, socket) when not is_nil(user_id) do
{:ok, active_characters} = WandererApp.Api.Character.active_by_user(%{user_id: user_id})
@@ -114,13 +112,6 @@ defmodule WandererAppWeb.MapsLive do
"auto_renew?" => true
}
options_form =
map.options
|> case do
nil -> %{"layout" => "left_to_right"}
options -> Jason.decode!(options)
end
{:ok, estimated_price, discount} =
WandererApp.Map.SubscriptionManager.estimate_price(subscription_form, false)
@@ -139,7 +130,6 @@ defmodule WandererAppWeb.MapsLive do
active_settings_tab: "general",
is_adding_subscription?: false,
selected_subscription: nil,
options_form: options_form |> to_form(),
map_subscriptions: map_subscriptions,
subscription_form: subscription_form |> to_form(),
estimated_price: estimated_price,
@@ -152,10 +142,6 @@ defmodule WandererAppWeb.MapsLive do
{"3 Months", "3"},
{"6 Months", "6"},
{"1 Year", "12"}
],
layout_options: [
{"Left To Right", "left_to_right"},
{"Top To Bottom", "top_to_bottom"}
]
)
|> allow_upload(:settings,
@@ -659,28 +645,6 @@ defmodule WandererAppWeb.MapsLive do
|> push_patch(to: ~p"/maps")}
end
def handle_event(
"update_options",
%{
"layout" => layout
} = options_form,
%{assigns: %{map_id: map_id, map: map}} = socket
) do
options = %{layout: layout}
updated_map =
map
|> WandererApp.Api.Map.update_options!(%{options: Jason.encode!(options)})
@pubsub_client.broadcast(
WandererApp.PubSub,
"maps:#{map_id}",
{:options_updated, options}
)
{:noreply, socket |> assign(map: updated_map, options_form: options_form)}
end
@impl true
def handle_event("noop", _, socket) do
{:noreply, socket}

View File

@@ -26,6 +26,7 @@
>
<figure class="absolute z-10 h-200 avatar w-full h-full">
<img :if={map.scope === :all} class="absolute h-200" src="/images/all_back.webp" />
<img
:if={map.scope === :wormholes}
class="absolute h-200"
@@ -189,6 +190,7 @@
>
<div role="tablist" class="tabs tabs-bordered">
<a
:if={@map_subscriptions_enabled?}
role="tab"
phx-click="change_settings_tab"
phx-value-tab="general"
@@ -199,17 +201,6 @@
>
<.icon name="hero-wrench-screwdriver-solid" class="w-4 h-4" />&nbsp;General
</a>
<a
role="tab"
phx-click="change_settings_tab"
phx-value-tab="import"
class={[
"tab",
classes("tab-active": @active_settings_tab == "import")
]}
>
<.icon name="hero-document-arrow-down-solid" class="w-4 h-4" />&nbsp;Import/Export
</a>
<a
:if={@map_subscriptions_enabled?}
role="tab"
@@ -236,27 +227,6 @@
</a>
</div>
<.header :if={@active_settings_tab == "general"} class="bordered border-1 border-zinc-800">
<:actions>
<.form
:let={f}
:if={assigns |> Map.get(:options_form, false)}
for={@options_form}
phx-change="update_options"
>
<div class="stat-title">Map systems layout</div>
<div class="stat-value text-white">
<.input
type="select"
field={f[:layout]}
class="p-dropdown p-component p-inputwrapper"
placeholder="Map default layout"
options={@layout_options}
/>
</div>
</.form>
</:actions>
</.header>
<.header :if={@active_settings_tab == "import"} class="bordered border-1 border-zinc-800">
Import/Export Map Settings
<:actions>
<.form :if={assigns |> Map.get(:import_form, false)} for={@import_form} phx-change="import">

View File

@@ -58,8 +58,7 @@ defmodule WandererAppWeb.Router do
~w('unsafe-inline'),
~w(https://unpkg.com),
~w(https://w.appzi.io),
~w(https://www.googletagmanager.com),
~w(https://cdnjs.cloudflare.com)
~w(https://www.googletagmanager.com)
],
style_src: @style_src,
img_src: @img_src,

View File

@@ -2,7 +2,7 @@ defmodule WandererApp.MixProject do
use Mix.Project
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.5.0"
@version "1.3.1"
def project do
[

View File

@@ -1,21 +0,0 @@
defmodule WandererApp.Repo.Migrations.AddMapOptions 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(:maps_v1) do
add :options, :text
end
end
def down do
alter table(:maps_v1) do
remove :options
end
end
end

View File

@@ -1,52 +0,0 @@
defmodule WandererApp.Repo.Migrations.AddMapUserSettings 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
create table(:map_user_settings_v1, primary_key: false) do
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
add :settings, :text
add :map_id,
references(:maps_v1,
column: :id,
name: "map_user_settings_v1_map_id_fkey",
type: :uuid,
prefix: "public"
),
primary_key: true,
null: false
add :user_id,
references(:user_v1,
column: :id,
name: "map_user_settings_v1_user_id_fkey",
type: :uuid,
prefix: "public"
),
primary_key: true,
null: false
end
create unique_index(:map_user_settings_v1, [:map_id, :user_id],
name: "map_user_settings_v1_uniq_map_user_index"
)
end
def down do
drop_if_exists unique_index(:map_user_settings_v1, [:map_id, :user_id],
name: "map_user_settings_v1_uniq_map_user_index"
)
drop constraint(:map_user_settings_v1, "map_user_settings_v1_map_id_fkey")
drop constraint(:map_user_settings_v1, "map_user_settings_v1_user_id_fkey")
drop table(:map_user_settings_v1)
end
end

View File

@@ -1,116 +0,0 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"gen_random_uuid()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "settings",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": true,
"references": {
"deferrable": false,
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"index?": false,
"match_type": null,
"match_with": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "map_user_settings_v1_map_id_fkey",
"on_delete": null,
"on_update": null,
"primary_key?": true,
"schema": "public",
"table": "maps_v1"
},
"size": null,
"source": "map_id",
"type": "uuid"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": true,
"references": {
"deferrable": false,
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"index?": false,
"match_type": null,
"match_with": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "map_user_settings_v1_user_id_fkey",
"on_delete": null,
"on_update": null,
"primary_key?": true,
"schema": "public",
"table": "user_v1"
},
"size": null,
"source": "user_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "88FB044C6F66793E2247BF2CEE17F2E4ED52C007C3DDCE02B6EF583EDDD44D85",
"identities": [
{
"all_tenants?": false,
"base_filter": null,
"index_name": "map_user_settings_v1_uniq_map_user_index",
"keys": [
{
"type": "atom",
"value": "map_id"
},
{
"type": "atom",
"value": "user_id"
}
],
"name": "uniq_map_user",
"nils_distinct?": true,
"where": null
}
],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.WandererApp.Repo",
"schema": null,
"table": "map_user_settings_v1"
}

View File

@@ -1,186 +0,0 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"gen_random_uuid()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "slug",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "description",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "personal_note",
"type": "text"
},
{
"allow_nil?": true,
"default": "[]",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "hubs",
"type": [
"array",
"text"
]
},
{
"allow_nil?": false,
"default": "\"wormholes\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "scope",
"type": "text"
},
{
"allow_nil?": true,
"default": "false",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "deleted",
"type": "boolean"
},
{
"allow_nil?": true,
"default": "false",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "only_tracked_characters",
"type": "boolean"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "options",
"type": "text"
},
{
"allow_nil?": false,
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "inserted_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "updated_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"deferrable": false,
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"index?": false,
"match_type": null,
"match_with": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "maps_v1_owner_id_fkey",
"on_delete": null,
"on_update": null,
"primary_key?": true,
"schema": "public",
"table": "character_v1"
},
"size": null,
"source": "owner_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "E5FC6B5F1B9AD5E23163494C7C93A8002F9C812AFC7A26A8C33A344877086A03",
"identities": [
{
"all_tenants?": false,
"base_filter": null,
"index_name": "maps_v1_unique_slug_index",
"keys": [
{
"type": "atom",
"value": "slug"
}
],
"name": "unique_slug",
"nils_distinct?": true,
"where": null
}
],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.WandererApp.Repo",
"schema": null,
"table": "maps_v1"
}