Compare commits

..

1 Commits

Author SHA1 Message Date
CI
85ace4f05e chore: release version v1.10.1 2024-10-14 14:25:55 +00:00
23 changed files with 430 additions and 778 deletions

View File

@@ -2,63 +2,52 @@
<!-- changelog -->
## [v1.12.1](https://github.com/wanderer-industries/wanderer/compare/v1.12.0...v1.12.1) (2024-10-16)
## [v1.10.1](https://github.com/wanderer-industries/wanderer/compare/v1.10.0...v1.10.1) (2024-10-14)
### Bug Fixes:
* Map: Fix system add error after map page refresh
## [v1.12.0](https://github.com/wanderer-industries/wanderer/compare/v1.11.5...v1.12.0) (2024-10-16)
### Features:
* Map: Prettify user settings
## [v1.11.5](https://github.com/wanderer-industries/wanderer/compare/v1.11.4...v1.11.5) (2024-10-16)
## [v1.11.4](https://github.com/wanderer-industries/wanderer/compare/v1.11.3...v1.11.4) (2024-10-16)
## [v1.11.3](https://github.com/wanderer-industries/wanderer/compare/v1.11.2...v1.11.3) (2024-10-16)
## [v1.11.2](https://github.com/wanderer-industries/wanderer/compare/v1.11.1...v1.11.2) (2024-10-15)
## [v1.11.1](https://github.com/wanderer-industries/wanderer/compare/v1.11.0...v1.11.1) (2024-10-14)
## [v1.11.0](https://github.com/wanderer-industries/wanderer/compare/v1.10.0...v1.11.0) (2024-10-14)
### Features:
* Map: Add map level option to store custom labels
## [v1.10.0](https://github.com/wanderer-industries/wanderer/compare/v1.9.0...v1.10.0) (2024-10-13)
### Features:
* Map: Link signature on splash
## [v1.9.0](https://github.com/wanderer-industries/wanderer/compare/v1.8.0...v1.9.0) (2024-10-13)
### Features:
* Map: Link signature on splash
## [v1.8.0](https://github.com/wanderer-industries/wanderer/compare/v1.7.0...v1.8.0) (2024-10-13)
### Features:
* Map: Link signature on splash
## [v1.7.0](https://github.com/wanderer-industries/wanderer/compare/v1.6.0...v1.7.0) (2024-10-13)
### Features:
* Map: Link signature on splash
## [v1.6.0](https://github.com/wanderer-industries/wanderer/compare/v1.5.0...v1.6.0) (2024-10-13)
### Features:
* Map: Link signature on splash
@@ -68,6 +57,15 @@
### 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
@@ -81,6 +79,35 @@
* 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)
## [v1.3.0](https://github.com/wanderer-industries/wanderer/compare/v1.2.10...v1.3.0) (2024-10-07)
@@ -94,6 +121,26 @@
* Map: Revision of sorting from also adding ability to sort all columns
## [v1.2.10](https://github.com/wanderer-industries/wanderer/compare/v1.2.9...v1.2.10) (2024-10-07)
## [v1.2.9](https://github.com/wanderer-industries/wanderer/compare/v1.2.8...v1.2.9) (2024-10-07)
## [v1.2.8](https://github.com/wanderer-industries/wanderer/compare/v1.2.7...v1.2.8) (2024-10-06)
## [v1.2.7](https://github.com/wanderer-industries/wanderer/compare/v1.2.6...v1.2.7) (2024-10-05)
## [v1.2.6](https://github.com/wanderer-industries/wanderer/compare/v1.2.5...v1.2.6) (2024-10-05)
@@ -130,6 +177,11 @@
* Map: Fix map loading after select a different map.
## [v1.2.2](https://github.com/wanderer-industries/wanderer/compare/v1.2.1...v1.2.2) (2024-10-02)
## [v1.2.1](https://github.com/wanderer-industries/wanderer/compare/v1.2.0...v1.2.1) (2024-10-02)
@@ -246,10 +298,20 @@
* docker: Fix DB connection in docker-compose internal network
## [v1.0.7](https://github.com/wanderer-industries/wanderer/compare/v1.0.6...v1.0.7) (2024-09-19)
## [v1.0.6](https://github.com/wanderer-industries/wanderer/compare/v1.0.5...v1.0.6) (2024-09-18)
## [v1.0.5](https://github.com/wanderer-industries/wanderer/compare/v1.0.4...v1.0.5) (2024-09-18)
## [v1.0.4](https://github.com/wanderer-industries/wanderer/compare/v1.0.3...v1.0.4) (2024-09-18)
### Bug Fixes
* core: skip search results for failed character info request
## [v1.0.3](https://github.com/wanderer-industries/wanderer/compare/v1.0.2...v1.0.3) (2024-09-18)
## [v1.0.2](https://github.com/wanderer-industries/wanderer/compare/v1.0.1...v1.0.2) (2024-09-18)
## [v1.0.1](https://github.com/wanderer-industries/wanderer/compare/v1.0.0...v1.0.1) (2024-09-18)

