mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-10 01:35:33 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
099650420d | ||
|
|
8ccf7fffa5 | ||
|
|
b97a055bf7 | ||
|
|
663fee6699 |
22
CHANGELOG.md
22
CHANGELOG.md
@@ -2,6 +2,28 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.24.0](https://github.com/wanderer-industries/wanderer/compare/v1.23.0...v1.24.0) (2024-11-27)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Signatures: Added "Lazy delete" option & got rid of update popup
|
||||
|
||||
## [v1.23.0](https://github.com/wanderer-industries/wanderer/compare/v1.22.0...v1.23.0) (2024-11-26)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: Lock systems available to manager/admin roles only (#75)
|
||||
|
||||
* Map: Lock systems available to manager/admin roles only
|
||||
|
||||
* Map: Fix add system & add acl member select behaviour
|
||||
|
||||
## [v1.22.0](https://github.com/wanderer-industries/wanderer/compare/v1.21.0...v1.22.0) (2024-11-26)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { ContextMenuSystemProps } from '@/hooks/Mapper/components/contexts';
|
||||
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
export const useContextMenuSystemItems = ({
|
||||
onDeleteSystem,
|
||||
@@ -25,6 +27,7 @@ export const useContextMenuSystemItems = ({
|
||||
const getStatus = useStatusMenu(systems, systemId, onSystemStatus);
|
||||
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
|
||||
const getWaypointMenu = useWaypointMenu(onWaypointSet);
|
||||
const canLockSystem = useMapCheckPermissions([UserPermission.LOCK_SYSTEM]);
|
||||
|
||||
return useMemo(() => {
|
||||
const system = systemId ? getSystemById(systems, systemId) : undefined;
|
||||
@@ -58,19 +61,25 @@ export const useContextMenuSystemItems = ({
|
||||
command: onHubToggle,
|
||||
},
|
||||
...(system.locked
|
||||
? [
|
||||
{
|
||||
label: 'Unlock',
|
||||
icon: PrimeIcons.LOCK_OPEN,
|
||||
command: onLockToggle,
|
||||
},
|
||||
]
|
||||
? canLockSystem
|
||||
? [
|
||||
{
|
||||
label: 'Unlock',
|
||||
icon: PrimeIcons.LOCK_OPEN,
|
||||
command: onLockToggle,
|
||||
},
|
||||
]
|
||||
: []
|
||||
: [
|
||||
{
|
||||
label: 'Lock',
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: onLockToggle,
|
||||
},
|
||||
...(canLockSystem
|
||||
? [
|
||||
{
|
||||
label: 'Lock',
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: onLockToggle,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ separator: true },
|
||||
{
|
||||
label: 'Delete',
|
||||
@@ -80,6 +89,7 @@ export const useContextMenuSystemItems = ({
|
||||
]),
|
||||
];
|
||||
}, [
|
||||
canLockSystem,
|
||||
systems,
|
||||
systemId,
|
||||
getTags,
|
||||
|
||||
@@ -32,6 +32,7 @@ const INITIAL_DATA: MapData = {
|
||||
visibleNodes: new Set(),
|
||||
showKSpaceBG: false,
|
||||
isThickConnections: false,
|
||||
userPermissions: {},
|
||||
};
|
||||
|
||||
export interface MapContextProps {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||
import { InfoDrawer, LayoutEventBlocker, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
|
||||
import {
|
||||
InfoDrawer,
|
||||
LayoutEventBlocker,
|
||||
TooltipPosition,
|
||||
WdImgButton,
|
||||
WdCheckbox,
|
||||
} from '@/hooks/Mapper/components/ui-kit';
|
||||
import { SystemSignaturesContent } from './SystemSignaturesContent';
|
||||
import {
|
||||
Setting,
|
||||
@@ -14,19 +20,22 @@ import {
|
||||
} from './SystemSignatureSettingsDialog';
|
||||
import { SignatureGroup } from '@/hooks/Mapper/types';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { CheckboxChangeEvent } from 'primereact/checkbox';
|
||||
|
||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v4_1';
|
||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v5';
|
||||
export const SHOW_DESCRIPTION_COLUMN_SETTING = 'show_description_column_setting';
|
||||
export const SHOW_UPDATED_COLUMN_SETTING = 'SHOW_UPDATED_COLUMN_SETTING';
|
||||
export const LAZY_DELETE_SIGNATURES_SETTING = 'LAZY_DELETE_SIGNATURES_SETTING';
|
||||
|
||||
const settings: Setting[] = [
|
||||
{ key: SHOW_UPDATED_COLUMN_SETTING, name: 'Show Updated Column', value: false, isFilter: false },
|
||||
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: false, isFilter: false },
|
||||
{ key: LAZY_DELETE_SIGNATURES_SETTING, name: 'Lazy Delete Signatures', value: false, isFilter: false },
|
||||
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true, isFilter: true },
|
||||
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true, isFilter: true },
|
||||
{ key: DEPLOYABLE, name: 'Show Deployables', value: true, isFilter: true },
|
||||
@@ -58,12 +67,25 @@ export const SystemSignatures = () => {
|
||||
|
||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
||||
|
||||
const lazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!.value;
|
||||
}, [settings]);
|
||||
|
||||
const handleSettingsChange = useCallback((settings: Setting[]) => {
|
||||
setSettings(settings);
|
||||
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
||||
setVisible(false);
|
||||
}, []);
|
||||
|
||||
const handleLazyDeleteChange = useCallback((event: CheckboxChangeEvent) => {
|
||||
setSettings(settings => {
|
||||
const lazyDelete = settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!;
|
||||
lazyDelete.value = !!event.checked;
|
||||
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
||||
return [...settings];
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
|
||||
|
||||
@@ -79,6 +101,15 @@ export const SystemSignatures = () => {
|
||||
<div className="flex gap-1">System Signatures</div>
|
||||
|
||||
<LayoutEventBlocker className="flex gap-2.5">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={'Lazy delete'}
|
||||
value={lazyDeleteValue}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={handleLazyDeleteChange}
|
||||
/>
|
||||
|
||||
<WdImgButton
|
||||
className={PrimeIcons.QUESTION_CIRCLE}
|
||||
tooltip={{
|
||||
@@ -102,7 +133,7 @@ export const SystemSignatures = () => {
|
||||
</InfoDrawer>
|
||||
<InfoDrawer title={<b className="text-slate-50">How to delete?</b>}>
|
||||
For delete any signature first of all you need select before
|
||||
<br /> and then use <b className="text-sky-500">Backspace</b>
|
||||
<br /> and then use <b className="text-sky-500">Del</b>
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
) as React.ReactNode,
|
||||
|
||||
@@ -36,6 +36,7 @@ import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets
|
||||
import {
|
||||
SHOW_DESCRIPTION_COLUMN_SETTING,
|
||||
SHOW_UPDATED_COLUMN_SETTING,
|
||||
LAZY_DELETE_SIGNATURES_SETTING,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures';
|
||||
type SystemSignaturesSortSettings = {
|
||||
sortField: string;
|
||||
@@ -66,8 +67,6 @@ export const SystemSignaturesContent = ({
|
||||
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
|
||||
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
|
||||
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
|
||||
const [parsedSignatures, setParsedSignatures] = useState<SystemSignature[]>([]);
|
||||
const [askUser, setAskUser] = useState(false);
|
||||
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
|
||||
|
||||
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
|
||||
@@ -84,6 +83,10 @@ export const SystemSignaturesContent = ({
|
||||
|
||||
const tooltipRef = useRef<WdTooltipHandlers>(null);
|
||||
|
||||
const lazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!.value;
|
||||
}, [settings]);
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (tableRef.current) {
|
||||
const tableWidth = tableRef.current.offsetWidth;
|
||||
@@ -132,13 +135,17 @@ export const SystemSignaturesContent = ({
|
||||
data: { system_id: systemId },
|
||||
});
|
||||
|
||||
setAskUser(false);
|
||||
setSignatures(signatures);
|
||||
}, [outCommand, systemId]);
|
||||
|
||||
const handleUpdateSignatures = useCallback(
|
||||
async (newSignatures: SystemSignature[], updateOnly: boolean) => {
|
||||
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly);
|
||||
async (newSignatures: SystemSignature[], updateOnly: boolean, skipUpdateUntouched?: boolean) => {
|
||||
const { added, updated, removed } = getActualSigs(
|
||||
signaturesRef.current,
|
||||
newSignatures,
|
||||
updateOnly,
|
||||
skipUpdateUntouched,
|
||||
);
|
||||
|
||||
const { signatures: updatedSignatures } = await outCommand({
|
||||
type: OutCommand.updateSignatures,
|
||||
@@ -172,6 +179,7 @@ export const SystemSignaturesContent = ({
|
||||
await handleUpdateSignatures(
|
||||
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
|
||||
false,
|
||||
true,
|
||||
);
|
||||
},
|
||||
[handleUpdateSignatures, selectable, signatures, selectedSignatures],
|
||||
@@ -181,16 +189,6 @@ export const SystemSignaturesContent = ({
|
||||
setSelectedSignatures(signatures);
|
||||
}, [signatures]);
|
||||
|
||||
const handleReplaceAll = useCallback(() => {
|
||||
handleUpdateSignatures(parsedSignatures, false);
|
||||
setAskUser(false);
|
||||
}, [parsedSignatures, handleUpdateSignatures]);
|
||||
|
||||
const handleUpdateOnly = useCallback(() => {
|
||||
handleUpdateSignatures(parsedSignatures, true);
|
||||
setAskUser(false);
|
||||
}, [parsedSignatures, handleUpdateSignatures]);
|
||||
|
||||
const handleSelectSignatures = useCallback(
|
||||
// TODO still will be good to define types if we use typescript
|
||||
// @ts-ignore
|
||||
@@ -219,14 +217,7 @@ export const SystemSignaturesContent = ({
|
||||
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(newSignatures, !lazyDeleteValue);
|
||||
};
|
||||
|
||||
useHotkey(true, ['a'], handleSelectAll);
|
||||
@@ -237,7 +228,6 @@ export const SystemSignaturesContent = ({
|
||||
useEffect(() => {
|
||||
if (!systemId) {
|
||||
setSignatures([]);
|
||||
setAskUser(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -430,27 +420,6 @@ export const SystemSignaturesContent = ({
|
||||
signatureData={selectedSignature}
|
||||
/>
|
||||
)}
|
||||
|
||||
{askUser && (
|
||||
<div className="absolute left-[1px] top-[29px] h-[calc(100%-30px)] w-[calc(100%-3px)] bg-stone-900/10 backdrop-blur-sm">
|
||||
<div className="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center">
|
||||
<div className="text-stone-400/80 text-sm">
|
||||
<div className="flex flex-col text-center gap-2">
|
||||
<button className="p-button p-component p-button-outlined p-button-sm btn-wide">
|
||||
<span className="p-button-label p-c" onClick={handleUpdateOnly}>
|
||||
Update
|
||||
</span>
|
||||
</button>
|
||||
<button className="p-button p-component p-button-outlined p-button-sm btn-wide">
|
||||
<span className="p-button-label p-c" onClick={handleReplaceAll}>
|
||||
Update & Delete missing
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ export const getActualSigs = (
|
||||
oldSignatures: SystemSignature[],
|
||||
newSignatures: SystemSignature[],
|
||||
updateOnly: boolean,
|
||||
skipUpdateUntouched?: boolean,
|
||||
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
|
||||
const updated: SystemSignature[] = [];
|
||||
const removed: SystemSignature[] = [];
|
||||
@@ -19,7 +20,7 @@ export const getActualSigs = (
|
||||
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
|
||||
if (isNeedUpgrade) {
|
||||
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
|
||||
} else {
|
||||
} else if (!skipUpdateUntouched) {
|
||||
updated.push({ ...oldSig });
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ContextStoreDataUpdate, useContextStore } from '@/hooks/Mapper/utils';
|
||||
import { createContext, Dispatch, ForwardedRef, forwardRef, RefObject, SetStateAction, useContext } from 'react';
|
||||
import { MapHandlers, MapUnionTypes, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { createContext, Dispatch, ForwardedRef, forwardRef, SetStateAction, useContext } from 'react';
|
||||
import { MapUnionTypes, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { useMapRootHandlers } from '@/hooks/Mapper/mapRootProvider/hooks';
|
||||
import { WithChildren } from '@/hooks/Mapper/types/common.ts';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
@@ -25,6 +25,7 @@ const INITIAL_DATA: MapRootData = {
|
||||
|
||||
selectedSystems: [],
|
||||
selectedConnections: [],
|
||||
userPermissions: {},
|
||||
};
|
||||
|
||||
export enum InterfaceStoredSettingsProps {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './useMapInit';
|
||||
export * from './useMapUpdated';
|
||||
export * from './useMapCheckPermissions';
|
||||
export * from './useRoutes';
|
||||
export * from './useCommandsConnections';
|
||||
export * from './useCommandsSystems';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
export const useMapCheckPermissions = (permissions: UserPermission[]) => {
|
||||
const {
|
||||
data: { userPermissions },
|
||||
} = useMapRootState();
|
||||
|
||||
return useMemo(() => permissions.every(x => userPermissions[x]), [permissions, userPermissions]);
|
||||
};
|
||||
@@ -19,6 +19,7 @@ export const useMapInit = () => {
|
||||
user_characters,
|
||||
present_characters,
|
||||
hubs,
|
||||
user_permissions,
|
||||
}: CommandInit) => {
|
||||
const updateData: Partial<MapRootData> = {};
|
||||
|
||||
@@ -51,6 +52,10 @@ export const useMapInit = () => {
|
||||
updateData.connections = connections;
|
||||
}
|
||||
|
||||
if (user_permissions) {
|
||||
updateData.userPermissions = user_permissions;
|
||||
}
|
||||
|
||||
if (hubs) {
|
||||
updateData.hubs = hubs;
|
||||
}
|
||||
|
||||
@@ -6,3 +6,4 @@ export * from './system';
|
||||
export * from './mapUnionTypes';
|
||||
export * from './signatures';
|
||||
export * from './connectionPassages';
|
||||
export * from './permissions';
|
||||
|
||||
@@ -4,6 +4,7 @@ import { WormholeDataRaw } from '@/hooks/Mapper/types/wormholes.ts';
|
||||
import { CharacterTypeRaw } from '@/hooks/Mapper/types/character.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { Kill } from '@/hooks/Mapper/types/kills.ts';
|
||||
import { UserPermissions } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum Commands {
|
||||
init = 'init',
|
||||
@@ -58,7 +59,7 @@ export type CommandInit = {
|
||||
characters: CharacterTypeRaw[];
|
||||
present_characters: string[];
|
||||
user_characters: string[];
|
||||
user_permissions: any;
|
||||
user_permissions: UserPermissions;
|
||||
hubs: string[];
|
||||
routes: RoutesList;
|
||||
reset?: boolean;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CharacterTypeRaw } from '@/hooks/Mapper/types/character.ts';
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types/connection.ts';
|
||||
import { UserPermissions } from '@/hooks/Mapper/types';
|
||||
|
||||
export type MapUnionTypes = {
|
||||
wormholesData: Record<string, WormholeDataRaw>;
|
||||
@@ -17,4 +18,5 @@ export type MapUnionTypes = {
|
||||
routes?: RoutesList;
|
||||
kills: Record<number, number>;
|
||||
connections: SolarSystemConnection[];
|
||||
userPermissions: Partial<UserPermissions>;
|
||||
};
|
||||
|
||||
19
assets/js/hooks/Mapper/types/permissions.ts
Normal file
19
assets/js/hooks/Mapper/types/permissions.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export enum UserPermission {
|
||||
ADMIN_MAP = 'admin_map',
|
||||
MANAGE_MAP = 'manage_map',
|
||||
VIEW_SYSTEM = 'view_system',
|
||||
VIEW_CHARACTER = 'view_character',
|
||||
VIEW_CONNECTION = 'view_connection',
|
||||
ADD_SYSTEM = 'add_system',
|
||||
ADD_CONNECTION = 'add_connection',
|
||||
UPDATE_SYSTEM = 'update_system',
|
||||
TRACK_CHARACTER = 'track_character',
|
||||
DELETE_CONNECTION = 'delete_connection',
|
||||
DELETE_SYSTEM = 'delete_system',
|
||||
LOCK_SYSTEM = 'lock_system',
|
||||
ADD_ACL = 'add_acl',
|
||||
DELETE_ACL = 'delete_acl',
|
||||
DELETE_MAP = 'delete_map',
|
||||
}
|
||||
|
||||
export type UserPermissions = Record<UserPermission, boolean>;
|
||||
@@ -61,12 +61,18 @@ defmodule WandererApp.Character do
|
||||
end)
|
||||
end
|
||||
|
||||
def get_character_state(character_id) do
|
||||
def get_character_state(character_id, init_if_empty? \\ true) do
|
||||
case Cachex.get(:character_state_cache, character_id) do
|
||||
{:ok, nil} ->
|
||||
character_state = WandererApp.Character.Tracker.init(character_id: character_id)
|
||||
Cachex.put(:character_state_cache, character_id, character_state)
|
||||
{:ok, character_state}
|
||||
case init_if_empty? do
|
||||
true ->
|
||||
character_state = WandererApp.Character.Tracker.init(character_id: character_id)
|
||||
Cachex.put(:character_state_cache, character_id, character_state)
|
||||
{:ok, character_state}
|
||||
|
||||
_ ->
|
||||
{:ok, nil}
|
||||
end
|
||||
|
||||
{:ok, character_state} ->
|
||||
{:ok, character_state}
|
||||
|
||||
@@ -83,22 +83,28 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
end
|
||||
|
||||
def stop_tracking(%__MODULE__{} = state, character_id) do
|
||||
{:ok, %{start_time: start_time}} = WandererApp.Character.get_character_state(character_id)
|
||||
{:ok, character_state} = WandererApp.Character.get_character_state(character_id, false)
|
||||
|
||||
duration = DateTime.diff(DateTime.utc_now(), start_time, :second)
|
||||
:telemetry.execute([:wanderer_app, :character, :tracker, :running], %{duration: duration})
|
||||
:telemetry.execute([:wanderer_app, :character, :tracker, :stopped], %{count: 1})
|
||||
Logger.debug(fn -> "Shutting down character tracker: #{inspect(character_id)}" end)
|
||||
case character_state do
|
||||
nil ->
|
||||
state
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_started")
|
||||
WandererApp.Cache.delete("character:#{character_id}:start_solar_system_id")
|
||||
WandererApp.Character.delete_character_state(character_id)
|
||||
%{start_time: start_time} ->
|
||||
duration = DateTime.diff(DateTime.utc_now(), start_time, :second)
|
||||
:telemetry.execute([:wanderer_app, :character, :tracker, :running], %{duration: duration})
|
||||
:telemetry.execute([:wanderer_app, :character, :tracker, :stopped], %{count: 1})
|
||||
Logger.debug(fn -> "Shutting down character tracker: #{inspect(character_id)}" end)
|
||||
|
||||
tracked_characters = state.characters |> Enum.reject(fn c_id -> c_id == character_id end)
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_started")
|
||||
WandererApp.Cache.delete("character:#{character_id}:start_solar_system_id")
|
||||
WandererApp.Character.delete_character_state(character_id)
|
||||
|
||||
WandererApp.Cache.insert("tracked_characters", tracked_characters)
|
||||
tracked_characters = state.characters |> Enum.reject(fn c_id -> c_id == character_id end)
|
||||
|
||||
%{state | characters: tracked_characters}
|
||||
WandererApp.Cache.insert("tracked_characters", tracked_characters)
|
||||
|
||||
%{state | characters: tracked_characters}
|
||||
end
|
||||
end
|
||||
|
||||
def update_track_settings(
|
||||
@@ -429,8 +435,19 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
end
|
||||
|
||||
def handle_info({:stop_track, character_id}, state) do
|
||||
@logger.debug(fn -> "Stopping character tracker: #{inspect(character_id)}" end)
|
||||
stop_tracking(state, character_id)
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:is_stop_tracking")
|
||||
|> case do
|
||||
false ->
|
||||
WandererApp.Cache.insert("character:#{character_id}:is_stop_tracking", true)
|
||||
Logger.debug(fn -> "Stopping character tracker: #{inspect(character_id)}" end)
|
||||
state = state |> stop_tracking(character_id)
|
||||
WandererApp.Cache.delete("character:#{character_id}:is_stop_tracking")
|
||||
|
||||
state
|
||||
|
||||
_ ->
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info(_event, state),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
180
lib/wanderer_app/map/server/map_server_acls_impl.ex
Normal file
180
lib/wanderer_app/map/server/map_server_acls_impl.ex
Normal file
@@ -0,0 +1,180 @@
|
||||
defmodule WandererApp.Map.Server.AclsImpl do
|
||||
@moduledoc false
|
||||
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Map.Server.Impl
|
||||
|
||||
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
|
||||
|
||||
def handle_map_acl_updated(%{map_id: map_id, map: old_map} = state, added_acls, removed_acls) do
|
||||
{:ok, map} =
|
||||
WandererApp.MapRepo.get(map_id,
|
||||
acls: [
|
||||
:owner_id,
|
||||
members: [:role, :eve_character_id, :eve_corporation_id, :eve_alliance_id]
|
||||
]
|
||||
)
|
||||
|
||||
track_acls(added_acls)
|
||||
|
||||
result =
|
||||
(added_acls ++ removed_acls)
|
||||
|> Task.async_stream(
|
||||
fn acl_id ->
|
||||
update_acl(acl_id)
|
||||
end,
|
||||
max_concurrency: 10,
|
||||
timeout: :timer.seconds(15)
|
||||
)
|
||||
|> Enum.reduce(
|
||||
%{
|
||||
eve_alliance_ids: [],
|
||||
eve_character_ids: [],
|
||||
eve_corporation_ids: []
|
||||
},
|
||||
fn result, acc ->
|
||||
case result do
|
||||
{:ok, val} ->
|
||||
{:ok,
|
||||
%{
|
||||
eve_alliance_ids: eve_alliance_ids,
|
||||
eve_character_ids: eve_character_ids,
|
||||
eve_corporation_ids: eve_corporation_ids
|
||||
}} = val
|
||||
|
||||
%{
|
||||
acc
|
||||
| eve_alliance_ids: eve_alliance_ids ++ acc.eve_alliance_ids,
|
||||
eve_character_ids: eve_character_ids ++ acc.eve_character_ids,
|
||||
eve_corporation_ids: eve_corporation_ids ++ acc.eve_corporation_ids
|
||||
}
|
||||
|
||||
error ->
|
||||
Logger.error("Failed to update map #{map_id} acl: #{inspect(error, pretty: true)}")
|
||||
|
||||
acc
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
map_update = %{acls: map.acls, scope: map.scope}
|
||||
|
||||
WandererApp.Map.update_map(map_id, map_update)
|
||||
|
||||
broadcast_acl_updates({:ok, result}, map_id)
|
||||
|
||||
%{state | map: Map.merge(old_map, map_update)}
|
||||
end
|
||||
|
||||
def handle_acl_updated(map_id, acl_id) do
|
||||
{:ok, map} =
|
||||
WandererApp.MapRepo.get(map_id,
|
||||
acls: [
|
||||
:owner_id,
|
||||
members: [:role, :eve_character_id, :eve_corporation_id, :eve_alliance_id]
|
||||
]
|
||||
)
|
||||
|
||||
if map.acls |> Enum.map(& &1.id) |> Enum.member?(acl_id) do
|
||||
WandererApp.Map.update_map(map_id, %{acls: map.acls})
|
||||
|
||||
:ok =
|
||||
acl_id
|
||||
|> update_acl()
|
||||
|> broadcast_acl_updates(map_id)
|
||||
end
|
||||
end
|
||||
|
||||
def track_acls([]), do: :ok
|
||||
|
||||
def track_acls([acl_id | rest]) do
|
||||
track_acl(acl_id)
|
||||
track_acls(rest)
|
||||
end
|
||||
|
||||
defp track_acl(acl_id),
|
||||
do: @pubsub_client.subscribe(WandererApp.PubSub, "acls:#{acl_id}")
|
||||
|
||||
defp broadcast_acl_updates(
|
||||
{:ok,
|
||||
%{
|
||||
eve_character_ids: eve_character_ids,
|
||||
eve_corporation_ids: eve_corporation_ids,
|
||||
eve_alliance_ids: eve_alliance_ids
|
||||
}},
|
||||
map_id
|
||||
) do
|
||||
eve_character_ids
|
||||
|> Enum.uniq()
|
||||
|> Enum.each(fn eve_character_id ->
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{eve_character_id}",
|
||||
:update_permissions
|
||||
)
|
||||
end)
|
||||
|
||||
eve_corporation_ids
|
||||
|> Enum.uniq()
|
||||
|> Enum.each(fn eve_corporation_id ->
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"corporation:#{eve_corporation_id}",
|
||||
:update_permissions
|
||||
)
|
||||
end)
|
||||
|
||||
eve_alliance_ids
|
||||
|> Enum.uniq()
|
||||
|> Enum.each(fn eve_alliance_id ->
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"alliance:#{eve_alliance_id}",
|
||||
:update_permissions
|
||||
)
|
||||
end)
|
||||
|
||||
character_ids =
|
||||
map_id
|
||||
|> WandererApp.Map.get_map!()
|
||||
|> Map.get(:characters, [])
|
||||
|
||||
WandererApp.Cache.insert("map_#{map_id}:invalidate_character_ids", character_ids)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp broadcast_acl_updates(_, _map_id), do: :ok
|
||||
|
||||
defp update_acl(acl_id) do
|
||||
{:ok, %{owner: owner, members: members}} =
|
||||
WandererApp.AccessListRepo.get(acl_id, [:owner, :members])
|
||||
|
||||
result =
|
||||
members
|
||||
|> Enum.reduce(
|
||||
%{eve_character_ids: [owner.eve_id], eve_corporation_ids: [], eve_alliance_ids: []},
|
||||
fn member, acc ->
|
||||
case member do
|
||||
%{eve_character_id: eve_character_id} when not is_nil(eve_character_id) ->
|
||||
acc
|
||||
|> Map.put(:eve_character_ids, [eve_character_id | acc.eve_character_ids])
|
||||
|
||||
%{eve_corporation_id: eve_corporation_id} when not is_nil(eve_corporation_id) ->
|
||||
acc
|
||||
|> Map.put(:eve_corporation_ids, [eve_corporation_id | acc.eve_corporation_ids])
|
||||
|
||||
%{eve_alliance_id: eve_alliance_id} when not is_nil(eve_alliance_id) ->
|
||||
acc
|
||||
|> Map.put(:eve_alliance_ids, [eve_alliance_id | acc.eve_alliance_ids])
|
||||
|
||||
_ ->
|
||||
acc
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
{:ok, result}
|
||||
end
|
||||
end
|
||||
459
lib/wanderer_app/map/server/map_server_characters_impl.ex
Normal file
459
lib/wanderer_app/map/server/map_server_characters_impl.ex
Normal file
@@ -0,0 +1,459 @@
|
||||
defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
@moduledoc false
|
||||
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Map.Server.{Impl, ConnectionsImpl, SystemsImpl}
|
||||
|
||||
def get_characters(%{map_id: map_id} = _state),
|
||||
do: {:ok, map_id |> WandererApp.Map.list_characters()}
|
||||
|
||||
def add_character(%{map_id: map_id} = state, %{id: character_id} = character, track_character) do
|
||||
Task.start_link(fn ->
|
||||
with :ok <- map_id |> WandererApp.Map.add_character(character),
|
||||
{:ok, _} <-
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
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 ->
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
:ok
|
||||
end
|
||||
end)
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def remove_character(map_id, character_id) do
|
||||
Task.start_link(fn ->
|
||||
with :ok <- WandererApp.Map.remove_character(map_id, character_id),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
Impl.broadcast!(map_id, :character_removed, character)
|
||||
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :removed], %{count: 1})
|
||||
|
||||
:ok
|
||||
else
|
||||
{:error, _error} ->
|
||||
:ok
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def update_tracked_characters(map_id) do
|
||||
Task.start_link(fn ->
|
||||
{:ok, map_tracked_character_ids} =
|
||||
map_id
|
||||
|> WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_all()
|
||||
|> case do
|
||||
{:ok, settings} -> {:ok, settings |> Enum.map(&Map.get(&1, :character_id))}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
|
||||
{:ok, tracked_characters} = WandererApp.Cache.lookup("tracked_characters", [])
|
||||
|
||||
map_active_tracked_characters =
|
||||
map_tracked_character_ids
|
||||
|> Enum.filter(fn character -> character in tracked_characters end)
|
||||
|
||||
WandererApp.Cache.insert("maps:#{map_id}:tracked_characters", map_active_tracked_characters)
|
||||
|
||||
:ok
|
||||
end)
|
||||
end
|
||||
|
||||
def untrack_characters(map_id, character_ids),
|
||||
do:
|
||||
character_ids
|
||||
|> Enum.each(fn character_id ->
|
||||
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: false
|
||||
})
|
||||
end)
|
||||
|
||||
def cleanup_characters(map_id, owner_id) do
|
||||
{:ok, invalidate_character_ids} =
|
||||
WandererApp.Cache.lookup(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[]
|
||||
)
|
||||
|
||||
invalidate_character_ids
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
character_id
|
||||
|> WandererApp.Character.get_character()
|
||||
|> case do
|
||||
{:ok, character} ->
|
||||
acls =
|
||||
map_id
|
||||
|> WandererApp.Map.get_map!()
|
||||
|> Map.get(:acls, [])
|
||||
|
||||
[character_permissions] =
|
||||
WandererApp.Permissions.check_characters_access([character], acls)
|
||||
|
||||
map_permissions =
|
||||
WandererApp.Permissions.get_map_permissions(
|
||||
character_permissions,
|
||||
owner_id,
|
||||
[character_id]
|
||||
)
|
||||
|
||||
case map_permissions do
|
||||
%{view_system: false} ->
|
||||
{:remove_character, character_id}
|
||||
|
||||
%{track_character: false} ->
|
||||
{:remove_character, character_id}
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end,
|
||||
timeout: :timer.seconds(60),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, {:remove_character, character_id}} ->
|
||||
remove_and_untrack_characters(map_id, [character_id])
|
||||
:ok
|
||||
|
||||
{:ok, _result} ->
|
||||
:ok
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Error in cleanup_characters: #{inspect(reason)}")
|
||||
end)
|
||||
|
||||
WandererApp.Cache.insert(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[]
|
||||
)
|
||||
end
|
||||
|
||||
defp remove_and_untrack_characters(map_id, character_ids) do
|
||||
Logger.debug(fn ->
|
||||
"Map #{map_id} - remove and untrack characters #{inspect(character_ids)}"
|
||||
end)
|
||||
|
||||
map_id
|
||||
|> untrack_characters(character_ids)
|
||||
|
||||
map_id
|
||||
|> WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_filtered(character_ids)
|
||||
|> case do
|
||||
{:ok, settings} ->
|
||||
settings
|
||||
|> Enum.each(fn s ->
|
||||
WandererApp.MapCharacterSettingsRepo.untrack(s)
|
||||
remove_character(map_id, s.character_id)
|
||||
end)
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def track_characters(_map_id, []), do: :ok
|
||||
|
||||
def track_characters(map_id, [character_id | rest]) do
|
||||
track_character(map_id, character_id)
|
||||
track_characters(map_id, rest)
|
||||
end
|
||||
|
||||
def update_characters(%{map_id: map_id} = state) do
|
||||
WandererApp.Cache.lookup!("maps:#{map_id}:tracked_characters", [])
|
||||
|> Enum.map(fn character_id ->
|
||||
Task.start_link(fn ->
|
||||
character_updates =
|
||||
maybe_update_online(map_id, character_id) ++
|
||||
maybe_update_location(map_id, character_id) ++
|
||||
maybe_update_ship(map_id, character_id) ++
|
||||
maybe_update_alliance(map_id, character_id) ++
|
||||
maybe_update_corporation(map_id, character_id)
|
||||
|
||||
character_updates
|
||||
|> Enum.filter(fn update -> update != :skip end)
|
||||
|> Enum.map(fn update ->
|
||||
update
|
||||
|> case do
|
||||
{:character_location, location_info, old_location_info} ->
|
||||
update_location(
|
||||
character_id,
|
||||
location_info,
|
||||
old_location_info,
|
||||
state
|
||||
)
|
||||
|
||||
:broadcast
|
||||
|
||||
{:character_ship, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_online, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_alliance, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[character_id],
|
||||
fn ids ->
|
||||
[character_id | ids]
|
||||
end
|
||||
)
|
||||
|
||||
:broadcast
|
||||
|
||||
{:character_corporation, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[character_id],
|
||||
fn ids ->
|
||||
[character_id | ids]
|
||||
end
|
||||
)
|
||||
|
||||
:broadcast
|
||||
|
||||
_ ->
|
||||
:skip
|
||||
end
|
||||
end)
|
||||
|> Enum.filter(fn update -> update != :skip end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.each(fn update ->
|
||||
case update do
|
||||
:broadcast ->
|
||||
update_character(map_id, character_id)
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end)
|
||||
|
||||
:ok
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp update_character(map_id, character_id) do
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
Impl.broadcast!(map_id, :character_updated, character)
|
||||
end
|
||||
|
||||
defp update_location(
|
||||
character_id,
|
||||
location,
|
||||
old_location,
|
||||
%{map: map, map_id: map_id, rtree_name: rtree_name, map_opts: map_opts} = _state
|
||||
) do
|
||||
case is_nil(old_location.solar_system_id) and
|
||||
ConnectionsImpl.can_add_location(map.scope, location.solar_system_id) do
|
||||
true ->
|
||||
:ok = SystemsImpl.maybe_add_system(map_id, location, nil, rtree_name, map_opts)
|
||||
|
||||
_ ->
|
||||
ConnectionsImpl.is_connection_valid(
|
||||
map.scope,
|
||||
old_location.solar_system_id,
|
||||
location.solar_system_id
|
||||
)
|
||||
|> case do
|
||||
true ->
|
||||
:ok =
|
||||
SystemsImpl.maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
|
||||
|
||||
:ok =
|
||||
SystemsImpl.maybe_add_system(map_id, old_location, location, rtree_name, map_opts)
|
||||
|
||||
:ok =
|
||||
ConnectionsImpl.maybe_add_connection(map_id, location, old_location, character_id)
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp track_character(map_id, character_id),
|
||||
do:
|
||||
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: true,
|
||||
track_online: true,
|
||||
track_location: true,
|
||||
track_ship: true
|
||||
})
|
||||
|
||||
defp maybe_update_online(map_id, character_id) do
|
||||
with {:ok, old_online} <-
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:online"),
|
||||
{:ok, %{online: online}} <-
|
||||
WandererApp.Character.get_character(character_id) do
|
||||
case old_online != online do
|
||||
true ->
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:online",
|
||||
online
|
||||
)
|
||||
|
||||
[{:character_online, %{online: online}}]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to update online: #{inspect(error, pretty: true)}")
|
||||
[:skip]
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_ship(map_id, character_id) do
|
||||
with {:ok, old_ship_type_id} <-
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:ship_type_id"),
|
||||
{:ok, old_ship_name} <-
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:ship_name"),
|
||||
{:ok, %{ship: ship_type_id, ship_name: ship_name}} <-
|
||||
WandererApp.Character.get_character(character_id) do
|
||||
case old_ship_type_id != ship_type_id or
|
||||
old_ship_name != ship_name do
|
||||
true ->
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:ship_type_id",
|
||||
ship_type_id
|
||||
)
|
||||
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:ship_name",
|
||||
ship_name
|
||||
)
|
||||
|
||||
[{:character_ship, %{ship: ship_type_id, ship_name: ship_name}}]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to update ship: #{inspect(error, pretty: true)}")
|
||||
[:skip]
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_location(map_id, character_id) do
|
||||
WandererApp.Cache.lookup!(
|
||||
"character:#{character_id}:location_started",
|
||||
false
|
||||
)
|
||||
|> case do
|
||||
true ->
|
||||
{:ok, old_solar_system_id} =
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:solar_system_id")
|
||||
|
||||
{:ok, %{solar_system_id: solar_system_id}} =
|
||||
WandererApp.Character.get_character(character_id)
|
||||
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:solar_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
|
||||
case solar_system_id != old_solar_system_id do
|
||||
true ->
|
||||
[
|
||||
{:character_location, %{solar_system_id: solar_system_id},
|
||||
%{solar_system_id: old_solar_system_id}}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
|
||||
false ->
|
||||
{:ok, old_solar_system_id} =
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:solar_system_id")
|
||||
|
||||
{:ok, %{solar_system_id: solar_system_id} = _character} =
|
||||
WandererApp.Character.get_character(character_id)
|
||||
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:solar_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
|
||||
if is_nil(old_solar_system_id) or solar_system_id != old_solar_system_id do
|
||||
[
|
||||
{:character_location, %{solar_system_id: solar_system_id}, %{solar_system_id: nil}}
|
||||
]
|
||||
else
|
||||
[:skip]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_alliance(map_id, character_id) do
|
||||
with {:ok, old_alliance_id} <-
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:alliance_id"),
|
||||
{:ok, %{alliance_id: alliance_id}} <-
|
||||
WandererApp.Character.get_character(character_id) do
|
||||
case old_alliance_id != alliance_id do
|
||||
true ->
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:alliance_id",
|
||||
alliance_id
|
||||
)
|
||||
|
||||
[{:character_alliance, %{alliance_id: alliance_id}}]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to update alliance: #{inspect(error, pretty: true)}")
|
||||
[:skip]
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_corporation(map_id, character_id) do
|
||||
with {:ok, old_corporation_id} <-
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:corporation_id"),
|
||||
{:ok, %{corporation_id: corporation_id}} <-
|
||||
WandererApp.Character.get_character(character_id) do
|
||||
case old_corporation_id != corporation_id do
|
||||
true ->
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:corporation_id",
|
||||
corporation_id
|
||||
)
|
||||
|
||||
[{:character_corporation, %{corporation_id: corporation_id}}]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to update corporation: #{inspect(error, pretty: true)}")
|
||||
[:skip]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,6 @@
|
||||
defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
@moduledoc """
|
||||
Holds state for a map and exposes an interface to managing the map instance
|
||||
"""
|
||||
@moduledoc false
|
||||
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Map.Server.Impl
|
||||
|
||||
493
lib/wanderer_app/map/server/map_server_systems_impl.ex
Normal file
493
lib/wanderer_app/map/server/map_server_systems_impl.ex
Normal file
@@ -0,0 +1,493 @@
|
||||
defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
@moduledoc false
|
||||
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Map.Server.{Impl}
|
||||
|
||||
@ddrt Application.compile_env(:wanderer_app, :ddrt)
|
||||
@system_auto_expire_minutes 15
|
||||
@system_inactive_timeout :timer.minutes(15)
|
||||
|
||||
def init_last_activity_cache(map_id, systems_last_activity) do
|
||||
systems_last_activity
|
||||
|> Enum.each(fn {system_id, last_activity} ->
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:system_#{system_id}:last_activity",
|
||||
last_activity,
|
||||
ttl: @system_inactive_timeout
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
def init_map_systems(state, [] = _systems), do: state
|
||||
|
||||
def init_map_systems(%{map_id: map_id, rtree_name: rtree_name} = state, systems) do
|
||||
systems
|
||||
|> Enum.each(fn %{id: system_id, solar_system_id: solar_system_id} = system ->
|
||||
@ddrt.insert(
|
||||
{solar_system_id, WandererApp.Map.PositionCalculator.get_system_bounding_rect(system)},
|
||||
rtree_name
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:system_#{system_id}:last_activity",
|
||||
DateTime.utc_now(),
|
||||
ttl: @system_inactive_timeout
|
||||
)
|
||||
end)
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def add_system(
|
||||
%{map_id: map_id} = state,
|
||||
%{
|
||||
solar_system_id: solar_system_id
|
||||
} = system_info,
|
||||
user_id,
|
||||
character_id
|
||||
) do
|
||||
case map_id |> WandererApp.Map.check_location(%{solar_system_id: solar_system_id}) do
|
||||
{:ok, _location} ->
|
||||
state |> _add_system(system_info, user_id, character_id)
|
||||
|
||||
{:error, :already_exists} ->
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_systems(%{map_id: map_id} = state) do
|
||||
expired_systems =
|
||||
map_id
|
||||
|> WandererApp.Map.list_systems!()
|
||||
|> Enum.filter(fn %{
|
||||
id: system_id,
|
||||
visible: system_visible,
|
||||
locked: system_locked,
|
||||
solar_system_id: solar_system_id
|
||||
} = _system ->
|
||||
last_updated_time =
|
||||
WandererApp.Cache.get("map_#{map_id}:system_#{system_id}:last_activity")
|
||||
|
||||
if system_visible and not system_locked and
|
||||
(is_nil(last_updated_time) or
|
||||
DateTime.diff(DateTime.utc_now(), last_updated_time, :minute) >=
|
||||
@system_auto_expire_minutes) do
|
||||
no_active_connections? =
|
||||
map_id
|
||||
|> WandererApp.Map.find_connections(solar_system_id)
|
||||
|> Enum.empty?()
|
||||
|
||||
no_active_characters? =
|
||||
map_id |> WandererApp.Map.get_system_characters(solar_system_id) |> Enum.empty?()
|
||||
|
||||
no_active_connections? and no_active_characters?
|
||||
else
|
||||
false
|
||||
end
|
||||
end)
|
||||
|> Enum.map(& &1.solar_system_id)
|
||||
|
||||
case expired_systems |> Enum.empty?() do
|
||||
false ->
|
||||
state |> delete_systems(expired_systems, nil, nil)
|
||||
|
||||
_ ->
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
def update_system_name(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_name, [:name], update)
|
||||
|
||||
def update_system_description(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_description, [:description], update)
|
||||
|
||||
def update_system_status(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_status, [:status], update)
|
||||
|
||||
def update_system_tag(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_tag, [:tag], update)
|
||||
|
||||
def update_system_locked(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_locked, [:locked], update)
|
||||
|
||||
def update_system_labels(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_labels, [:labels], update)
|
||||
|
||||
def update_system_position(
|
||||
%{rtree_name: rtree_name} = state,
|
||||
update
|
||||
),
|
||||
do:
|
||||
state
|
||||
|> update_system(
|
||||
:update_position,
|
||||
[:position_x, :position_y],
|
||||
update,
|
||||
fn updated_system ->
|
||||
@ddrt.update(
|
||||
updated_system.solar_system_id,
|
||||
WandererApp.Map.PositionCalculator.get_system_bounding_rect(updated_system),
|
||||
rtree_name
|
||||
)
|
||||
end
|
||||
)
|
||||
|
||||
def add_hub(
|
||||
%{map_id: map_id} = state,
|
||||
hub_info
|
||||
) do
|
||||
with :ok <- WandererApp.Map.add_hub(map_id, hub_info),
|
||||
{:ok, hubs} = map_id |> WandererApp.Map.list_hubs(),
|
||||
{:ok, _} <-
|
||||
WandererApp.MapRepo.update_hubs(map_id, hubs) do
|
||||
Impl.broadcast!(map_id, :update_map, %{hubs: hubs})
|
||||
state
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to add hub: #{inspect(error, pretty: true)}")
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
def remove_hub(
|
||||
%{map_id: map_id} = state,
|
||||
hub_info
|
||||
) do
|
||||
with :ok <- WandererApp.Map.remove_hub(map_id, hub_info),
|
||||
{:ok, hubs} = map_id |> WandererApp.Map.list_hubs(),
|
||||
{:ok, _} <-
|
||||
WandererApp.MapRepo.update_hubs(map_id, hubs) do
|
||||
Impl.broadcast!(map_id, :update_map, %{hubs: hubs})
|
||||
state
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to remove hub: #{inspect(error, pretty: true)}")
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
def delete_systems(
|
||||
%{map_id: map_id, rtree_name: rtree_name} = state,
|
||||
removed_ids,
|
||||
user_id,
|
||||
character_id
|
||||
) do
|
||||
connections_to_remove =
|
||||
removed_ids
|
||||
|> Enum.map(fn solar_system_id ->
|
||||
WandererApp.Map.find_connections(map_id, solar_system_id)
|
||||
end)
|
||||
|> List.flatten()
|
||||
|> Enum.uniq_by(& &1.id)
|
||||
|
||||
:ok = WandererApp.Map.remove_connections(map_id, connections_to_remove)
|
||||
:ok = WandererApp.Map.remove_systems(map_id, removed_ids)
|
||||
|
||||
removed_ids
|
||||
|> Enum.each(fn solar_system_id ->
|
||||
map_id
|
||||
|> WandererApp.MapSystemRepo.remove_from_map(solar_system_id)
|
||||
|> case do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to remove system from map: #{inspect(error, pretty: true)}")
|
||||
:ok
|
||||
end
|
||||
end)
|
||||
|
||||
connections_to_remove
|
||||
|> Enum.each(fn connection ->
|
||||
Logger.debug(fn -> "Removing connection from map: #{inspect(connection)}" end)
|
||||
WandererApp.MapConnectionRepo.destroy(map_id, connection)
|
||||
end)
|
||||
|
||||
@ddrt.delete(removed_ids, rtree_name)
|
||||
|
||||
Impl.broadcast!(map_id, :remove_connections, connections_to_remove)
|
||||
Impl.broadcast!(map_id, :systems_removed, removed_ids)
|
||||
|
||||
case not is_nil(user_id) do
|
||||
true ->
|
||||
{:ok, _} =
|
||||
WandererApp.User.ActivityTracker.track_map_event(:systems_removed, %{
|
||||
character_id: character_id,
|
||||
user_id: user_id,
|
||||
map_id: map_id,
|
||||
solar_system_ids: removed_ids
|
||||
})
|
||||
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :map, :systems, :remove],
|
||||
%{count: removed_ids |> Enum.count()}
|
||||
)
|
||||
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
|
||||
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, map_opts)
|
||||
|
||||
case WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(
|
||||
map_id,
|
||||
location.solar_system_id
|
||||
) do
|
||||
{:ok, existing_system} when not is_nil(existing_system) ->
|
||||
{:ok, updated_system} =
|
||||
existing_system
|
||||
|> WandererApp.MapSystemRepo.update_position!(%{
|
||||
position_x: position.x,
|
||||
position_y: position.y
|
||||
})
|
||||
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|
||||
|> WandererApp.MapSystemRepo.update_visible!(%{visible: true})
|
||||
|> WandererApp.MapSystemRepo.cleanup_tags()
|
||||
|
||||
@ddrt.insert(
|
||||
{existing_system.solar_system_id,
|
||||
WandererApp.Map.PositionCalculator.get_system_bounding_rect(%{
|
||||
position_x: position.x,
|
||||
position_y: position.y
|
||||
})},
|
||||
rtree_name
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:system_#{updated_system.id}:last_activity",
|
||||
DateTime.utc_now(),
|
||||
ttl: @system_inactive_timeout
|
||||
)
|
||||
|
||||
WandererApp.Map.add_system(map_id, updated_system)
|
||||
|
||||
Impl.broadcast!(map_id, :add_system, updated_system)
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
{:ok, solar_system_info} =
|
||||
WandererApp.CachedInfo.get_system_static_info(location.solar_system_id)
|
||||
|
||||
WandererApp.MapSystemRepo.create(%{
|
||||
map_id: map_id,
|
||||
solar_system_id: location.solar_system_id,
|
||||
name: solar_system_info.solar_system_name,
|
||||
position_x: position.x,
|
||||
position_y: position.y
|
||||
})
|
||||
|> case do
|
||||
{:ok, new_system} ->
|
||||
@ddrt.insert(
|
||||
{new_system.solar_system_id,
|
||||
WandererApp.Map.PositionCalculator.get_system_bounding_rect(new_system)},
|
||||
rtree_name
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:system_#{new_system.id}:last_activity",
|
||||
DateTime.utc_now(),
|
||||
ttl: @system_inactive_timeout
|
||||
)
|
||||
|
||||
WandererApp.Map.add_system(map_id, new_system)
|
||||
Impl.broadcast!(map_id, :add_system, new_system)
|
||||
|
||||
:ok
|
||||
|
||||
error ->
|
||||
Logger.warning("Failed to create system: #{inspect(error, pretty: true)}")
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.debug("Skip adding system: #{inspect(error, pretty: true)}")
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_add_system(_map_id, _location, _old_location, _rtree_name, _map_opts), do: :ok
|
||||
|
||||
defp _add_system(
|
||||
%{map_id: map_id, map_opts: map_opts, rtree_name: rtree_name} = state,
|
||||
%{
|
||||
solar_system_id: solar_system_id,
|
||||
coordinates: coordinates
|
||||
} = system_info,
|
||||
user_id,
|
||||
character_id
|
||||
) do
|
||||
%{"x" => x, "y" => y} =
|
||||
coordinates
|
||||
|> case do
|
||||
%{"x" => x, "y" => y} ->
|
||||
%{"x" => x, "y" => y}
|
||||
|
||||
_ ->
|
||||
%{x: x, y: y} =
|
||||
WandererApp.Map.PositionCalculator.get_new_system_position(nil, rtree_name, map_opts)
|
||||
|
||||
%{"x" => x, "y" => y}
|
||||
end
|
||||
|
||||
{:ok, system} =
|
||||
case WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(map_id, solar_system_id) do
|
||||
{:ok, existing_system} when not is_nil(existing_system) ->
|
||||
use_old_coordinates = Map.get(system_info, :use_old_coordinates, false)
|
||||
|
||||
if use_old_coordinates do
|
||||
@ddrt.insert(
|
||||
{solar_system_id,
|
||||
WandererApp.Map.PositionCalculator.get_system_bounding_rect(%{
|
||||
position_x: existing_system.position_x,
|
||||
position_y: existing_system.position_y
|
||||
})},
|
||||
rtree_name
|
||||
)
|
||||
|
||||
existing_system
|
||||
|> WandererApp.MapSystemRepo.update_visible(%{visible: true})
|
||||
else
|
||||
@ddrt.insert(
|
||||
{solar_system_id,
|
||||
WandererApp.Map.PositionCalculator.get_system_bounding_rect(%{
|
||||
position_x: x,
|
||||
position_y: y
|
||||
})},
|
||||
rtree_name
|
||||
)
|
||||
|
||||
existing_system
|
||||
|> WandererApp.MapSystemRepo.update_position!(%{position_x: x, position_y: y})
|
||||
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|
||||
|> WandererApp.MapSystemRepo.cleanup_tags!()
|
||||
|> WandererApp.MapSystemRepo.update_visible(%{visible: true})
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:ok, solar_system_info} =
|
||||
WandererApp.CachedInfo.get_system_static_info(solar_system_id)
|
||||
|
||||
@ddrt.insert(
|
||||
{solar_system_id,
|
||||
WandererApp.Map.PositionCalculator.get_system_bounding_rect(%{
|
||||
position_x: x,
|
||||
position_y: y
|
||||
})},
|
||||
rtree_name
|
||||
)
|
||||
|
||||
WandererApp.MapSystemRepo.create(%{
|
||||
map_id: map_id,
|
||||
solar_system_id: solar_system_id,
|
||||
name: solar_system_info.solar_system_name,
|
||||
position_x: x,
|
||||
position_y: y
|
||||
})
|
||||
end
|
||||
|
||||
:ok = map_id |> WandererApp.Map.add_system(system)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:system_#{system.id}:last_activity",
|
||||
DateTime.utc_now(),
|
||||
ttl: @system_inactive_timeout
|
||||
)
|
||||
|
||||
Impl.broadcast!(map_id, :add_system, system)
|
||||
|
||||
{:ok, _} =
|
||||
WandererApp.User.ActivityTracker.track_map_event(:system_added, %{
|
||||
character_id: character_id,
|
||||
user_id: user_id,
|
||||
map_id: map_id,
|
||||
solar_system_id: solar_system_id
|
||||
})
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
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 update_system(
|
||||
%{map_id: map_id} = state,
|
||||
update_method,
|
||||
attributes,
|
||||
update,
|
||||
callback_fn \\ nil
|
||||
) do
|
||||
with :ok <- WandererApp.Map.update_system_by_solar_system_id(map_id, update),
|
||||
{:ok, system} <-
|
||||
WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(
|
||||
map_id,
|
||||
update.solar_system_id
|
||||
),
|
||||
{:ok, update_map} <- Impl.get_update_map(update, attributes) do
|
||||
{:ok, updated_system} =
|
||||
apply(WandererApp.MapSystemRepo, update_method, [
|
||||
system,
|
||||
update_map
|
||||
])
|
||||
|
||||
if not is_nil(callback_fn) do
|
||||
callback_fn.(updated_system)
|
||||
end
|
||||
|
||||
update_map_system_last_activity(map_id, updated_system)
|
||||
|
||||
state
|
||||
else
|
||||
error ->
|
||||
Logger.error("Fail ed to update system: #{inspect(error, pretty: true)}")
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
defp update_map_system_last_activity(
|
||||
map_id,
|
||||
updated_system
|
||||
) do
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:system_#{updated_system.id}:last_activity",
|
||||
DateTime.utc_now(),
|
||||
ttl: @system_inactive_timeout
|
||||
)
|
||||
|
||||
Impl.broadcast!(map_id, :update_system, updated_system)
|
||||
end
|
||||
end
|
||||
@@ -15,6 +15,8 @@ defmodule WandererApp.Permissions do
|
||||
@add_acl 1024
|
||||
@delete_acl 2048
|
||||
@delete_map 4096
|
||||
@manage_map 8192
|
||||
@admin_map 16384
|
||||
|
||||
@viewer_role [@view_system, @view_character, @view_connection]
|
||||
@member_role @viewer_role ++
|
||||
@@ -24,11 +26,10 @@ defmodule WandererApp.Permissions do
|
||||
@update_system,
|
||||
@track_character,
|
||||
@delete_connection,
|
||||
@delete_system,
|
||||
@lock_system
|
||||
@delete_system
|
||||
]
|
||||
@manager_role @member_role
|
||||
@admin_role @manager_role ++ [@add_acl, @delete_acl, @delete_map]
|
||||
@manager_role @member_role ++ [@lock_system, @manage_map]
|
||||
@admin_role @manager_role ++ [@add_acl, @delete_acl, @delete_map, @admin_map]
|
||||
|
||||
@viewer_role_mask @viewer_role |> Enum.reduce(0, fn x, acc -> x ||| acc end)
|
||||
@member_role_mask @member_role |> Enum.reduce(0, fn x, acc -> x ||| acc end)
|
||||
@@ -72,6 +73,8 @@ defmodule WandererApp.Permissions do
|
||||
|
||||
def get_permissions(user_permissions) do
|
||||
%{
|
||||
admin_map: check_permission(user_permissions, @admin_map),
|
||||
manage_map: check_permission(user_permissions, @manage_map),
|
||||
view_system: check_permission(user_permissions, @view_system),
|
||||
view_character: check_permission(user_permissions, @view_character),
|
||||
view_connection: check_permission(user_permissions, @view_connection),
|
||||
|
||||
@@ -222,7 +222,7 @@ defmodule WandererAppWeb.AccessListsLive do
|
||||
|
||||
def handle_event(
|
||||
"add_members",
|
||||
%{"member_id" => member_id} = _params,
|
||||
%{"member_id" => [member_id]} = _params,
|
||||
%{assigns: assigns} = socket
|
||||
)
|
||||
when is_binary(member_id) and member_id != "" do
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
available_option_class="w-full"
|
||||
debounce={250}
|
||||
update_min_len={3}
|
||||
mode={:tags}
|
||||
options={@member_search_options}
|
||||
placeholder="Search a character/corporation/alliance"
|
||||
>
|
||||
|
||||
@@ -74,23 +74,20 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
{:ok, character_settings} =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do
|
||||
{:ok, settings} -> {:ok, settings}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(
|
||||
show_tracking?: true,
|
||||
character_settings: character_settings
|
||||
)
|
||||
|> assign(show_tracking?: true)
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, map} =
|
||||
map_id
|
||||
|> WandererApp.MapRepo.get([:acls])
|
||||
|
||||
{:ok, character_settings} =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do
|
||||
{:ok, settings} -> {:ok, settings}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
|
||||
map
|
||||
|> WandererApp.Maps.load_characters(
|
||||
character_settings,
|
||||
@@ -122,12 +119,17 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id,
|
||||
character_settings: character_settings,
|
||||
current_user: current_user,
|
||||
only_tracked_characters: only_tracked_characters
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
{:ok, character_settings} =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do
|
||||
{:ok, settings} -> {:ok, settings}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
|
||||
socket =
|
||||
case character_settings |> Enum.find(&(&1.character_id == character_id)) do
|
||||
nil ->
|
||||
@@ -202,7 +204,6 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
socket
|
||||
|> assign(user_characters: user_character_eve_ids)
|
||||
|> assign(has_tracked_characters?: has_tracked_characters?(user_character_eve_ids))
|
||||
|> assign(character_settings: character_settings)
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, %{characters: characters}}
|
||||
end)
|
||||
|
||||
@@ -66,7 +66,7 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
|
||||
def handle_ui_event(
|
||||
"add_system",
|
||||
%{"system_id" => solar_system_id} = _event,
|
||||
%{"system_id" => [solar_system_id]} = _event,
|
||||
%{
|
||||
assigns:
|
||||
%{
|
||||
@@ -217,7 +217,7 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
current_user: current_user,
|
||||
tracked_character_ids: tracked_character_ids,
|
||||
has_tracked_characters?: true,
|
||||
user_permissions: %{update_system: true}
|
||||
user_permissions: %{update_system: true} = user_permissions
|
||||
}
|
||||
} =
|
||||
socket
|
||||
@@ -244,23 +244,25 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
_ -> :none
|
||||
end
|
||||
|
||||
apply(WandererApp.Map.Server, method_atom, [
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: "#{solar_system_id}" |> String.to_integer()
|
||||
}
|
||||
|> Map.put_new(key_atom, value)
|
||||
])
|
||||
if can_update_system?(key_atom, user_permissions) do
|
||||
apply(WandererApp.Map.Server, method_atom, [
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: "#{solar_system_id}" |> String.to_integer()
|
||||
}
|
||||
|> Map.put_new(key_atom, value)
|
||||
])
|
||||
|
||||
{:ok, _} =
|
||||
WandererApp.User.ActivityTracker.track_map_event(:system_updated, %{
|
||||
character_id: tracked_character_ids |> List.first(),
|
||||
user_id: current_user.id,
|
||||
map_id: map_id,
|
||||
solar_system_id: "#{solar_system_id}" |> String.to_integer(),
|
||||
key: key_atom,
|
||||
value: value
|
||||
})
|
||||
{:ok, _} =
|
||||
WandererApp.User.ActivityTracker.track_map_event(:system_updated, %{
|
||||
character_id: tracked_character_ids |> List.first(),
|
||||
user_id: current_user.id,
|
||||
map_id: map_id,
|
||||
solar_system_id: "#{solar_system_id}" |> String.to_integer(),
|
||||
key: key_atom,
|
||||
value: value
|
||||
})
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
@@ -305,6 +307,9 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
def handle_ui_event(event, body, socket),
|
||||
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
|
||||
|
||||
defp can_update_system?(:locked, %{lock_system: false} = _user_permissions), do: false
|
||||
defp can_update_system?(_key, _user_permissions), do: true
|
||||
|
||||
defp update_system_positions(_map_id, []), do: :ok
|
||||
|
||||
defp update_system_positions(map_id, [position | rest]) do
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
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">
|
||||
@@ -96,7 +97,7 @@
|
||||
on_cancel={JS.push("hide_tracking")}
|
||||
>
|
||||
<.async_result :let={characters} assign={@characters}>
|
||||
<:loading>Loading...</:loading>
|
||||
<:loading><span class="loading loading-dots loading-xs" />.</:loading>
|
||||
<:failed :let={reason}><%= reason %></:failed>
|
||||
|
||||
<.table
|
||||
@@ -104,7 +105,6 @@
|
||||
id="characters-tracking-table"
|
||||
class="h-[400px] !overflow-y-auto"
|
||||
rows={characters}
|
||||
|
||||
>
|
||||
<:col :let={character} label="Tracked">
|
||||
<label class="flex items-center gap-3">
|
||||
|
||||
Reference in New Issue
Block a user