mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-28 12:03:22 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e526040bf | ||
|
|
869c25cd60 | ||
|
|
6aac698cd8 | ||
|
|
230016b90f | ||
|
|
4b1aef8dd9 | ||
|
|
d34509d7a0 | ||
|
|
fca98ec232 | ||
|
|
e2814e95bd | ||
|
|
68a3f84704 | ||
|
|
4bc76feefc | ||
|
|
da39a55fd0 | ||
|
|
ee3cf04cd4 | ||
|
|
d79e7fe2ff | ||
|
|
8de9fdef32 | ||
|
|
f51deeec2d | ||
|
|
a971c69a96 | ||
|
|
b7995f50de | ||
|
|
14997a2959 | ||
|
|
8fef6bcf82 | ||
|
|
1f82d23963 | ||
|
|
28317a2431 | ||
|
|
6aac496a57 | ||
|
|
ac9306b713 | ||
|
|
d55e804efa | ||
|
|
08407a5679 | ||
|
|
c37d175bec | ||
|
|
69c5326e72 | ||
|
|
305f63e11d | ||
|
|
698fd5e083 | ||
|
|
1af8342d30 | ||
|
|
68b59da78e | ||
|
|
e784a3f850 | ||
|
|
a45e2f3fc2 | ||
|
|
8a3d920c31 |
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -78,22 +78,23 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: 😅 Cache deps
|
||||
id: cache-deps
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-elixir-deps
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
|
||||
path: |
|
||||
deps
|
||||
key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('**/mix.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-mix-${{ env.cache-name }}-
|
||||
${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-
|
||||
- name: 😅 Cache compiled build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-compiled-build
|
||||
with:
|
||||
path: |
|
||||
**/_build
|
||||
_build
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}-${{ hashFiles( '**/lib/**/*.{ex,eex}', '**/config/*.exs', '**/mix.exs' ) }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}-
|
||||
@@ -187,6 +188,8 @@ jobs:
|
||||
push: true
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,"name=${{ env.REGISTRY_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
133
CHANGELOG.md
133
CHANGELOG.md
@@ -2,6 +2,139 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.43.1](https://github.com/wanderer-industries/wanderer/compare/v1.43.0...v1.43.1) (2025-01-20)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.43.0](https://github.com/wanderer-industries/wanderer/compare/v1.42.5...v1.43.0) (2025-01-20)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* add news post for structures widget (#131)
|
||||
|
||||
## [v1.42.5](https://github.com/wanderer-industries/wanderer/compare/v1.42.4...v1.42.5) (2025-01-20)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix link signatures on splash. Fix deleting connection on locked system remove.
|
||||
|
||||
## [v1.42.4](https://github.com/wanderer-industries/wanderer/compare/v1.42.3...v1.42.4) (2025-01-20)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Fix system statics list (required EVE DB data update). Add system name to signature added/removed audit log
|
||||
|
||||
## [v1.42.3](https://github.com/wanderer-industries/wanderer/compare/v1.42.2...v1.42.3) (2025-01-17)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* change structure tooltip to avoid paste confusion (#125)
|
||||
|
||||
* change structure tooltip to avoid paste confusion
|
||||
|
||||
* clarify use of evetime and use primereact calendar
|
||||
|
||||
## [v1.42.2](https://github.com/wanderer-industries/wanderer/compare/v1.42.1...v1.42.2) (2025-01-16)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.42.1](https://github.com/wanderer-industries/wanderer/compare/v1.42.0...v1.42.1) (2025-01-16)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Remove linked sig ID if system containing signature removed from map
|
||||
|
||||
## [v1.42.0](https://github.com/wanderer-industries/wanderer/compare/v1.41.0...v1.42.0) (2025-01-16)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Audit: Add 'Signatures added/removed' map audit events
|
||||
|
||||
## [v1.41.0](https://github.com/wanderer-industries/wanderer/compare/v1.40.7...v1.41.0) (2025-01-16)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Audit: Add 'ACL added/removed' map audit events
|
||||
|
||||
## [v1.40.7](https://github.com/wanderer-industries/wanderer/compare/v1.40.6...v1.40.7) (2025-01-15)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.40.6](https://github.com/wanderer-industries/wanderer/compare/v1.40.5...v1.40.6) (2025-01-15)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix follow mode
|
||||
|
||||
* center system is not selected text for structures (#122)
|
||||
|
||||
* Map: Fix system revert issues
|
||||
|
||||
* Map: Fix issues with splashing signatures select & sig ID in temp names
|
||||
|
||||
## [v1.40.5](https://github.com/wanderer-industries/wanderer/compare/v1.40.4...v1.40.5) (2025-01-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix follow mode
|
||||
|
||||
## [v1.40.4](https://github.com/wanderer-industries/wanderer/compare/v1.40.3...v1.40.4) (2025-01-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* center system is not selected text for structures (#122)
|
||||
|
||||
## [v1.40.3](https://github.com/wanderer-industries/wanderer/compare/v1.40.2...v1.40.3) (2025-01-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix system revert issues
|
||||
|
||||
## [v1.40.2](https://github.com/wanderer-industries/wanderer/compare/v1.40.1...v1.40.2) (2025-01-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix issues with splashing signatures select & sig ID in temp names
|
||||
|
||||
## [v1.40.1](https://github.com/wanderer-industries/wanderer/compare/v1.40.0...v1.40.1) (2025-01-14)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo }
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
Edge,
|
||||
EdgeChange,
|
||||
MiniMap,
|
||||
Node,
|
||||
NodeChange,
|
||||
@@ -83,6 +84,7 @@ interface MapCompProps {
|
||||
onCommand: OutCommandHandler;
|
||||
onSelectionChange: OnMapSelectionChange;
|
||||
onManualDelete(systems: string[]): void;
|
||||
canRemoveConnection?(connectionId: string): boolean;
|
||||
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
||||
@@ -112,8 +114,9 @@ const MapComp = ({
|
||||
isSoftBackground,
|
||||
theme,
|
||||
onAddSystem,
|
||||
canRemoveConnection,
|
||||
}: MapCompProps) => {
|
||||
const { getNode, getNodes } = useReactFlow();
|
||||
const { getEdge, getNode, getNodes } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
|
||||
|
||||
@@ -222,6 +225,40 @@ const MapComp = ({
|
||||
[getNode, getNodes, onManualDelete, onNodesChange],
|
||||
);
|
||||
|
||||
const handleEdgesChange = useCallback(
|
||||
(changes: EdgeChange[]) => {
|
||||
const nextChanges = changes.reduce((acc, change) => {
|
||||
if (change.type !== 'remove') {
|
||||
return [...acc, change];
|
||||
}
|
||||
|
||||
if (canRemoveConnection?.(change.id)) {
|
||||
return [...acc, change];
|
||||
}
|
||||
|
||||
const edge = getEdge(change.id);
|
||||
if (!edge) {
|
||||
return [...acc, change];
|
||||
}
|
||||
|
||||
const sourceNode = getNode(edge.source);
|
||||
const targetNode = getNode(edge.target);
|
||||
if (!sourceNode || !targetNode) {
|
||||
return [...acc, change];
|
||||
}
|
||||
|
||||
if (sourceNode.data.locked || targetNode.data.locked) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
return [...acc, change];
|
||||
}, [] as EdgeChange[]);
|
||||
|
||||
onEdgesChange(nextChanges);
|
||||
},
|
||||
[getEdge, getNode, onEdgesChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
update(x => ({
|
||||
...x,
|
||||
@@ -237,7 +274,7 @@ const MapComp = ({
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onEdgesChange={handleEdgesChange}
|
||||
onConnect={onConnect}
|
||||
// TODO we need save into session all of this
|
||||
// and on any action do either
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>) => {
|
||||
const nodeVars = useSolarSystemNode(props);
|
||||
|
||||
|
||||
@@ -1,274 +1,39 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
@import './SolarSystemNodeDefault.module.scss';
|
||||
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020;
|
||||
/* ---------------------------
|
||||
Only override what's different
|
||||
--------------------------- */
|
||||
|
||||
/* 1) .RootCustomNode:
|
||||
- new background-color using CSS var
|
||||
- plus color, font-family, and font-weight */
|
||||
.RootCustomNode {
|
||||
display: flex;
|
||||
width: 130px;
|
||||
height: 34px;
|
||||
|
||||
flex-direction: column;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
|
||||
background-color: var(--rf-node-bg-color, #202020) !important;
|
||||
color: var(--rf-text-color, #ffffff);
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
|
||||
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
|
||||
border: 1px solid darken($pastel-blue, 10%);
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
||||
&.Mataria,
|
||||
&.Amarria,
|
||||
&.Gallente,
|
||||
&.Caldaria {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
z-index: -1;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Mataria {
|
||||
&::before {
|
||||
background-image: url('/images/mataria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Caldaria {
|
||||
&::before {
|
||||
background-image: url('/images/caldaria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Amarria {
|
||||
&::before {
|
||||
opacity: 0.45;
|
||||
background-image: url('/images/amarr-180.png');
|
||||
background-position-x: 0;
|
||||
background-position-y: -13px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Gallente {
|
||||
&::before {
|
||||
opacity: 0.5;
|
||||
background-image: url('/images/gallente-180.png');
|
||||
background-position-x: 1px;
|
||||
background-position-y: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
&.tooltip {
|
||||
background-color: $tooltip-bg;
|
||||
color: $text-color;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $pastel-pink;
|
||||
}
|
||||
|
||||
&.eve-system-status-home {
|
||||
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-home),
|
||||
transparent
|
||||
);
|
||||
&.selected {
|
||||
border-color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-friendly {
|
||||
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-friendly-dark30),
|
||||
transparent
|
||||
);
|
||||
&.selected {
|
||||
border-color: var(--eve-solar-system-status-color-friendly-dark5);
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-lookingFor {
|
||||
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
|
||||
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-warning {
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-warning),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
&.eve-system-status-dangerous {
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-dangerous),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
&.eve-system-status-target {
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-target),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2) .Bookmarks:
|
||||
- add var-based font family/weight
|
||||
*/
|
||||
.Bookmarks {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
left: 4px;
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
|
||||
|
||||
& > .Bookmark {
|
||||
min-width: 13px;
|
||||
height: 22px;
|
||||
position: relative;
|
||||
top: -13px;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
padding-top: 2px;
|
||||
font-weight: bolder;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
|
||||
//background-color: #833ca4;
|
||||
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.BookmarkWithIcon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: -2px;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
|
||||
padding-right: 2px;
|
||||
|
||||
& > .icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
& > .text {
|
||||
margin-top: 1px;
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Unsplashed {
|
||||
position: absolute;
|
||||
width: calc(50% - 4px);
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
left: 2px;
|
||||
|
||||
&--right {
|
||||
left: calc(50% + 6px);
|
||||
}
|
||||
|
||||
& > .Signature {
|
||||
width: 13px;
|
||||
height: 4px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
padding-top: 2px;
|
||||
font-weight: bolder;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
display: block;
|
||||
|
||||
background-color: #833ca4;
|
||||
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
/* 3) .HeadRow, .classTitle, .classSystemName:
|
||||
- add new references to var-based font family/weight
|
||||
*/
|
||||
.HeadRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
|
||||
.classTitle {
|
||||
font-size: 11px;
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
text-shadow: 0 0 2px rgb(0 0 0 / 73%);
|
||||
}
|
||||
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
@@ -280,20 +45,15 @@ $tooltip-bg: #202020;
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
}
|
||||
|
||||
.solarSystemName {
|
||||
}
|
||||
}
|
||||
|
||||
/* 4) .BottomRow:
|
||||
- introduces .tagTitle, .regionName, .customName, .localCounter
|
||||
referencing new CSS variables */
|
||||
.BottomRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 19px;
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
|
||||
|
||||
.tagTitle {
|
||||
font-size: 11px;
|
||||
font-weight: medium;
|
||||
@@ -327,100 +87,5 @@ $tooltip-bg: #202020;
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
}
|
||||
|
||||
& > i {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
& > span {
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
font-weight: 500;
|
||||
//margin-top: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.effect {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-top: -2px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.statics {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
font-size: 8px;
|
||||
|
||||
& > * {
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.Handlers {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid $pastel-blue;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
}
|
||||
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
&.Tick {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
|
||||
&.HandleTop {
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -3px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
|
||||
break;
|
||||
case Commands.updateSystems:
|
||||
setTimeout(() => mapUpdateSystems(data as CommandUpdateSystems), 100);
|
||||
mapUpdateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems:
|
||||
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
|
||||
@@ -70,7 +70,7 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
setTimeout(() => addConnections(data as CommandAddConnections), 100);
|
||||
break;
|
||||
case Commands.removeConnections:
|
||||
removeConnections(data as CommandRemoveConnections);
|
||||
setTimeout(() => removeConnections(data as CommandRemoveConnections), 100);
|
||||
break;
|
||||
case Commands.charactersUpdated:
|
||||
charactersUpdated(data as CommandCharactersUpdated);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormhol
|
||||
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { CharacterTypeRaw, OutCommand } from '@/hooks/Mapper/types';
|
||||
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
|
||||
|
||||
function getActivityType(count: number) {
|
||||
@@ -33,7 +33,17 @@ function sortedLabels(labels: string[]) {
|
||||
|
||||
export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>) {
|
||||
const { id, data, selected } = props;
|
||||
const { system_static_info, system_signatures, locked, name, tag, status, labels, temporary_name } = data;
|
||||
const {
|
||||
system_static_info,
|
||||
system_signatures,
|
||||
locked,
|
||||
name,
|
||||
tag,
|
||||
status,
|
||||
labels,
|
||||
temporary_name,
|
||||
linked_sig_eve_id: linkedSigEveId = '',
|
||||
} = data;
|
||||
|
||||
const {
|
||||
system_class,
|
||||
@@ -71,7 +81,6 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>) {
|
||||
visibleNodes,
|
||||
showKSpaceBG,
|
||||
isThickConnections,
|
||||
linkedSigEveId,
|
||||
},
|
||||
outCommand,
|
||||
} = useMapState();
|
||||
@@ -132,12 +141,12 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isShowLinkedSigIdTempName) {
|
||||
return temporary_name ? `${linkedSigPrefix}・${temporary_name}` : linkedSigPrefix;
|
||||
if (isShowLinkedSigIdTempName && linkedSigPrefix) {
|
||||
return temporary_name ? `${linkedSigPrefix}・${temporary_name}` : `${linkedSigPrefix}・${solar_system_name}`;
|
||||
}
|
||||
|
||||
return temporary_name;
|
||||
}, [isShowLinkedSigIdTempName, isTempSystemNameEnabled, linkedSigPrefix, temporary_name]);
|
||||
}, [isShowLinkedSigIdTempName, isTempSystemNameEnabled, linkedSigPrefix, solar_system_name, temporary_name]);
|
||||
|
||||
const systemName = useMemo(() => {
|
||||
if (isTempSystemNameEnabled && temporaryName) {
|
||||
@@ -146,7 +155,7 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>) {
|
||||
return solar_system_name;
|
||||
}, [isTempSystemNameEnabled, solar_system_name, temporaryName]);
|
||||
|
||||
const customName = (isTempSystemNameEnabled && temporaryName && name) || (solar_system_name !== name && name);
|
||||
const customName = (isTempSystemNameEnabled && temporaryName && name) || (solar_system_name !== name && name) || null;
|
||||
|
||||
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||
if (!isShowUnsplashedSignatures) {
|
||||
@@ -203,3 +212,43 @@ export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>) {
|
||||
|
||||
return nodeVars;
|
||||
}
|
||||
|
||||
export interface SolarSystemNodeVars {
|
||||
id: string;
|
||||
selected: boolean;
|
||||
visible: boolean;
|
||||
isWormhole: boolean;
|
||||
classTitleColor: string | null;
|
||||
killsCount: number | null;
|
||||
killsActivityType: string | null;
|
||||
hasUserCharacters: boolean;
|
||||
showHandlers: boolean;
|
||||
regionClass: string | null;
|
||||
systemName: string;
|
||||
customName?: string | null;
|
||||
labelCustom: string | null;
|
||||
isShattered: boolean;
|
||||
tag?: string | null;
|
||||
status?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
labelsInfo: Array<any>;
|
||||
dbClick: (event?: void) => void;
|
||||
sortedStatics: Array<string | number>;
|
||||
effectName: string | null;
|
||||
regionName: string | null;
|
||||
solarSystemId: number;
|
||||
solarSystemName: string | null;
|
||||
locked: boolean;
|
||||
hubs: string[] | number[];
|
||||
name: string | null;
|
||||
isConnecting: boolean;
|
||||
hoverNodeId: string | null;
|
||||
charactersInSystem: Array<CharacterTypeRaw>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
unsplashedLeft: Array<any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
unsplashedRight: Array<any>;
|
||||
isThickConnections: boolean;
|
||||
classTitle: string | null;
|
||||
temporaryName?: string | null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
|
||||
@@ -40,7 +39,6 @@
|
||||
--eve-wh-type-color-c13: #7986cb;
|
||||
--eve-wh-type-color-drifter: #44aa82;
|
||||
|
||||
|
||||
--rf-node-font-weight: bold;
|
||||
--rf-node-line-height: normal;
|
||||
--rf-node-font-family: 'Oxygen', sans-serif;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
@@ -58,7 +58,7 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
|
||||
<Dialog
|
||||
header="Select signature to link"
|
||||
visible
|
||||
draggable={false}
|
||||
draggable={true}
|
||||
style={{ width: '500px' }}
|
||||
onHide={handleHide}
|
||||
contentClassName="!p-0"
|
||||
|
||||
@@ -79,14 +79,14 @@ export const SystemStructures: React.FC = () => {
|
||||
content: (
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title={<b className="text-slate-50">How to add/update structures?</b>}>
|
||||
In game, select one or more structures in D-Scan and press Ctrl+C,
|
||||
In game, select one or more structures in D-Scan and then
|
||||
<br />
|
||||
then click on this widget and press Ctrl+V
|
||||
use the blue add structure data button
|
||||
</InfoDrawer>
|
||||
<InfoDrawer title={<b className="text-slate-50">How to add a timer?</b>}>
|
||||
In game, select a structure with an active timer, right click to copy, and then use the
|
||||
In game, select a structure with an active timer, right click to copy, and then
|
||||
<span className="text-blue-500"> blue </span>
|
||||
add timer button
|
||||
use the blue add structure data button
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
),
|
||||
@@ -101,7 +101,7 @@ export const SystemStructures: React.FC = () => {
|
||||
<div tabIndex={0} onPaste={handlePaste} className="h-full flex flex-col" style={{ outline: 'none' }}>
|
||||
<Widget label={renderWidgetLabel()}>
|
||||
{isNotSelectedSystem ? (
|
||||
<div className="flex-1 flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
|
||||
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
|
||||
System is not selected
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { Button } from 'primereact/button';
|
||||
import { AutoComplete } from 'primereact/autocomplete';
|
||||
import { Calendar } from 'primereact/calendar';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { StructureItem, StructureStatus, statusesRequiringTimer, formatToISO } from '../helpers';
|
||||
@@ -53,7 +54,9 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
|
||||
|
||||
// If user typed more text but we have partial match in prevResults
|
||||
if (newQuery.startsWith(prevQuery) && prevResults.length > 0) {
|
||||
const filtered = prevResults.filter(item => item.label.toLowerCase().includes(newQuery.toLowerCase()));
|
||||
const filtered = prevResults.filter(item =>
|
||||
item.label.toLowerCase().includes(newQuery.toLowerCase()),
|
||||
);
|
||||
setOwnerSuggestions(filtered);
|
||||
return;
|
||||
}
|
||||
@@ -74,12 +77,18 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
|
||||
[prevQuery, prevResults, outCommand],
|
||||
);
|
||||
|
||||
const handleChange = (field: keyof StructureItem, val: string) => {
|
||||
const handleChange = (field: keyof StructureItem, val: string | Date) => {
|
||||
// If we want to forbid changing structureTypeId or structureType from the dialog, do so here:
|
||||
if (field === 'structureTypeId' || field === 'structureType') return;
|
||||
|
||||
setEditData(prev => {
|
||||
if (!prev) return null;
|
||||
|
||||
// If this is the endTime (Date from Calendar), we store as ISO or string:
|
||||
if (field === 'endTime' && val instanceof Date) {
|
||||
return { ...prev, endTime: val.toISOString() };
|
||||
}
|
||||
|
||||
return { ...prev, [field]: val };
|
||||
});
|
||||
};
|
||||
@@ -87,7 +96,9 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
|
||||
// when user picks a corp from auto-complete
|
||||
const handleSelectOwner = (selected: { label: string; value: string }) => {
|
||||
setOwnerInput(selected.label);
|
||||
setEditData(prev => (prev ? { ...prev, ownerName: selected.label, ownerId: selected.value } : null));
|
||||
setEditData(prev =>
|
||||
prev ? { ...prev, ownerName: selected.label, ownerId: selected.value } : null,
|
||||
);
|
||||
};
|
||||
|
||||
const handleStatusChange = (val: string) => {
|
||||
@@ -107,7 +118,7 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
|
||||
if (!statusesRequiringTimer.includes(editData.status)) {
|
||||
editData.endTime = '';
|
||||
} else if (editData.endTime) {
|
||||
// convert to full ISO
|
||||
// convert to full ISO if not already
|
||||
editData.endTime = formatToISO(editData.endTime);
|
||||
}
|
||||
|
||||
@@ -146,7 +157,11 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
|
||||
<div className="flex flex-col gap-2 text-[14px]">
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center">
|
||||
<span>Type:</span>
|
||||
<input readOnly className="p-inputtext p-component cursor-not-allowed" value={editData.structureType ?? ''} />
|
||||
<input
|
||||
readOnly
|
||||
className="p-inputtext p-component cursor-not-allowed"
|
||||
value={editData.structureType ?? ''}
|
||||
/>
|
||||
</label>
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center">
|
||||
<span>Name:</span>
|
||||
@@ -186,17 +201,21 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
|
||||
<option value="Reinforced">Reinforced</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
{statusesRequiringTimer.includes(editData.status) && (
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center">
|
||||
<span>End Time:</span>
|
||||
<input
|
||||
type="datetime-local"
|
||||
className="p-inputtext p-component"
|
||||
value={editData.endTime ? editData.endTime.replace('Z', '').slice(0, 16) : ''}
|
||||
onChange={e => handleChange('endTime', e.target.value)}
|
||||
<span>Timer <br /> (Eve Time):</span>
|
||||
<Calendar
|
||||
value={editData.endTime ? new Date(editData.endTime) : undefined}
|
||||
onChange={(e) => handleChange('endTime', e.value ?? '')}
|
||||
showTime
|
||||
hourFormat="24"
|
||||
dateFormat="yy-mm-dd"
|
||||
showIcon
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
|
||||
<label className="grid grid-cols-[100px_1fr] gap-2 items-start mt-2">
|
||||
<span className="mt-1">Notes:</span>
|
||||
<textarea
|
||||
|
||||
@@ -14,9 +14,10 @@ import classes from './MapWrapper.module.scss';
|
||||
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
|
||||
import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { Node, XYPosition } from 'reactflow';
|
||||
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useCommandsSystems } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { emitMapEvent, useMapEventListener } from '@/hooks/Mapper/events';
|
||||
|
||||
import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
|
||||
@@ -32,7 +33,7 @@ export const MapWrapper = () => {
|
||||
const {
|
||||
update,
|
||||
outCommand,
|
||||
data: { selectedConnections, selectedSystems, hubs, systems },
|
||||
data: { selectedConnections, selectedSystems, hubs, systems, connections, linkSignatureToSystem },
|
||||
interfaceSettings: {
|
||||
isShowMenu,
|
||||
isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap,
|
||||
@@ -46,25 +47,19 @@ export const MapWrapper = () => {
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
const { mapRef, runCommand } = useCommonMapEventProcessor();
|
||||
|
||||
const { updateLinkSignatureToSystem } = useCommandsSystems();
|
||||
const { open, ...systemContextProps } = useContextMenuSystemHandlers({ systems, hubs, outCommand });
|
||||
const { handleSystemMultipleContext, ...systemMultipleCtxProps } = useContextMenuSystemMultipleHandlers();
|
||||
|
||||
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
||||
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
|
||||
const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<XYPosition | null>(null);
|
||||
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
|
||||
|
||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems });
|
||||
ref.current = { selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems };
|
||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, connections, deleteSystems });
|
||||
ref.current = { selectedConnections, selectedSystems, systemContextProps, systems, connections, deleteSystems };
|
||||
|
||||
useMapEventListener(event => {
|
||||
switch (event.name) {
|
||||
case Commands.linkSignatureToSystem:
|
||||
setOpenLinkSignatures(event.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
runCommand(event);
|
||||
});
|
||||
|
||||
@@ -93,9 +88,6 @@ export const MapWrapper = () => {
|
||||
case OutCommand.openSettings:
|
||||
setOpenSettings(event.data.system_id);
|
||||
break;
|
||||
case OutCommand.linkSignatureToSystem:
|
||||
setOpenLinkSignatures(event.data);
|
||||
break;
|
||||
default:
|
||||
return outCommand(event);
|
||||
}
|
||||
@@ -133,6 +125,11 @@ export const MapWrapper = () => {
|
||||
setOpenAddSystem(coordinates);
|
||||
}, []);
|
||||
|
||||
const canRemoveConnection = useCallback((connectionId: string) => {
|
||||
const { connections } = ref.current;
|
||||
return !connections.some(x => x.id === connectionId);
|
||||
}, []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (ref.current.systems.some(x => x.system_static_info.solar_system_id === item.value)) {
|
||||
@@ -169,6 +166,7 @@ export const MapWrapper = () => {
|
||||
isSoftBackground={isSoftBackground}
|
||||
theme={theme}
|
||||
onAddSystem={onAddSystem}
|
||||
canRemoveConnection={canRemoveConnection}
|
||||
/>
|
||||
|
||||
{openSettings != null && (
|
||||
@@ -179,8 +177,8 @@ export const MapWrapper = () => {
|
||||
<SystemCustomLabelDialog systemId={openCustomLabel} visible setVisible={() => setOpenCustomLabel(null)} />
|
||||
)}
|
||||
|
||||
{openLinkSignatures != null && (
|
||||
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
||||
{linkSignatureToSystem != null && (
|
||||
<SystemLinkSignatureDialog data={linkSignatureToSystem} setVisible={() => updateLinkSignatureToSystem(null)} />
|
||||
)}
|
||||
|
||||
<AddSystemDialog
|
||||
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
useStoreWidgets,
|
||||
WindowStoreInfo,
|
||||
} from '@/hooks/Mapper/mapRootProvider/hooks/useStoreWidgets.ts';
|
||||
import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types';
|
||||
|
||||
export type MapRootData = MapUnionTypes & {
|
||||
selectedSystems: string[];
|
||||
selectedConnections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
||||
linkSignatureToSystem: CommandLinkSignatureToSystem | null;
|
||||
};
|
||||
|
||||
const INITIAL_DATA: MapRootData = {
|
||||
@@ -34,6 +36,7 @@ const INITIAL_DATA: MapRootData = {
|
||||
selectedConnections: [],
|
||||
userPermissions: {},
|
||||
options: {},
|
||||
linkSignatureToSystem: null,
|
||||
};
|
||||
|
||||
export enum InterfaceStoredSettingsProps {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandAddSystems, CommandRemoveSystems, CommandUpdateSystems } from '@/hooks/Mapper/types';
|
||||
import {
|
||||
CommandAddSystems,
|
||||
CommandRemoveSystems,
|
||||
CommandUpdateSystems,
|
||||
CommandLinkSignatureToSystem,
|
||||
} from '@/hooks/Mapper/types';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
@@ -74,5 +79,10 @@ export const useCommandsSystems = () => {
|
||||
[outCommand],
|
||||
);
|
||||
|
||||
return { addSystems, removeSystems, updateSystems, updateSystemSignatures };
|
||||
const updateLinkSignatureToSystem = useCallback(async (command: CommandLinkSignatureToSystem) => {
|
||||
const { update } = ref.current;
|
||||
update({ linkSignatureToSystem: command }, true);
|
||||
}, []);
|
||||
|
||||
return { addSystems, removeSystems, updateSystems, updateSystemSignatures, updateLinkSignatureToSystem };
|
||||
};
|
||||
|
||||
@@ -32,7 +32,8 @@ import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
|
||||
export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
const mapInit = useMapInit();
|
||||
const { addSystems, removeSystems, updateSystems, updateSystemSignatures } = useCommandsSystems();
|
||||
const { addSystems, removeSystems, updateSystems, updateSystemSignatures, updateLinkSignatureToSystem } =
|
||||
useCommandsSystems();
|
||||
const { addConnections, removeConnections, updateConnection } = useCommandsConnections();
|
||||
const { charactersUpdated, characterAdded, characterRemoved, characterUpdated, presentCharacters } =
|
||||
useCommandsCharacters();
|
||||
@@ -93,7 +94,9 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem: // USED
|
||||
// do nothing here
|
||||
setTimeout(() => {
|
||||
updateLinkSignatureToSystem(data as CommandLinkSignatureToSystem);
|
||||
}, 200);
|
||||
break;
|
||||
|
||||
case Commands.centerSystem: // USED
|
||||
|
||||
@@ -117,6 +117,7 @@ export type SolarSystemRawType = {
|
||||
status: number;
|
||||
name: string | null;
|
||||
temporary_name: string | null;
|
||||
linked_sig_eve_id: string | null;
|
||||
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
system_signatures: SystemSignature[];
|
||||
|
||||
BIN
assets/static/images/news/01-20-structure-widget/enable-widget.png
Executable file
BIN
assets/static/images/news/01-20-structure-widget/enable-widget.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/static/images/news/01-20-structure-widget/widget.png
Executable file
BIN
assets/static/images/news/01-20-structure-widget/widget.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -64,7 +64,19 @@ map_subscription_characters_limit =
|
||||
|
||||
map_subscription_hubs_limit =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 100)
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 10)
|
||||
|
||||
map_subscription_base_price =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_BASE_PRICE", 100_000_000)
|
||||
|
||||
map_subscription_extra_characters_100_price =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_EXTRA_CHARACTERS_100_PRICE", 50_000_000)
|
||||
|
||||
map_subscription_extra_hubs_10_price =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_EXTRA_HUBS_10_PRICE", 10_000_000)
|
||||
|
||||
map_connection_auto_expire_hours =
|
||||
config_dir
|
||||
@@ -76,7 +88,7 @@ map_connection_auto_eol_hours =
|
||||
|
||||
map_connection_eol_expire_timeout_mins =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_CONNECTION_EOL_EXPIRE_TIMEOUT_MINS", 30)
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_CONNECTION_EOL_EXPIRE_TIMEOUT_MINS", 60)
|
||||
|
||||
wallet_tracking_enabled =
|
||||
config_dir
|
||||
@@ -117,16 +129,16 @@ config :wanderer_app,
|
||||
},
|
||||
%{
|
||||
id: "omega",
|
||||
characters_limit: 300,
|
||||
hubs_limit: 20,
|
||||
base_price: 250_000_000,
|
||||
characters_limit: map_subscription_characters_limit * 2,
|
||||
hubs_limit: map_subscription_hubs_limit * 2,
|
||||
base_price: map_subscription_base_price,
|
||||
month_3_discount: 0.2,
|
||||
month_6_discount: 0.4,
|
||||
month_12_discount: 0.5
|
||||
}
|
||||
],
|
||||
extra_characters_100: 75_000_000,
|
||||
extra_hubs_10: 25_000_000
|
||||
extra_characters_100: map_subscription_extra_characters_100_price,
|
||||
extra_hubs_10: map_subscription_extra_hubs_10_price
|
||||
}
|
||||
|
||||
config :ueberauth, Ueberauth,
|
||||
|
||||
@@ -95,7 +95,9 @@ defmodule WandererApp.Api.UserActivity do
|
||||
:map_acl_member_updated,
|
||||
:map_connection_added,
|
||||
:map_connection_updated,
|
||||
:map_connection_removed
|
||||
:map_connection_removed,
|
||||
:signatures_added,
|
||||
:signatures_removed
|
||||
]
|
||||
)
|
||||
|
||||
@@ -108,8 +110,6 @@ defmodule WandererApp.Api.UserActivity do
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
|
||||
|
||||
relationships do
|
||||
belongs_to :character, WandererApp.Api.Character do
|
||||
allow_nil? true
|
||||
|
||||
@@ -68,7 +68,7 @@ defmodule WandererApp.Map do
|
||||
end
|
||||
|
||||
def get_characters_limit(map_id),
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:characters_limit, 100)}
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:characters_limit, 50)}
|
||||
|
||||
def is_subscription_active?(map_id) do
|
||||
{:ok, %{plan: plan}} = WandererApp.Map.SubscriptionManager.get_active_map_subscription(map_id)
|
||||
|
||||
@@ -206,8 +206,24 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
user_id,
|
||||
character_id
|
||||
) do
|
||||
connections_to_remove =
|
||||
filtered_ids =
|
||||
removed_ids
|
||||
|> Enum.map(fn solar_system_id ->
|
||||
WandererApp.Map.find_system_by_location(map_id, %{solar_system_id: solar_system_id})
|
||||
end)
|
||||
|> Enum.filter(fn system -> not is_nil(system) && not system.locked end)
|
||||
|> Enum.map(&{&1.solar_system_id, &1.id})
|
||||
|
||||
solar_system_ids_to_remove =
|
||||
filtered_ids
|
||||
|> Enum.map(fn {solar_system_id, _} -> solar_system_id end)
|
||||
|
||||
system_ids_to_remove =
|
||||
filtered_ids
|
||||
|> Enum.map(fn {_, system_id} -> system_id end)
|
||||
|
||||
connections_to_remove =
|
||||
solar_system_ids_to_remove
|
||||
|> Enum.map(fn solar_system_id ->
|
||||
WandererApp.Map.find_connections(map_id, solar_system_id)
|
||||
end)
|
||||
@@ -215,9 +231,9 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
|> Enum.uniq_by(& &1.id)
|
||||
|
||||
:ok = WandererApp.Map.remove_connections(map_id, connections_to_remove)
|
||||
:ok = WandererApp.Map.remove_systems(map_id, removed_ids)
|
||||
:ok = WandererApp.Map.remove_systems(map_id, solar_system_ids_to_remove)
|
||||
|
||||
removed_ids
|
||||
solar_system_ids_to_remove
|
||||
|> Enum.each(fn solar_system_id ->
|
||||
map_id
|
||||
|> WandererApp.MapSystemRepo.remove_from_map(solar_system_id)
|
||||
@@ -237,7 +253,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
WandererApp.MapConnectionRepo.destroy(map_id, connection)
|
||||
end)
|
||||
|
||||
removed_ids
|
||||
solar_system_ids_to_remove
|
||||
|> Enum.map(fn solar_system_id ->
|
||||
WandererApp.Api.MapSystemSignature.by_linked_system_id!(solar_system_id)
|
||||
end)
|
||||
@@ -250,10 +266,28 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
Impl.broadcast!(map_id, :signatures_updated, system.solar_system_id)
|
||||
end)
|
||||
|
||||
@ddrt.delete(removed_ids, rtree_name)
|
||||
linked_system_ids =
|
||||
system_ids_to_remove
|
||||
|> Enum.map(fn system_id ->
|
||||
WandererApp.Api.MapSystemSignature.by_system_id!(system_id)
|
||||
|> Enum.filter(fn s -> not is_nil(s.linked_system_id) end)
|
||||
|> Enum.map(fn s -> s.linked_system_id end)
|
||||
end)
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
|
||||
linked_system_ids
|
||||
|> Enum.each(fn linked_system_id ->
|
||||
WandererApp.Map.Server.update_system_linked_sig_eve_id(map_id, %{
|
||||
solar_system_id: linked_system_id,
|
||||
linked_sig_eve_id: nil
|
||||
})
|
||||
end)
|
||||
|
||||
@ddrt.delete(solar_system_ids_to_remove, rtree_name)
|
||||
|
||||
Impl.broadcast!(map_id, :remove_connections, connections_to_remove)
|
||||
Impl.broadcast!(map_id, :systems_removed, removed_ids)
|
||||
Impl.broadcast!(map_id, :systems_removed, solar_system_ids_to_remove)
|
||||
|
||||
case not is_nil(user_id) do
|
||||
true ->
|
||||
@@ -262,12 +296,12 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
character_id: character_id,
|
||||
user_id: user_id,
|
||||
map_id: map_id,
|
||||
solar_system_ids: removed_ids
|
||||
solar_system_ids: solar_system_ids_to_remove
|
||||
})
|
||||
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :map, :systems, :remove],
|
||||
%{count: removed_ids |> Enum.count()}
|
||||
%{count: solar_system_ids_to_remove |> Enum.count()}
|
||||
)
|
||||
|
||||
:ok
|
||||
|
||||
@@ -55,18 +55,33 @@ defmodule WandererApp.Maps do
|
||||
|
||||
def get_available_maps(current_user) do
|
||||
case WandererApp.Api.Map.available(%{}, actor: current_user) do
|
||||
{:ok, maps} -> {:ok, maps |> _filter_blocked_maps(current_user)}
|
||||
{:ok, maps} -> {:ok, maps |> filter_blocked_maps(current_user)}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
end
|
||||
|
||||
def get_tracked_map_characters(map_id, current_user) do
|
||||
case WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_filtered(
|
||||
map_id,
|
||||
current_user.characters |> Enum.map(& &1.id)
|
||||
) do
|
||||
{:ok, settings} ->
|
||||
{:ok,
|
||||
settings
|
||||
|> Enum.map(fn s -> s |> Ash.load!(:character) |> Map.get(:character) end)}
|
||||
|
||||
_ ->
|
||||
{:ok, []}
|
||||
end
|
||||
end
|
||||
|
||||
def load_characters(map, character_settings, user_id) do
|
||||
{:ok, user_characters} =
|
||||
WandererApp.Api.Character.active_by_user(%{user_id: user_id})
|
||||
|
||||
characters =
|
||||
map
|
||||
|> _get_map_available_characters(user_characters)
|
||||
|> get_map_available_characters(user_characters)
|
||||
|> Enum.map(fn c ->
|
||||
map_character(c, character_settings |> Enum.find(&(&1.character_id == c.id)))
|
||||
end)
|
||||
@@ -146,7 +161,7 @@ defmodule WandererApp.Maps do
|
||||
}}
|
||||
end
|
||||
|
||||
defp _get_map_available_characters(map, user_characters) do
|
||||
defp get_map_available_characters(map, user_characters) do
|
||||
{:ok,
|
||||
%{
|
||||
map_acl_owner_ids: map_acl_owner_ids,
|
||||
@@ -164,7 +179,7 @@ defmodule WandererApp.Maps do
|
||||
end)
|
||||
end
|
||||
|
||||
defp _filter_blocked_maps(maps, current_user) do
|
||||
defp filter_blocked_maps(maps, current_user) do
|
||||
user_character_ids = current_user.characters |> Enum.map(& &1.id)
|
||||
user_character_eve_ids = current_user.characters |> Enum.map(& &1.eve_id)
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ defmodule WandererAppWeb.UserActivity do
|
||||
</p>
|
||||
|
||||
<p class="text-sm text-[var(--color-gray-4)] w-[15%]">
|
||||
<%= _get_event_name(@activity.event_type) %>
|
||||
<%= get_event_name(@activity.event_type) %>
|
||||
</p>
|
||||
<.activity_event event_type={@activity.event_type} event_data={@activity.event_data} />
|
||||
|
||||
@@ -115,7 +115,7 @@ defmodule WandererAppWeb.UserActivity do
|
||||
<div class="w-[40%]">
|
||||
<div class="flex items-center gap-1">
|
||||
<h6 class="text-base leading-[150%] font-semibold dark:text-white">
|
||||
<%= _get_event_data(@event_type, Jason.decode!(@event_data) |> Map.drop(["character_id"])) %>
|
||||
<%= get_event_data(@event_type, Jason.decode!(@event_data) |> Map.drop(["character_id"])) %>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,107 +129,128 @@ defmodule WandererAppWeb.UserActivity do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp _get_event_name(:hub_added), do: "Hub Added"
|
||||
defp _get_event_name(:hub_removed), do: "Hub Removed"
|
||||
defp _get_event_name(:map_connection_added), do: "Connection Added"
|
||||
defp _get_event_name(:map_connection_updated), do: "Connection Updated"
|
||||
defp _get_event_name(:map_connection_removed), do: "Connection Removed"
|
||||
defp _get_event_name(:map_acl_added), do: "Acl Added"
|
||||
defp _get_event_name(:map_acl_removed), do: "Acl Removed"
|
||||
defp _get_event_name(:system_added), do: "System Added"
|
||||
defp _get_event_name(:system_updated), do: "System Updated"
|
||||
defp _get_event_name(:systems_removed), do: "System(s) Removed"
|
||||
defp _get_event_name(name), do: name
|
||||
defp get_event_name(:hub_added), do: "Hub Added"
|
||||
defp get_event_name(:hub_removed), do: "Hub Removed"
|
||||
defp get_event_name(:map_connection_added), do: "Connection Added"
|
||||
defp get_event_name(:map_connection_updated), do: "Connection Updated"
|
||||
defp get_event_name(:map_connection_removed), do: "Connection Removed"
|
||||
defp get_event_name(:map_acl_added), do: "Acl Added"
|
||||
defp get_event_name(:map_acl_removed), do: "Acl Removed"
|
||||
defp get_event_name(:system_added), do: "System Added"
|
||||
defp get_event_name(:system_updated), do: "System Updated"
|
||||
defp get_event_name(:systems_removed), do: "System(s) Removed"
|
||||
defp get_event_name(:signatures_added), do: "Signatures Added"
|
||||
defp get_event_name(:signatures_removed), do: "Signatures Removed"
|
||||
defp get_event_name(name), do: name
|
||||
|
||||
# defp _get_event_data(:hub_added, data), do: Jason.encode!(data)
|
||||
# defp _get_event_data(:hub_removed, data), do: data
|
||||
defp get_event_data(:map_acl_added, %{"acl_id" => acl_id}) do
|
||||
{:ok, acl} = WandererApp.AccessListRepo.get(acl_id)
|
||||
"#{acl.name}"
|
||||
end
|
||||
|
||||
# defp _get_event_data(:map_acl_added, data), do: data
|
||||
# defp _get_event_data(:map_acl_removed, data), do: data
|
||||
# defp _get_event_data(:system_added, data), do: data
|
||||
defp get_event_data(:map_acl_removed, %{"acl_id" => acl_id}) do
|
||||
{:ok, acl} = WandererApp.AccessListRepo.get(acl_id)
|
||||
"#{acl.name}"
|
||||
end
|
||||
|
||||
# defp get_event_data(:map_acl_removed, data), do: data
|
||||
# defp get_event_data(:system_added, data), do: data
|
||||
#
|
||||
|
||||
defp _get_event_data(:system_updated, %{
|
||||
defp get_event_data(:system_updated, %{
|
||||
"key" => "labels",
|
||||
"solar_system_id" => solar_system_id,
|
||||
"value" => value
|
||||
}) do
|
||||
system_name = _get_system_name(solar_system_id)
|
||||
system_name = get_system_name(solar_system_id)
|
||||
|
||||
try do
|
||||
%{"customLabel" => customLabel, "labels" => labels} = Jason.decode!(value)
|
||||
|
||||
"#{system_name} labels - #{inspect(labels)}, customLabel - #{customLabel}"
|
||||
"#{system_name}: labels - #{inspect(labels)}, customLabel - #{customLabel}"
|
||||
rescue
|
||||
_ ->
|
||||
"#{system_name} labels - #{inspect(value)}"
|
||||
"#{system_name}: labels - #{inspect(value)}"
|
||||
end
|
||||
end
|
||||
|
||||
defp _get_event_data(:system_added, %{
|
||||
defp get_event_data(:system_added, %{
|
||||
"solar_system_id" => solar_system_id
|
||||
}),
|
||||
do: _get_system_name(solar_system_id)
|
||||
do: get_system_name(solar_system_id)
|
||||
|
||||
defp _get_event_data(:hub_added, %{
|
||||
defp get_event_data(:hub_added, %{
|
||||
"solar_system_id" => solar_system_id
|
||||
}),
|
||||
do: _get_system_name(solar_system_id)
|
||||
do: get_system_name(solar_system_id)
|
||||
|
||||
defp _get_event_data(:hub_removed, %{
|
||||
defp get_event_data(:hub_removed, %{
|
||||
"solar_system_id" => solar_system_id
|
||||
}),
|
||||
do: _get_system_name(solar_system_id)
|
||||
do: get_system_name(solar_system_id)
|
||||
|
||||
defp _get_event_data(:system_updated, %{
|
||||
defp get_event_data(:system_updated, %{
|
||||
"key" => key,
|
||||
"solar_system_id" => solar_system_id,
|
||||
"value" => value
|
||||
}) do
|
||||
system_name = _get_system_name(solar_system_id)
|
||||
"#{system_name} #{key} - #{inspect(value)}"
|
||||
system_name = get_system_name(solar_system_id)
|
||||
"#{system_name}: #{key} - #{inspect(value)}"
|
||||
end
|
||||
|
||||
defp _get_event_data(:systems_removed, %{
|
||||
defp get_event_data(:systems_removed, %{
|
||||
"solar_system_ids" => solar_system_ids
|
||||
}),
|
||||
do:
|
||||
solar_system_ids
|
||||
|> Enum.map(&_get_system_name/1)
|
||||
|> Enum.map(&get_system_name/1)
|
||||
|> Enum.join(", ")
|
||||
|
||||
defp _get_event_data(:map_connection_added, %{
|
||||
defp get_event_data(signatures_event, %{
|
||||
"solar_system_id" => solar_system_id,
|
||||
"signatures" => signatures
|
||||
})
|
||||
when signatures_event in [:signatures_added, :signatures_removed],
|
||||
do: "#{get_system_name(solar_system_id)}: #{signatures |> Enum.join(", ")}"
|
||||
|
||||
defp get_event_data(signatures_event, %{
|
||||
"signatures" => signatures
|
||||
})
|
||||
when signatures_event in [:signatures_added, :signatures_removed],
|
||||
do: signatures |> Enum.join(", ")
|
||||
|
||||
defp get_event_data(:map_connection_added, %{
|
||||
"solar_system_source_id" => solar_system_source_id,
|
||||
"solar_system_target_id" => solar_system_target_id
|
||||
}) do
|
||||
source_system_name = _get_system_name(solar_system_source_id)
|
||||
target_system_name = _get_system_name(solar_system_target_id)
|
||||
source_system_name = get_system_name(solar_system_source_id)
|
||||
target_system_name = get_system_name(solar_system_target_id)
|
||||
"[#{source_system_name}:#{target_system_name}]"
|
||||
end
|
||||
|
||||
defp _get_event_data(:map_connection_removed, %{
|
||||
defp get_event_data(:map_connection_removed, %{
|
||||
"solar_system_source_id" => solar_system_source_id,
|
||||
"solar_system_target_id" => solar_system_target_id
|
||||
}) do
|
||||
source_system_name = _get_system_name(solar_system_source_id)
|
||||
target_system_name = _get_system_name(solar_system_target_id)
|
||||
source_system_name = get_system_name(solar_system_source_id)
|
||||
target_system_name = get_system_name(solar_system_target_id)
|
||||
"[#{source_system_name}:#{target_system_name}]"
|
||||
end
|
||||
|
||||
defp _get_event_data(:map_connection_updated, %{
|
||||
defp get_event_data(:map_connection_updated, %{
|
||||
"key" => key,
|
||||
"solar_system_source_id" => solar_system_source_id,
|
||||
"solar_system_target_id" => solar_system_target_id,
|
||||
"value" => value
|
||||
}) do
|
||||
source_system_name = _get_system_name(solar_system_source_id)
|
||||
target_system_name = _get_system_name(solar_system_target_id)
|
||||
source_system_name = get_system_name(solar_system_source_id)
|
||||
target_system_name = get_system_name(solar_system_target_id)
|
||||
"[#{source_system_name}:#{target_system_name}] #{key} - #{inspect(value)}"
|
||||
end
|
||||
|
||||
defp _get_event_data(_name, data), do: Jason.encode!(data)
|
||||
defp get_event_data(_name, data), do: Jason.encode!(data)
|
||||
|
||||
defp _get_system_name(solar_system_id) do
|
||||
defp get_system_name(solar_system_id) do
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, nil} ->
|
||||
solar_system_id
|
||||
|
||||
@@ -156,7 +156,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
|
||||
%{result: characters} = socket.assigns.characters
|
||||
|
||||
{:ok, map_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
{:ok, map_characters} = WandererApp.Maps.get_tracked_map_characters(map_id, current_user)
|
||||
|
||||
user_character_eve_ids = map_characters |> Enum.map(& &1.eve_id)
|
||||
|
||||
@@ -204,7 +204,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
{:ok, all_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
# Find and filter user's characters
|
||||
{:ok, user_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
{:ok, user_characters} = WandererApp.Maps.get_tracked_map_characters(map_id, current_user)
|
||||
user_char_ids = Enum.map(user_characters, & &1.id)
|
||||
|
||||
my_settings =
|
||||
@@ -251,7 +251,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
# re-fetch or re-map to confirm final results in UI
|
||||
%{result: characters} = socket.assigns.characters
|
||||
|
||||
{:ok, tracked_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
{:ok, tracked_characters} = WandererApp.Maps.get_tracked_map_characters(map_id, current_user)
|
||||
user_eve_ids = Enum.map(tracked_characters, & &1.eve_id)
|
||||
|
||||
{:ok, final_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
@@ -316,21 +316,6 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
def has_tracked_characters?([]), do: false
|
||||
def has_tracked_characters?(_user_characters), do: true
|
||||
|
||||
def get_tracked_map_characters(map_id, current_user) do
|
||||
case WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_filtered(
|
||||
map_id,
|
||||
current_user.characters |> Enum.map(& &1.id)
|
||||
) do
|
||||
{:ok, settings} ->
|
||||
{:ok,
|
||||
settings
|
||||
|> Enum.map(fn s -> s |> Ash.load!(:character) |> Map.get(:character) end)}
|
||||
|
||||
_ ->
|
||||
{:ok, []}
|
||||
end
|
||||
end
|
||||
|
||||
def map_ui_character(character),
|
||||
do:
|
||||
character
|
||||
|
||||
@@ -119,10 +119,9 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
),
|
||||
do: socket
|
||||
|
||||
def handle_server_event(%{event: :structures_updated, payload: _solar_system_id}, socket) do
|
||||
socket
|
||||
end
|
||||
|
||||
def handle_server_event(%{event: :structures_updated, payload: _solar_system_id}, socket) do
|
||||
socket
|
||||
end
|
||||
|
||||
def handle_server_event(event, socket) do
|
||||
Logger.warning(fn -> "unhandled map core event: #{inspect(event)} #{inspect(socket)} " end)
|
||||
@@ -271,6 +270,8 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
current_user.characters |> Enum.map(& &1.id)
|
||||
)
|
||||
|
||||
{:ok, map_user_settings} = WandererApp.MapUserSettingsRepo.get(map_id, current_user.id)
|
||||
|
||||
{:ok, character_settings} =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do
|
||||
{:ok, settings} -> {:ok, settings}
|
||||
@@ -303,6 +304,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
|> assign(
|
||||
map_id: map_id,
|
||||
map_user_settings: map_user_settings,
|
||||
page_title: map_name,
|
||||
user_permissions: user_permissions,
|
||||
tracked_character_ids: tracked_character_ids,
|
||||
@@ -335,9 +337,8 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
} = socket
|
||||
) do
|
||||
with {:ok, _} <- current_user |> WandererApp.Api.User.update_last_map(%{last_map_id: map_id}),
|
||||
{:ok, map_user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user.id),
|
||||
{:ok, tracked_map_characters} <-
|
||||
MapCharactersEventHandler.get_tracked_map_characters(map_id, current_user),
|
||||
WandererApp.Maps.get_tracked_map_characters(map_id, current_user),
|
||||
{:ok, characters_limit} <- map_id |> WandererApp.Map.get_characters_limit(),
|
||||
{:ok, present_character_ids} <-
|
||||
WandererApp.Cache.lookup("map_#{map_id}:presence_character_ids", []),
|
||||
@@ -415,7 +416,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
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
|
||||
@@ -438,7 +438,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket,
|
||||
%{
|
||||
map_id: map_id,
|
||||
map_user_settings: map_user_settings,
|
||||
user_characters: user_character_eve_ids,
|
||||
initial_data: initial_data,
|
||||
events: events
|
||||
@@ -469,7 +468,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
|> assign(
|
||||
map_loaded?: true,
|
||||
map_user_settings: map_user_settings,
|
||||
user_characters: user_character_eve_ids,
|
||||
has_tracked_characters?: has_tracked_characters?
|
||||
)
|
||||
@@ -543,21 +541,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
}
|
||||
end
|
||||
|
||||
defp get_tracked_map_characters(map_id, current_user) do
|
||||
case WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_filtered(
|
||||
map_id,
|
||||
current_user.characters |> Enum.map(& &1.id)
|
||||
) do
|
||||
{:ok, settings} ->
|
||||
{:ok,
|
||||
settings
|
||||
|> Enum.map(fn s -> s |> Ash.load!(:character) |> Map.get(:character) end)}
|
||||
|
||||
_ ->
|
||||
{:ok, []}
|
||||
end
|
||||
end
|
||||
|
||||
defp filter_map_characters(
|
||||
characters,
|
||||
user_character_eve_ids,
|
||||
|
||||
@@ -81,6 +81,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do
|
||||
},
|
||||
%{
|
||||
assigns: %{
|
||||
current_user: current_user,
|
||||
map_id: map_id,
|
||||
map_user_settings: map_user_settings,
|
||||
user_characters: user_characters,
|
||||
@@ -161,10 +162,40 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do
|
||||
end)
|
||||
|
||||
added_signatures
|
||||
|> Enum.map(fn s ->
|
||||
|> Enum.each(fn s ->
|
||||
s |> WandererApp.Api.MapSystemSignature.create!()
|
||||
end)
|
||||
|
||||
added_signatures_eve_ids =
|
||||
added_signatures
|
||||
|> Enum.map(fn s -> s.eve_id end)
|
||||
|
||||
first_tracked_character =
|
||||
current_user.characters
|
||||
|> Enum.find(fn c -> c.eve_id === first_character_eve_id end)
|
||||
|
||||
if not is_nil(first_tracked_character) &&
|
||||
not (added_signatures_eve_ids |> Enum.empty?()) do
|
||||
WandererApp.User.ActivityTracker.track_map_event(:signatures_added, %{
|
||||
character_id: first_tracked_character.id,
|
||||
user_id: current_user.id,
|
||||
map_id: map_id,
|
||||
solar_system_id: solar_system_id,
|
||||
signatures: added_signatures_eve_ids
|
||||
})
|
||||
end
|
||||
|
||||
if not is_nil(first_tracked_character) &&
|
||||
not (removed_signatures_eve_ids |> Enum.empty?()) do
|
||||
WandererApp.User.ActivityTracker.track_map_event(:signatures_removed, %{
|
||||
character_id: first_tracked_character.id,
|
||||
user_id: current_user.id,
|
||||
map_id: map_id,
|
||||
solar_system_id: solar_system_id,
|
||||
signatures: removed_signatures_eve_ids
|
||||
})
|
||||
end
|
||||
|
||||
Phoenix.PubSub.broadcast!(WandererApp.PubSub, map_id, %{
|
||||
event: :signatures_updated,
|
||||
payload: system.solar_system_id
|
||||
|
||||
@@ -21,57 +21,63 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
|> MapEventHandler.push_map_event("remove_systems", solar_system_ids)
|
||||
|
||||
def handle_server_event(
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: character_id,
|
||||
solar_system_id: solar_system_id
|
||||
}
|
||||
},
|
||||
%{assigns: %{current_user: current_user, map_id: map_id, map_user_settings: map_user_settings}} = socket
|
||||
) do
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: character_id,
|
||||
solar_system_id: solar_system_id
|
||||
}
|
||||
},
|
||||
%{
|
||||
assigns: %{
|
||||
current_user: current_user,
|
||||
map_id: map_id,
|
||||
map_user_settings: map_user_settings
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
is_user_character =
|
||||
current_user.characters
|
||||
|> Enum.map(& &1.id)
|
||||
|> Enum.member?(character_id)
|
||||
|
||||
is_user_character =
|
||||
current_user.characters
|
||||
|> Enum.map(& &1.id)
|
||||
|> Enum.member?(character_id)
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
is_followed =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character_id) do
|
||||
{:ok, setting} -> setting.followed == true
|
||||
_ -> false
|
||||
end
|
||||
|
||||
is_followed =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character_id) do
|
||||
{:ok, setting} -> setting.followed == true
|
||||
_ -> false
|
||||
end
|
||||
must_select? = is_user_character && (is_select_on_spash || is_followed)
|
||||
|
||||
must_select? = is_user_character && (is_select_on_spash || is_followed)
|
||||
if not must_select? do
|
||||
if not must_select? do
|
||||
socket
|
||||
else
|
||||
# Check if we already selected this exact system for this char:
|
||||
last_selected =
|
||||
WandererApp.Cache.lookup!(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
nil
|
||||
)
|
||||
|
||||
if last_selected == solar_system_id do
|
||||
# same system => skip
|
||||
socket
|
||||
else
|
||||
# Check if we already selected this exact system for this char:
|
||||
last_selected =
|
||||
WandererApp.Cache.lookup!(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
nil
|
||||
)
|
||||
# new system => update cache + push event
|
||||
WandererApp.Cache.put(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
|
||||
if last_selected == solar_system_id do
|
||||
# same system => skip
|
||||
socket
|
||||
else
|
||||
# new system => update cache + push event
|
||||
WandererApp.Cache.put(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
end
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_server_event(%{event: :kills_updated, payload: kills}, socket) do
|
||||
|
||||
@@ -171,7 +171,9 @@ defmodule WandererAppWeb.MapAuditLive do
|
||||
{"ACL Removed", :map_acl_removed},
|
||||
{"Connection Added", :map_connection_added},
|
||||
{"Connection Updated", :map_connection_updated},
|
||||
{"Connection Removed", :map_connection_removed}
|
||||
{"Connection Removed", :map_connection_removed},
|
||||
{"Signatures Added", :signatures_added},
|
||||
{"Signatures Removed", :signatures_removed}
|
||||
])
|
||||
|> load_activity(1)
|
||||
end
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
<main
|
||||
id="map-events-list"
|
||||
class="pt-20 w-full h-full col-span-2 lg:col-span-1 p-4 pl-20 pb-20 overflow-auto"
|
||||
>
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<.live_component
|
||||
module={UserActivity}
|
||||
id="user-activity"
|
||||
notify_to={self()}
|
||||
can_undo_types={@can_undo_types}
|
||||
stream={@streams.activity}
|
||||
page={@page}
|
||||
end_of_stream?={@end_of_stream?}
|
||||
event_name="activity_event"
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<nav class="fixed top-0 z-100 px-6 pl-20 flex items-center justify-between w-full h-12 pointer-events-auto border-b border-stone-800 bg-opacity-70 bg-neutral-900">
|
||||
<span className="w-full font-medium text-sm">
|
||||
<.link navigate={~p"/#{@map_slug}"} class="text-neutral-100">
|
||||
@@ -113,3 +96,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main
|
||||
id="map-events-list"
|
||||
class="pt-20 w-full h-full col-span-2 lg:col-span-1 p-4 pl-20 pb-20 overflow-auto"
|
||||
>
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<.live_component
|
||||
module={UserActivity}
|
||||
id="user-activity"
|
||||
notify_to={self()}
|
||||
can_undo_types={@can_undo_types}
|
||||
stream={@streams.activity}
|
||||
page={@page}
|
||||
end_of_stream?={@end_of_stream?}
|
||||
event_name="activity_event"
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -112,7 +112,7 @@ defmodule WandererAppWeb.MapsLive do
|
||||
subscription_form = %{
|
||||
"plan" => "omega",
|
||||
"period" => "1",
|
||||
"characters_limit" => "300",
|
||||
"characters_limit" => "100",
|
||||
"hubs_limit" => "10",
|
||||
"auto_renew?" => true
|
||||
}
|
||||
@@ -636,6 +636,33 @@ defmodule WandererAppWeb.MapsLive do
|
||||
{:map_acl_updated, added_acls, removed_acls}
|
||||
)
|
||||
|
||||
{:ok, tracked_characters} =
|
||||
WandererApp.Maps.get_tracked_map_characters(map.id, current_user)
|
||||
|
||||
first_tracked_character_id = Enum.map(tracked_characters, & &1.id) |> List.first()
|
||||
|
||||
added_acls
|
||||
|> Enum.each(fn acl_id ->
|
||||
{:ok, _} =
|
||||
WandererApp.User.ActivityTracker.track_map_event(:map_acl_added, %{
|
||||
character_id: first_tracked_character_id,
|
||||
user_id: current_user.id,
|
||||
map_id: map.id,
|
||||
acl_id: acl_id
|
||||
})
|
||||
end)
|
||||
|
||||
removed_acls
|
||||
|> Enum.each(fn acl_id ->
|
||||
{:ok, _} =
|
||||
WandererApp.User.ActivityTracker.track_map_event(:map_acl_removed, %{
|
||||
character_id: first_tracked_character_id,
|
||||
user_id: current_user.id,
|
||||
map_id: map.id,
|
||||
acl_id: acl_id
|
||||
})
|
||||
end)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign_async(:maps, fn ->
|
||||
|
||||
@@ -580,11 +580,9 @@
|
||||
>
|
||||
<div :if={is_nil(@selected_subscription)}>
|
||||
Add subscription
|
||||
<div class="badge badge-secondary">Limited time offer: 50%</div>
|
||||
</div>
|
||||
<div :if={not is_nil(@selected_subscription)}>
|
||||
Edit subscription
|
||||
<div class="badge badge-secondary">Limited time offer: 50%</div>
|
||||
</div>
|
||||
<.form
|
||||
:let={f}
|
||||
@@ -609,7 +607,7 @@
|
||||
label="Characters limit"
|
||||
show_value={true}
|
||||
type="range"
|
||||
min="300"
|
||||
min="100"
|
||||
max="5000"
|
||||
step="100"
|
||||
class="range range-xs"
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
|
||||
@version "1.40.1"
|
||||
@version "1.43.1"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
151
priv/posts/2025/01-20-structure-widget.md
Normal file
151
priv/posts/2025/01-20-structure-widget.md
Normal file
@@ -0,0 +1,151 @@
|
||||
%{
|
||||
title: "Managing Upwell Structures & Timers with the Structures Widget",
|
||||
author: "Wanderer Team",
|
||||
cover_image_uri: "/images/news/structure-widget/cover.png",
|
||||
tags: ~w(interface guide map structures),
|
||||
description: "Learn how to track structure information using the Structures Widget."
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
### Introduction
|
||||
|
||||
Upwell structures like **Astrahus**, **Athanor**, and more are key strategic points in EVE Online. Staying informed about their statuses—whether they’re anchoring, powered, or reinforced—helps you plan defenses, coordinate attacks, and align with allies. Our **Structures Widget** simplifies the process by allowing you to:
|
||||
|
||||
- Copy structure information directly from the in-game Directional Scanner (`D-Scan`) and paste it into the widget.
|
||||
- Keep track of **anchoring** or **reinforced** timers, including exact vulnerability windows.
|
||||
- Share real-time data across the map with your corporation or alliance, ensuring everyone is on the same page.
|
||||
|
||||
In this guide, we’ll explore how to enable the Structures Widget, manage structure data, and make use of the built-in API for remote structure updates.
|
||||
|
||||
---
|
||||
|
||||
### 1. Enabling the Structure Widget
|
||||
|
||||

|
||||
|
||||
1. **Open the Map:**
|
||||
2. **Locate the Widget Settings:** By default, the structure widget panel is not visible. Enable it by going to menu -> map settings -> widgets.
|
||||
3. **Add the Structures Widget:** Click the checkbox for **Structures** from the list of available widgets.
|
||||
|
||||
> **Tip:** Rearrange your widgets by dragging them around the panel to suit your workflow.
|
||||
|
||||
---
|
||||
|
||||
### 2. Overview of the Structures Widget
|
||||
|
||||

|
||||
|
||||
Once enabled, the **Structures Widget** appears in the map. It shows:
|
||||
|
||||
- **Structure Type** (Astrahus, Fortizar, etc.)
|
||||
- **Structure Name** (auto-detected if you paste from D-Scan)
|
||||
- **Owner** (Corporation ticker)
|
||||
- **Status** (Powered, Anchoring, Low Power, Reinforced, etc.)
|
||||
- **Timer** (Reinforced or anchoring end time)
|
||||
|
||||
You can **click** or **double-click** on an entry to edit details like the structure’s owner or add notes about the structure’s purpose or location.
|
||||
|
||||
---
|
||||
|
||||
### 3. Adding Structures via Copy & Paste
|
||||
|
||||
A fast way to add structure data is by copying from in-game D-Scan or show-info panels:
|
||||
|
||||
1. **In EVE Online:** Open the D-Scan window or structure context menu, select the relevant lines of text, and press **Ctrl + C**.
|
||||
2. **In the Widget:** Focus on the Structures Widget, click in the widget area, and press **Ctrl + V** to paste or use the **blue** add structure info button.
|
||||
3. The widget automatically parses the structure names and types. You can also add owners and notes manually.
|
||||
|
||||
This eliminates manual typing and reduces the chance of errors, especially useful when scanning multiple systems.
|
||||
|
||||
---
|
||||
|
||||
### 4. Tracking Reinforced Timers
|
||||
|
||||
When a structure is in a **Reinforced** or **Anchoring** state, we have a timer to note when it becomes vulnerable or completes anchoring:
|
||||
|
||||
- **Timer Field:** If the structure’s status is set to “Reinforced” or “Anchoring,” the widget enables a **Calendar** pop-up where you can set the _end time_.
|
||||
|
||||
Keep your fleet prepared by referencing this schedule. When the timer hits zero, the structure becomes vulnerable (or finishes anchoring).
|
||||
|
||||
---
|
||||
|
||||
### 5. Editing and Deleting Structures
|
||||
|
||||
1. **Single-click** a structure entry to select it.
|
||||
2. Press **Delete** (or **Backspace**) to remove it entirely—useful when clearing out old data or removing outdated structures.
|
||||
3. **Double-click** to open the **Edit Dialog**:
|
||||
- Change **Name**, **Owner**, or **Status**.
|
||||
- Update or remove **Reinforced** timers.
|
||||
- Add or edit **Notes**.
|
||||
|
||||
Any changes made here are immediately visible to other map users.
|
||||
|
||||
---
|
||||
|
||||
### 6. API Integration for Automated Timers
|
||||
|
||||
Beyond the in-app widget, there is a dedicated API endpoint to fetch or update structure timers programmatically. This allows advanced users and third-party applications to seamlessly incorporate structure data.
|
||||
|
||||
**Example API Request/Response**:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
|
||||
"https://wanderer.yourdomain.space/api/map/structure-timers?slug=yourmap"
|
||||
|
||||
"data": [
|
||||
{
|
||||
"name": "Overlook Hotel",
|
||||
"status": "Reinforced",
|
||||
"notes": null,
|
||||
"owner_id": null,
|
||||
"solar_system_id": 31000515,
|
||||
"solar_system_name": "J114942",
|
||||
"character_eve_id": "2122839817",
|
||||
"system_id": "4865aec4-b69d-4524-91d3-250b0556322b",
|
||||
"end_time": "2025-01-22T23:42:03.000000Z",
|
||||
"owner_name": null,
|
||||
"owner_ticker": null,
|
||||
"structure_type": "Astrahus",
|
||||
"structure_type_id": "35832"
|
||||
},
|
||||
{
|
||||
"name": "Some Structure",
|
||||
"status": "Reinforced",
|
||||
"notes": null,
|
||||
"owner_id": null,
|
||||
"solar_system_id": 3100229,
|
||||
"solar_system_name": "somecustomname",
|
||||
"character_eve_id": "some name",
|
||||
"system_id": "ae779ed6-92b3-4349-899d-f1bdf299082f",
|
||||
"end_time": "2025-01-16T03:04:00.000000Z",
|
||||
"owner_name": null,
|
||||
"owner_ticker": null,
|
||||
"structure_type": "Athanor",
|
||||
"structure_type_id": "35835"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
With this API, you could, for example, build automated pings on Slack/Discord when timers are about to expire or display status updates on a custom web dashboard.
|
||||
|
||||
> **Note:** Ensure your API token (`Bearer YOUR_API_TOKEN`) matches the api key generated for you map.
|
||||
|
||||
---
|
||||
|
||||
### 7. Best Practices & Tips
|
||||
|
||||
- **Keep Data Fresh:** Update timers as soon as possible after a structure enters reinforcement. This keeps your corporation or alliance fully informed.
|
||||
- **Use Notes Effectively:** Add details such as final reinforcement phases or relevant system intel (e.g., known hostiles, safe spots) to help allies plan more effectively.
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The **Structures Widget** is your central hub for monitoring, updating, and sharing information about Upwell structures across New Eden. From real-time timer tracking to simple copy-and-paste integration with D-Scan, this widget streamlines group operations and cuts down on manual data entry.
|
||||
|
||||
Whether you’re a solo explorer managing a personal citadel network or a fleet commander overseeing multiple staging systems, the Structures Widget and its accompanying API ensure you’ll always have up-to-date intel on the structures that matter most.
|
||||
|
||||
Fly safe,
|
||||
**The Wanderer Team**
|
||||
@@ -222,7 +222,7 @@
|
||||
{
|
||||
"mass_regen": 500000000,
|
||||
"dest": "hs",
|
||||
"src": ["c3"],
|
||||
"src": ["c3", "c4-shattered"],
|
||||
"static": true,
|
||||
"max_mass_per_jump": 300000000,
|
||||
"lifetime": "24",
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_type_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "notes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "owner_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "owner_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "owner_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "status",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "end_time",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_system_structures_v1_system_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "map_system_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "system_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "B9DA704034C53F0EC20C28EED99D579A34034655225EDC3BC7E57719B276F83F",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_system_structures_v1"
|
||||
}
|
||||
Reference in New Issue
Block a user