View File

@@ -1,5 +1,5 @@
/* Основной класс диалога */
body .p-dialog {
.p-dialog {
display: flex;
flex-direction: column;
//position: absolute;
@@ -7,26 +7,11 @@ body .p-dialog {
left: 0;
//visibility: hidden;
overflow: hidden;
border-radius: 2px;
border-radius: 6px;
box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2);
transition: box-shadow 0.3s;
background: #fff;
z-index: 1000;
border: 1px solid #212121;
background: var(--surface-h);
color: var(--text-color);
.p-dialog-header {
background: #171717 !important;
color: var(--text-color);
.p-dialog-header-icon:focus-visible {
box-shadow: none !important;
}
}
.p-dialog-footer {
border-top: 1px solid var(--surface-d);
}
}
/* Стиль видимого диалога */
@@ -60,12 +45,12 @@ body .p-dialog {
justify-content: space-between;
padding: 1rem;
background: #f4f4f4;
//border-bottom: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
/* Содержимое диалога */
.p-dialog-content {
padding: 0.5rem;
padding: 1rem;
overflow-y: auto;
flex: 1;
}
@@ -93,3 +78,23 @@ body .p-dialog {
.p-dialog-header-close .pi {
font-size: 1.25rem;
}
/* Тема Saga Blue (пример) */
body .p-dialog {
background: var(--surface-a);
color: var(--text-color);
}
body .p-dialog .p-dialog-header,
body .p-dialog .p-dialog-footer {
background: var(--surface-b);
color: var(--text-color);
}
body .p-dialog .p-dialog-header {
border-bottom: 1px solid var(--surface-d);
}
body .p-dialog .p-dialog-footer {
border-top: 1px solid var(--surface-d);
}

View File

@@ -9,7 +9,6 @@
--surface-d: #3f4b5b;
--surface-e: #2a323d;
--surface-f: #2a323d;
--surface-h: #171717;
--text-color: rgba(255, 255, 255, 0.87);
--text-color-secondary: rgba(255, 255, 255, 0.6);
--primary-color: #8dd0ff;

View File

@@ -320,8 +320,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
></Column>
<Column
field="linked_system"
header="Leads To"
headerClassName="whitespace-nowrap"
header="Linked System"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderLinkedSystem}
style={{ maxWidth: nameColumnWidth }}
@@ -365,7 +364,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
</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
Update & Delete
</span>
</button>
</div>

View File

@@ -7,7 +7,6 @@ import { useCallback, useState } from 'react';
import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components';
import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx';
import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu';
import { MapSettings } from "@/hooks/Mapper/components/mapRootContent/components/MapSettings";
export interface MapRootContentProps {}
@@ -17,11 +16,9 @@ export const MapRootContent = ({}: MapRootContentProps) => {
const { isShowMenu } = interfaceSettings;
const [showOnTheMap, setShowOnTheMap] = useState(false);
const [showMapSettings, setShowMapSettings] = useState(false);
const mapInterface = <MapInterface />;
const handleShowOnTheMap = useCallback(() => setShowOnTheMap(true), []);
const handleShowMapSettings = useCallback(() => setShowMapSettings(true), []);
useSkipContextMenu();
@@ -34,19 +31,18 @@ export const MapRootContent = ({}: MapRootContentProps) => {
{mapInterface}
</div>
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
<RightBar onShowOnTheMap={handleShowOnTheMap} />
</div>
</div>
) : (
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
<Topbar>
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} />
</Topbar>
{mapInterface}
</div>
)}
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
</Layout>
);
};

View File

@@ -8,11 +8,10 @@ import { MenuItem } from 'primereact/menuitem';
export interface MapContextMenuProps {
onShowOnTheMap?: () => void;
onShowMapSettings?: () => void;
}
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
const { outCommand, setInterfaceSettings } = useMapRootState();
export const MapContextMenu = ({ onShowOnTheMap }: MapContextMenuProps) => {
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const menuRight = useRef<Menu>(null);
@@ -23,6 +22,13 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
});
}, [outCommand]);
const toggleMinimap = useCallback(() => {
setInterfaceSettings(x => ({
...x,
isShowMinimap: !x.isShowMinimap,
}));
}, [setInterfaceSettings]);
const items = useMemo(() => {
return [
{
@@ -37,9 +43,9 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
},
{ separator: true },
{
label: 'Settings',
icon: `pi pi-cog`,
command: onShowMapSettings,
label: interfaceSettings.isShowMinimap ? 'Hide minimap' : 'Show minimap',
icon: `pi ${interfaceSettings.isShowMinimap ? 'pi-eye-slash' : 'pi-eye'}`,
command: toggleMinimap,
},
{
label: 'Dock menu',
@@ -51,7 +57,7 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
})),
},
] as MenuItem[];
}, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
}, [handleAddCharacter, interfaceSettings.isShowMinimap, onShowOnTheMap, setInterfaceSettings, toggleMinimap]);
return (
<div className="ml-1">

View File

@@ -1,127 +0,0 @@
.verticalTabsContainer {
display: flex;
width: 100%;
min-height: 300px;
:global {
.p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
}
.p-tabview-nav-container {
border-right: none;
height: 100%;
}
.p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition: background-color 200ms, border-right-color 200ms;
&:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.p-tabview-nav-link {
transition: color 200ms;
justify-content: flex-end;
padding: 10px;
//background-color: var(--surface-card);
background-color: initial;
border: none;
color: var(--gray-400);
border-radius: initial;
font-weight: 400;
margin: 0;
}
&.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
.p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
&:hover {
//background-color: var(--surface-hover);
border-right: 4px solid var(--primary-color);
}
}
}
}
.p-tabview-panel {
flex-grow: 1;
}
}
}
.CheckboxContainer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
& > span:nth-child(1) {
color: var(--gray-200);
font-size: 13px;
}
& > :nth-child(2){
border-bottom: 2px dotted #3f3f3f;
height: 2px;
margin: 0 12px;
}
}
/* Уменьшение размеров InputSwitch с использованием глобальных стилей */
.smallInputSwitch {
height: 100%;
display: flex;
align-items: center;
:global {
.p-inputswitch {
height: 1rem;
width: 2rem;
&.p-inputswitch-checked {
.p-inputswitch-slider::before {
transform: translateX(1rem);
}
}
&.p-highlight .p-inputswitch-slider:before {
transform: translateX(1rem);
}
.p-inputswitch-slider::before {
width: 0.8rem;
height: 0.8rem;
margin-top: -0.4rem;
margin-left: -3px;
}
}
}
}

View File

@@ -1,167 +0,0 @@
import styles from './MapSettings.module.scss';
import { Dialog } from 'primereact/dialog';
import { useCallback, useMemo, useState } from 'react';
import { TabPanel, TabView } from 'primereact/tabview';
import { PrettySwitchbox } from './components';
import { InterfaceStoredSettings, InterfaceStoredSettingsProps, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { OutCommand } from '@/hooks/Mapper/types';
export enum UserSettingsRemoteProps {
link_signature_on_splash = 'link_signature_on_splash',
select_on_spash = 'select_on_spash',
}
export const DEFAULT_REMOTE_SETTINGS = {
[UserSettingsRemoteProps.link_signature_on_splash]: false,
[UserSettingsRemoteProps.select_on_spash]: false,
};
export const UserSettingsRemoteList = [
UserSettingsRemoteProps.link_signature_on_splash,
UserSettingsRemoteProps.select_on_spash,
];
export type UserSettingsRemote = {
link_signature_on_splash: boolean;
select_on_spash: boolean;
};
export type UserSettings = UserSettingsRemote & InterfaceStoredSettings;
export interface MapSettingsProps {
show: boolean;
onHide: () => void;
}
type CheckboxesList = {
prop: keyof UserSettings;
label: string;
}[];
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
];
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' },
{ prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' },
];
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
];
const UI_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
];
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({ ...DEFAULT_REMOTE_SETTINGS });
const mergedSettings = useMemo(() => {
return {
...interfaceSettings,
...userRemoteSettings,
};
}, [userRemoteSettings, interfaceSettings]);
const handleShow = async () => {
const { user_settings } = await outCommand({
type: OutCommand.getUserSettings,
data: null,
});
setUserRemoteSettings({
...user_settings,
});
};
const handleChangeChecked = useCallback(
(prop: keyof UserSettings) => async (checked: boolean) => {
// @ts-ignore
if (UserSettingsRemoteList.includes(prop)) {
const newRemoteSettings = {
...userRemoteSettings,
[prop]: checked,
};
await outCommand({
type: OutCommand.updateUserSettings,
data: newRemoteSettings,
});
setUserRemoteSettings(newRemoteSettings);
return;
}
setInterfaceSettings({
...interfaceSettings,
[prop]: checked,
});
},
[interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings],
);
const renderCheckboxesList = (list: CheckboxesList) => {
return list.map(x => {
return (
<PrettySwitchbox
key={x.prop}
label={x.label}
checked={mergedSettings[x.prop]}
setChecked={handleChangeChecked(x.prop)}
/>
);
});
};
return (
<Dialog
header="Map settings"
visible={show}
draggable={false}
style={{ width: '550px' }}
onShow={handleShow}
onHide={() => {
if (!show) {
return;
}
setActiveIndex(0);
onHide();
}}
>
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
<div className={styles.verticalTabsContainer}>
<TabView
activeIndex={activeIndex}
onTabChange={e => setActiveIndex(e.index)}
className={styles.verticalTabView}
>
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">{renderCheckboxesList(COMMON_CHECKBOXES_PROPS)}</div>
</TabPanel>
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)}
</div>
</TabPanel>
<TabPanel disabled header="Connections" headerClassName={styles.verticalTabHeader}>
<p>Connections</p>
</TabPanel>
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(UI_CHECKBOXES_PROPS)}
</TabPanel>
</TabView>
</div>
</div>
</div>
</Dialog>
);
};

View File

@@ -1,48 +0,0 @@
.CheckboxContainer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
& > span:nth-child(1) {
color: var(--gray-200);
font-size: 13px;
user-select: none;
}
& > :nth-child(2){
border-bottom: 2px dotted #3f3f3f;
height: 1px;
margin: 0 12px;
}
}
/* Уменьшение размеров InputSwitch с использованием глобальных стилей */
.smallInputSwitch {
height: 100%;
display: flex;
align-items: center;
:global {
.p-inputswitch {
height: 1rem;
width: 2rem;
&.p-inputswitch-checked {
.p-inputswitch-slider::before {
transform: translateX(1rem);
}
}
&.p-highlight .p-inputswitch-slider:before {
transform: translateX(1rem);
}
.p-inputswitch-slider::before {
width: 0.8rem;
height: 0.8rem;
margin-top: -0.4rem;
margin-left: -3px;
}
}
}
}

View File

@@ -1,20 +0,0 @@
import styles from './MapSettings.module.scss';
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
interface PrettySwitchboxProps {
checked: boolean;
setChecked: (checked: boolean) => void;
label: string;
}
export const PrettySwitchbox = ({ checked, setChecked, label }: PrettySwitchboxProps) => {
return (
<label className={styles.CheckboxContainer}>
<span>{label}</span>
<div />
<div className={styles.smallInputSwitch}>
<WdCheckbox size="m" label={''} value={checked} onChange={e => setChecked(e.checked ?? false)} />
</div>
</label>
);
};

View File

@@ -1 +0,0 @@
export * from './PrettySwitchbox';

View File

@@ -1 +0,0 @@
export * from './MapSettings';

View File

@@ -8,10 +8,9 @@ import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
interface RightBarProps {
onShowOnTheMap?: () => void;
onShowMapSettings?: () => void;
}
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
export const RightBar = ({ onShowOnTheMap }: RightBarProps) => {
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
@@ -23,6 +22,13 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) =
});
}, [outCommand]);
const handleOpenUserSettings = useCallback(() => {
outCommand({
type: OutCommand.openUserSettings,
data: null,
});
}, [outCommand]);
const toggleMinimap = useCallback(() => {
setInterfaceSettings(x => ({
...x,
@@ -64,6 +70,16 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: 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"></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"
@@ -76,16 +92,6 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) =
</div>
<div className="flex flex-col items-center mb-2 gap-1">
<WdTooltipWrapper content="User settings" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={onShowMapSettings}
>
<i className="pi pi-cog"></i>
</button>
</WdTooltipWrapper>
<WdTooltipWrapper
content={
interfaceSettings.isShowKSpace ? 'Hide highlighting Imperial Space' : 'Show highlighting Imperial Space'

View File

@@ -26,13 +26,7 @@ const INITIAL_DATA: MapRootData = {
selectedConnections: [],
};
export enum InterfaceStoredSettingsProps {
isShowMenu = 'isShowMenu',
isShowMinimap = 'isShowMinimap',
isShowKSpace = 'isShowKSpace',
}
export type InterfaceStoredSettings = {
type InterfaceStoredSettings = {
isShowMenu: boolean;
isShowMinimap: boolean;
isShowKSpace: boolean;

View File

@@ -140,9 +140,6 @@ export enum OutCommand {
// Only UI commands
openSettings = 'open_settings',
getUserSettings = 'get_user_settings',
updateUserSettings = 'update_user_settings',
}
export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>;

View File

@@ -149,7 +149,7 @@ defmodule WandererApp.Map.Server.Impl do
WandererApp.Cache.insert("map_#{map_id}:started", true)
broadcast!(map_id, :map_server_started)
broadcast!(map_id, :map_started)
:telemetry.execute([:wanderer_app, :map, :started], %{count: 1})

View File

@@ -15,23 +15,10 @@ defmodule WandererApp.MapRepo do
end
end
def get_by_slug_with_permissions(map_slug, current_user),
do:
map_slug
|> WandererApp.Api.Map.get_map_by_slug()
|> load_user_permissions(current_user)
def load_relationships(map, []), do: {:ok, map}
def load_relationships(map, relationships), do: map |> Ash.load(relationships)
defp load_user_permissions({:ok, map}, current_user),
do:
map
|> Ash.load([:acls, :user_permissions], actor: current_user)
defp load_user_permissions(error, _current_user), do: error
def update_hubs(map_id, hubs) do
map_id
|> WandererApp.Api.Map.by_id()
@@ -49,9 +36,7 @@ defmodule WandererApp.MapRepo do
map
|> WandererApp.Api.Map.update_options(%{options: Jason.encode!(options)})
def options_to_form_data(%{options: options} = _map_options) when not is_nil(options),
do: {:ok, Jason.decode!(options)}
def options_to_form_data(%{options: options} = _map_options) when not is_nil(options), do: {:ok, Jason.decode!(options)}
def options_to_form_data(_), do: {:ok, @default_map_options}
def options_to_form_data!(options) do

View File

@@ -38,9 +38,7 @@ defmodule WandererApp.MapSystemRepo do
end
def cleanup_labels(%{labels: labels} = system, opts) do
store_custom_labels? =
Keyword.get(opts, :store_custom_labels, "false") |> String.to_existing_atom()
store_custom_labels? = Keyword.get(opts, :store_custom_labels, "false") |> String.to_existing_atom()
labels = get_filtered_labels(labels, store_custom_labels?)
system
@@ -56,12 +54,10 @@ defmodule WandererApp.MapSystemRepo do
%{"customLabel" => customLabel} = labels when is_binary(customLabel) ->
%{"customLabel" => customLabel, "labels" => []}
|> Jason.encode!()
_ ->
nil
end
end
def get_filtered_labels(_, _store_custom_labels), do: nil
def update_name(system, update),

View File

@@ -62,7 +62,6 @@ defmodule WandererAppWeb do
use Phoenix.LiveView, @opts
unquote(html_helpers())
defguard is_connected?(socket) when socket.transport_pid != nil
end
end

View File

@@ -4,14 +4,57 @@ defmodule WandererAppWeb.MapLive do
require Logger
@impl true
def mount(params, _session, socket) when is_connected?(socket) do
def mount(params, _session, socket) do
socket =
with %{"slug" => map_slug} <- params do
Process.send_after(self(), {:load_map, map_slug}, Enum.random(10..200))
socket
|> _init_state(map_slug)
else
_ ->
# redirect back to main
socket
|> assign(
map_loaded?: false,
maps_loading: false,
selected_subscription: nil,
maps: [],
map: nil,
map_id: nil,
map_slug: nil,
user_permissions: nil,
form: to_form(%{"map_slug" => nil})
)
end
{:ok, socket |> assign(server_online: false)}
end
defp _init_state(socket, map_slug) do
current_user = socket.assigns.current_user
ErrorTracker.set_context(%{user_id: current_user.id})
Task.async(fn -> _get_available_maps(current_user) end)
map_slug
|> WandererApp.Api.Map.get_map_by_slug()
|> _load_user_permissions(current_user)
|> case do
{:ok,
%{
id: map_id,
deleted: false
} = map} ->
Process.send_after(self(), {:init_map, map}, 10)
socket
|> assign(
map: map,
map_id: map_id,
map_loaded?: false,
maps_loading: true,
maps: [],
user_permissions: nil,
selected_subscription: nil,
map_slug: map_slug,
form: to_form(%{"map_slug" => map_slug})
)
@@ -20,42 +63,26 @@ defmodule WandererAppWeb.MapLive do
attr: "data-loading",
timeout: 2000
})
else
_ ->
# redirect back to main
socket
|> assign(
maps_loading: false,
map_id: nil,
map_slug: nil,
form: to_form(%{"map_slug" => nil})
)
end
{:ok,
socket
|> assign(
map_loaded?: false,
maps: [],
server_online: false,
selected_subscription: nil,
user_permissions: nil
)}
end
{:ok,
%{
deleted: true
} = _map} ->
socket
|> put_flash(
:error,
"Map was deleted by owner or administrator."
)
|> push_navigate(to: ~p"/maps")
@impl true
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(
maps_loading: false,
map_loaded?: false,
maps: [],
server_online: false,
selected_subscription: nil,
user_permissions: nil,
form: to_form(%{"map_slug" => nil})
)}
{:error, _} ->
socket
|> put_flash(
:error,
"Something went wrong. Please try one more time or submit an issue."
)
|> push_navigate(to: ~p"/maps")
end
end
@impl true
@@ -63,12 +90,33 @@ defmodule WandererAppWeb.MapLive do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:active_page, :map)
end
defp apply_action(socket, :add_system, _params) do
socket
|> assign(:active_page, :map)
|> assign(:page_title, "Add System")
|> assign(:add_system_form, to_form(%{"system_id" => nil}))
end
@impl true
def handle_info(
%{event: :map_server_started},
socket
),
do: {:noreply, socket |> handle_map_server_started()}
%{event: :map_started},
%{
assigns: %{
current_user: current_user,
map_id: map_id,
user_permissions: user_permissions
}
} = socket
) do
on_map_started(map_id, current_user, user_permissions)
{:noreply, socket}
end
@impl true
def handle_info(:character_token_invalid, socket),
@@ -387,49 +435,7 @@ defmodule WandererAppWeb.MapLive do
{:noreply, socket}
end
def handle_info({:load_map, map_slug}, %{assigns: %{current_user: current_user}} = socket) do
ErrorTracker.set_context(%{user_id: current_user.id})
Task.async(fn -> _get_available_maps(current_user) end)
map_slug
|> WandererApp.MapRepo.get_by_slug_with_permissions(current_user)
|> case do
{:ok,
%{
id: map_id,
deleted: false
} = map} ->
Process.send_after(self(), {:init_map, map}, 10)
{:noreply, socket}
{:ok,
%{
deleted: true
} = _map} ->
{:noreply,
socket
|> put_flash(
:error,
"Map was deleted by owner or administrator."
)
|> push_navigate(to: ~p"/maps")}
{:error, _} ->
{:noreply,
socket
|> put_flash(
:error,
"Something went wrong. Please try one more time or submit an issue."
)
|> push_navigate(to: ~p"/maps")}
end
end
def handle_info(
{:init_map, map},
%{assigns: %{current_user: current_user, map_slug: map_slug}} = socket
) do
def handle_info({:init_map, map}, %{assigns: %{current_user: current_user}} = socket) do
with %{
id: map_id,
deleted: false,
@@ -467,36 +473,126 @@ defmodule WandererAppWeb.MapLive do
cond do
(only_tracked_characters and can_track? and all_character_tracked?) or
(not only_tracked_characters and can_view?) ->
Phoenix.PubSub.subscribe(WandererApp.PubSub, map_id)
{:ok, ui_loaded} = WandererApp.Cache.get_and_remove("map_#{map_slug}:ui_loaded", false)
if ui_loaded do
maybe_start_map(map_id)
end
{:noreply,
socket
|> assign(
map_id: map_id,
page_title: map_name,
user_permissions: user_permissions,
tracked_character_ids: tracked_character_ids,
only_tracked_characters: only_tracked_characters
)}
Process.send_after(
self(),
{:map_init,
%{
map_id: map_id,
page_title: map_name,
user_permissions: user_permissions,
tracked_character_ids: tracked_character_ids
}},
10
)
only_tracked_characters and can_track? and not all_character_tracked? ->
Process.send_after(self(), :not_all_characters_tracked, 10)
{:noreply, socket}
true ->
Process.send_after(self(), :no_permissions, 10)
{:noreply, socket}
end
else
_ ->
Process.send_after(self(), :no_access, 10)
{:noreply, socket}
end
{:noreply, socket}
end
def handle_info({:map_init, %{map_id: map_id} = initial_data}, socket) do
Phoenix.PubSub.subscribe(WandererApp.PubSub, map_id)
{:noreply,
socket
|> assign(initial_data)}
end
def handle_info(
{:map_start,
%{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
} = _started_data},
socket
) do
socket =
events
|> Enum.reduce(socket, fn event, socket ->
case event do
{:track_characters, map_characters, track_character} ->
:ok = _track_characters(map_characters, map_id, track_character)
:ok = _add_characters(map_characters, map_id, track_character)
socket
:invalid_token_message ->
socket
|> put_flash(
:error,
"One of your characters has expired token. Please refresh it on characters page."
)
:empty_tracked_characters ->
socket
|> put_flash(
:info,
"You should enable tracking for at least one character to work with map."
)
:map_character_limit ->
socket
|> put_flash(
:error,
"Map reached its character limit, your characters won't be tracked. Please contact administrator."
)
_ ->
socket
end
end)
Process.send_after(
self(),
{:map_loaded,
%{
map_id: map_id,
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)
)}
end
def handle_info(
{:map_loaded,
%{
map_id: map_id,
initial_data: initial_data
} = _loaded_data},
socket
) do
map_characters = map_id |> WandererApp.Map.list_characters()
{:noreply,
socket
|> assign(map_loaded?: true)
|> push_map_event(
"init",
initial_data |> Map.put(:characters, map_characters |> Enum.map(&map_ui_character/1))
)
|> push_event("js-exec", %{
to: "#map-loader",
attr: "data-loaded"
})}
end
def handle_info(:no_access, socket),
@@ -540,6 +636,10 @@ defmodule WandererAppWeb.MapLive do
maps: maps
)}
{:map_started_data, started_data} ->
Process.send_after(self(), {:map_start, started_data}, 100)
{:noreply, socket}
{:map_error, map_error} ->
Process.send_after(self(), map_error, 100)
{:noreply, socket}
@@ -571,15 +671,13 @@ defmodule WandererAppWeb.MapLive do
def handle_info(_event, socket), do: {:noreply, socket}
@impl true
def handle_event("ui_loaded", _body, %{assigns: %{map_slug: map_slug} = assigns} = socket) do
assigns
|> Map.get(:map_id)
|> case do
map_id when not is_nil(map_id) ->
maybe_start_map(map_id)
def handle_event("ui_loaded", _body, %{assigns: %{map_id: map_id}} = socket) do
{:ok, map_started} = WandererApp.Cache.lookup("map_#{map_id}:started", false)
_ ->
WandererApp.Cache.insert("map_#{map_slug}:ui_loaded", true)
if map_started do
Process.send_after(self(), %{event: :map_started}, 10)
else
WandererApp.Map.Manager.start_map(map_id)
end
{:noreply, socket}
@@ -1330,24 +1428,14 @@ defmodule WandererAppWeb.MapLive do
end
@impl true
def handle_event(
"add_character",
_,
%{
assigns: %{
current_user: current_user,
map_id: map_id,
user_permissions: user_permissions
}
} = socket
) do
def handle_event("add_character", _, %{assigns: assigns} = socket) do
{:ok, character_settings} =
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map_id}) do
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: assigns.map_id}) do
{:ok, settings} -> {:ok, settings}
_ -> {:ok, []}
end
case user_permissions.track_character do
case assigns.user_permissions.track_character do
true ->
{:noreply,
socket
@@ -1356,14 +1444,10 @@ defmodule WandererAppWeb.MapLive do
character_settings: character_settings
)
|> assign_async(:characters, fn ->
{:ok, map} =
map_id
|> WandererApp.MapRepo.get([:acls])
map
|> WandererApp.Maps.load_characters(
WandererApp.Maps.load_characters(
assigns.map |> Ash.load!(:acls),
character_settings,
current_user.id
assigns.current_user.id
)
end)}
@@ -1383,33 +1467,24 @@ defmodule WandererAppWeb.MapLive do
end
@impl true
def handle_event(
"toggle_track",
%{"character-id" => character_id},
%{
assigns: %{
map_id: map_id,
map_slug: map_slug,
character_settings: character_settings,
current_user: current_user,
only_tracked_characters: only_tracked_characters
}
} = socket
) do
def handle_event("toggle_track", %{"character-id" => character_id}, socket) do
map = socket.assigns.map
character_settings = socket.assigns.character_settings
socket =
case character_settings |> Enum.find(&(&1.character_id == character_id)) do
nil ->
{:ok, map_character_settings} =
WandererApp.Api.MapCharacterSettings.create(%{
character_id: character_id,
map_id: map_id,
map_id: map.id,
tracked: true
})
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
:ok = _track_characters([character], map_id, true)
:ok = _add_characters([character], map_id, true)
:ok = _track_characters([character], map.id, true)
:ok = _add_characters([character], map.id, true)
socket
@@ -1422,16 +1497,16 @@ defmodule WandererAppWeb.MapLive do
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
:ok = _untrack_characters([character], map_id)
:ok = _remove_characters([character], map_id)
:ok = _untrack_characters([character], map.id)
:ok = _remove_characters([character], map.id)
if only_tracked_characters do
if map.only_tracked_characters do
socket
|> put_flash(
:error,
"You should enable tracking for all characters that have access to this map first!"
)
|> push_navigate(to: ~p"/tracking/#{map_slug}")
|> push_navigate(to: ~p"/tracking/#{map.slug}")
else
socket
end
@@ -1443,8 +1518,8 @@ defmodule WandererAppWeb.MapLive do
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
:ok = _track_characters([character], map_id, true)
:ok = _add_characters([character], map_id, true)
:ok = _track_characters([character], map.id, true)
:ok = _add_characters([character], map.id, true)
socket
end
@@ -1452,12 +1527,12 @@ defmodule WandererAppWeb.MapLive do
%{result: characters} = socket.assigns.characters
{:ok, map_characters} = _get_tracked_map_characters(map_id, current_user)
{:ok, map_characters} = _get_tracked_map_characters(map.id, socket.assigns.current_user)
user_character_eve_ids = map_characters |> Enum.map(& &1.eve_id)
{:ok, character_settings} =
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map_id}) do
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map.id}) do
{:ok, settings} -> {:ok, settings}
_ -> {:ok, []}
end
@@ -1506,19 +1581,6 @@ defmodule WandererAppWeb.MapLive do
)}
end
@impl true
def handle_event(
"get_user_settings",
_,
%{assigns: %{map_id: map_id, current_user: current_user}} = socket
) do
{:ok, user_settings} =
WandererApp.MapUserSettingsRepo.get!(map_id, current_user.id)
|> WandererApp.MapUserSettingsRepo.to_form_data()
{:reply, %{user_settings: user_settings}, socket}
end
@impl true
def handle_event(
"update_user_settings",
@@ -1642,106 +1704,10 @@ defmodule WandererAppWeb.MapLive do
{:noreply, socket}
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:active_page, :map)
end
defp apply_action(socket, :add_system, _params) do
socket
|> assign(:active_page, :map)
|> assign(:page_title, "Add System")
|> assign(:add_system_form, to_form(%{"system_id" => nil}))
end
defp maybe_start_map(map_id) do
{:ok, map_server_started} = WandererApp.Cache.lookup("map_#{map_id}:started", false)
if map_server_started do
Process.send_after(self(), %{event: :map_server_started}, 10)
else
WandererApp.Map.Manager.start_map(map_id)
end
end
defp handle_map_start_events(socket, map_id, events) do
events
|> Enum.reduce(socket, fn event, socket ->
case event do
{:track_characters, map_characters, track_character} ->
:ok = _track_characters(map_characters, map_id, track_character)
:ok = _add_characters(map_characters, map_id, track_character)
socket
:invalid_token_message ->
socket
|> put_flash(
:error,
"One of your characters has expired token. Please refresh it on characters page."
)
:empty_tracked_characters ->
socket
|> put_flash(
:info,
"You should enable tracking for at least one character to work with map."
)
:map_character_limit ->
socket
|> put_flash(
:error,
"Map reached its character limit, your characters won't be tracked. Please contact administrator."
)
_ ->
socket
end
end)
end
defp map_start(
socket,
%{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
} = _started_data
) do
socket =
socket
|> handle_map_start_events(map_id, events)
map_characters = map_id |> WandererApp.Map.list_characters()
socket
|> assign(
map_loaded?: true,
map_user_settings: map_user_settings,
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))
)
|> push_event("js-exec", %{
to: "#map-loader",
attr: "data-loaded"
})
end
defp handle_map_server_started(
%{
assigns: %{
current_user: current_user,
map_id: map_id,
user_permissions:
%{view_system: true, track_character: track_character} = user_permissions
}
} = socket
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),
@@ -1820,27 +1786,27 @@ defmodule WandererAppWeb.MapLive do
)
|> Map.put(:reset, true)
socket
|> map_start(%{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
})
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)
socket
end
end
defp handle_map_server_started(socket) do
Process.send_after(self(), :no_access, 10)
socket
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,
@@ -1865,6 +1831,13 @@ defmodule WandererAppWeb.MapLive do
end
end
defp _load_user_permissions({:ok, map}, current_user) do
map
|> Ash.load([:acls, :user_permissions], actor: current_user)
end
defp _load_user_permissions(error, _current_user), do: error
defp _get_map_data(map_id, include_static_data? \\ true) do
{:ok, hubs} = map_id |> WandererApp.Map.list_hubs()
{:ok, connections} = map_id |> WandererApp.Map.list_connections()

View File

@@ -49,7 +49,7 @@
</div>
<.modal
:if={@live_action in [:add_system] && not is_nil(assigns |> Map.get(:map_slug)) && @map_loaded?}
:if={@live_action in [:add_system]}
id="add-system-modal"
class="!w-[400px]"
title="Add System"

View File

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