feat(esi): add PostRouteForAllByNames and resolve system names in batch
This commit introduces a new function `PostRouteForAllByNames` to the ESI service, which allows setting a complete route (including waypoints) for all logged-in characters. This is achieved by batch resolving system names to their IDs, improving efficiency and simplifying the process of setting complex routes. The changes include: - Adding `ResolveSystemIDsByNames` to `ESISSO` to fetch multiple system IDs in a single ESI request. - Implementing `PostRouteForAll` in `ESISSO` to handle the logic of setting the destination and waypoints for all characters. - Updating `App.go` to expose `PostRouteForAllByNames` for frontend use. - Modifying the frontend component `RegionMap.tsx` to utilize the new `PostRouteForAllByNames` function when setting routes, replacing the previous sequential calls to `SetDestinationForAll` and `AddWaypointForAllByName`. - Updating Wails generated type definitions (`.d.ts` and `.js`) to reflect the new function.
This commit is contained in:
39
app.go
39
app.go
@@ -58,7 +58,6 @@ func (a *App) StartESILogin() (string, error) {
|
|||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESILoginStatus returns a short status string of the active token/character
|
|
||||||
func (a *App) ESILoginStatus() string {
|
func (a *App) ESILoginStatus() string {
|
||||||
if a.ssi == nil {
|
if a.ssi == nil {
|
||||||
return "not initialised"
|
return "not initialised"
|
||||||
@@ -70,7 +69,6 @@ func (a *App) ESILoginStatus() string {
|
|||||||
return "not logged in"
|
return "not logged in"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESILoggedIn returns true if a valid access token is present
|
|
||||||
func (a *App) ESILoggedIn() bool {
|
func (a *App) ESILoggedIn() bool {
|
||||||
if a.ssi == nil {
|
if a.ssi == nil {
|
||||||
return false
|
return false
|
||||||
@@ -78,27 +76,6 @@ func (a *App) ESILoggedIn() bool {
|
|||||||
return a.ssi.Status().LoggedIn
|
return a.ssi.Status().LoggedIn
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDestination posts a waypoint to ESI to set destination
|
|
||||||
func (a *App) SetDestination(destinationID int64, clearOthers bool, addToBeginning bool) error {
|
|
||||||
if a.ssi == nil {
|
|
||||||
return errors.New("ESI not initialised")
|
|
||||||
}
|
|
||||||
return a.ssi.PostWaypoint(destinationID, clearOthers, addToBeginning)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDestinationByName resolves a solar system name to ID and sets destination
|
|
||||||
func (a *App) SetDestinationByName(systemName string, clearOthers bool, addToBeginning bool) error {
|
|
||||||
if a.ssi == nil {
|
|
||||||
return errors.New("ESI not initialised")
|
|
||||||
}
|
|
||||||
id, err := a.ssi.ResolveSystemIDByName(a.ctx, systemName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return a.ssi.PostWaypoint(id, clearOthers, addToBeginning)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDestinationForAll resolves system name to ID and applies waypoint to all logged-in characters
|
|
||||||
func (a *App) SetDestinationForAll(systemName string, clearOthers bool, addToBeginning bool) error {
|
func (a *App) SetDestinationForAll(systemName string, clearOthers bool, addToBeginning bool) error {
|
||||||
if a.ssi == nil {
|
if a.ssi == nil {
|
||||||
return errors.New("ESI not initialised")
|
return errors.New("ESI not initialised")
|
||||||
@@ -110,7 +87,6 @@ func (a *App) SetDestinationForAll(systemName string, clearOthers bool, addToBeg
|
|||||||
return a.ssi.PostWaypointForAll(id, clearOthers, addToBeginning)
|
return a.ssi.PostWaypointForAll(id, clearOthers, addToBeginning)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddWaypointForAllByName resolves system name and appends as waypoint for all characters
|
|
||||||
func (a *App) AddWaypointForAllByName(systemName string, addToBeginning bool) error {
|
func (a *App) AddWaypointForAllByName(systemName string, addToBeginning bool) error {
|
||||||
if a.ssi == nil {
|
if a.ssi == nil {
|
||||||
return errors.New("ESI not initialised")
|
return errors.New("ESI not initialised")
|
||||||
@@ -122,7 +98,20 @@ func (a *App) AddWaypointForAllByName(systemName string, addToBeginning bool) er
|
|||||||
return a.ssi.PostWaypointForAll(id, false, addToBeginning)
|
return a.ssi.PostWaypointForAll(id, false, addToBeginning)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCharacters returns all characters stored in the token DB
|
// PostRouteForAllByNames posts a full route: via names (in order) then destination at the end (clearing first)
|
||||||
|
func (a *App) PostRouteForAllByNames(destination string, vias []string) error {
|
||||||
|
if a.ssi == nil {
|
||||||
|
return errors.New("ESI not initialised")
|
||||||
|
}
|
||||||
|
ids, err := a.ssi.ResolveSystemIDsByNames(a.ctx, append(vias, destination))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
viaIDs := ids[:len(vias)]
|
||||||
|
destID := ids[len(vias)]
|
||||||
|
return a.ssi.PostRouteForAll(destID, viaIDs)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) ListCharacters() ([]CharacterInfo, error) {
|
func (a *App) ListCharacters() ([]CharacterInfo, error) {
|
||||||
if a.ssi == nil || a.ssi.db == nil {
|
if a.ssi == nil || a.ssi.db == nil {
|
||||||
return nil, errors.New("ESI not initialised")
|
return nil, errors.New("ESI not initialised")
|
||||||
|
77
esi_sso.go
77
esi_sso.go
@@ -670,4 +670,81 @@ func (s *ESISSO) ResolveSystemIDByName(ctx context.Context, name string) (int64,
|
|||||||
}
|
}
|
||||||
fmt.Printf("ESI: names resolved fallback: returning %d for %q\n", namesResp[0].ID, name)
|
fmt.Printf("ESI: names resolved fallback: returning %d for %q\n", namesResp[0].ID, name)
|
||||||
return namesResp[0].ID, nil
|
return namesResp[0].ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveSystemIDsByNames returns IDs in the same order as names. Missing entries error.
|
||||||
|
func (s *ESISSO) ResolveSystemIDsByNames(ctx context.Context, names []string) ([]int64, error) {
|
||||||
|
ordered := make([]int64, len(names))
|
||||||
|
if len(names) == 0 {
|
||||||
|
return ordered, nil
|
||||||
|
}
|
||||||
|
// DB first
|
||||||
|
nameLower := make([]string, len(names))
|
||||||
|
for i, n := range names { nameLower[i] = strings.TrimSpace(n) }
|
||||||
|
found := map[string]int64{}
|
||||||
|
if s.db != nil {
|
||||||
|
var rows []SolarSystem
|
||||||
|
if err := s.db.Where("solarSystemName IN ?", nameLower).Find(&rows).Error; err == nil {
|
||||||
|
for _, r := range rows {
|
||||||
|
found[r.SolarSystemName] = r.SolarSystemID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Collect missing for ESI batch
|
||||||
|
missing := []string{}
|
||||||
|
for _, n := range nameLower {
|
||||||
|
if _, ok := found[n]; !ok {
|
||||||
|
missing = append(missing, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(missing) > 0 {
|
||||||
|
idsURL := esiBase + "/v3/universe/ids/?datasource=tranquility"
|
||||||
|
body, _ := json.Marshal(struct{ Names []string `json:"names"` }{Names: missing})
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, idsURL, strings.NewReader(string(body)))
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
b, _ := io.ReadAll(resp.Body)
|
||||||
|
return nil, fmt.Errorf("universe/ids failed: %s: %s", resp.Status, string(b))
|
||||||
|
}
|
||||||
|
var idsResp struct{ Systems []struct{ ID int64 `json:"id"`; Name string `json:"name"` } `json:"systems"` }
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&idsResp); err != nil { return nil, err }
|
||||||
|
for _, it := range idsResp.Systems { found[it.Name] = it.ID }
|
||||||
|
}
|
||||||
|
// Build ordered list
|
||||||
|
for i, n := range nameLower {
|
||||||
|
id, ok := found[n]
|
||||||
|
if !ok { return nil, fmt.Errorf("system not found: %s", n) }
|
||||||
|
ordered[i] = id
|
||||||
|
}
|
||||||
|
return ordered, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostRouteForAll clears route and posts vias then destination last
|
||||||
|
func (s *ESISSO) PostRouteForAll(destID int64, viaIDs []int64) error {
|
||||||
|
if s.db == nil { return errors.New("db not initialised") }
|
||||||
|
var tokens []ESIToken
|
||||||
|
if err := s.db.Find(&tokens).Error; err != nil { return err }
|
||||||
|
var firstErr error
|
||||||
|
for i := range tokens {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
tok, err := s.ensureAccessTokenFor(ctx, &tokens[i])
|
||||||
|
cancel()
|
||||||
|
if err != nil { if firstErr == nil { firstErr = err }; continue }
|
||||||
|
// Determine sequence: clear with first element (via if any, else dest)
|
||||||
|
if len(viaIDs) > 0 {
|
||||||
|
if err := s.postWaypointWithToken(tok, viaIDs[0], true, false); err != nil && firstErr == nil { firstErr = err }
|
||||||
|
for _, id := range viaIDs[1:] {
|
||||||
|
if err := s.postWaypointWithToken(tok, id, false, false); err != nil && firstErr == nil { firstErr = err }
|
||||||
|
}
|
||||||
|
if err := s.postWaypointWithToken(tok, destID, false, false); err != nil && firstErr == nil { firstErr = err }
|
||||||
|
} else {
|
||||||
|
if err := s.postWaypointWithToken(tok, destID, true, false); err != nil && firstErr == nil { firstErr = err }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
}
|
}
|
@@ -8,7 +8,7 @@ import { loadWormholeSystems, saveWormholeSystem, deleteWormholeSystem } from '@
|
|||||||
import { System, Position, Connection as ConnectionType } from '@/lib/types';
|
import { System, Position, Connection as ConnectionType } from '@/lib/types';
|
||||||
import { getSecurityColor } from '@/utils/securityColors';
|
import { getSecurityColor } from '@/utils/securityColors';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName } from 'wailsjs/go/main/App';
|
import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName, PostRouteForAllByNames } from 'wailsjs/go/main/App';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
|
||||||
interface RegionMapProps {
|
interface RegionMapProps {
|
||||||
@@ -76,14 +76,10 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onKeyDown = async (e: KeyboardEvent) => {
|
const onKeyDown = async (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape' && viaMode) {
|
if (e.key === 'Escape' && viaMode) {
|
||||||
// Commit the route: post destination then queued waypoints
|
|
||||||
try {
|
try {
|
||||||
if (!(await ensureAnyLoggedIn())) return;
|
if (!(await ensureAnyLoggedIn())) return;
|
||||||
if (viaDest) {
|
if (viaDest) {
|
||||||
await SetDestinationForAll(viaDest, true, false);
|
await PostRouteForAllByNames(viaDest, viaQueue);
|
||||||
for (const name of viaQueue) {
|
|
||||||
await AddWaypointForAllByName(name, false);
|
|
||||||
}
|
|
||||||
toast({ title: 'Route set', description: `${viaDest}${viaQueue.length ? ' via ' + viaQueue.join(', ') : ''}` });
|
toast({ title: 'Route set', description: `${viaDest}${viaQueue.length ? ' via ' + viaQueue.join(', ') : ''}` });
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
4
frontend/wailsjs/go/main/App.d.ts
vendored
4
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -12,9 +12,7 @@ export function Greet(arg1:string):Promise<string>;
|
|||||||
|
|
||||||
export function ListCharacters():Promise<Array<main.CharacterInfo>>;
|
export function ListCharacters():Promise<Array<main.CharacterInfo>>;
|
||||||
|
|
||||||
export function SetDestination(arg1:number,arg2:boolean,arg3:boolean):Promise<void>;
|
export function PostRouteForAllByNames(arg1:string,arg2:Array<string>):Promise<void>;
|
||||||
|
|
||||||
export function SetDestinationByName(arg1:string,arg2:boolean,arg3:boolean):Promise<void>;
|
|
||||||
|
|
||||||
export function SetDestinationForAll(arg1:string,arg2:boolean,arg3:boolean):Promise<void>;
|
export function SetDestinationForAll(arg1:string,arg2:boolean,arg3:boolean):Promise<void>;
|
||||||
|
|
||||||
|
@@ -22,12 +22,8 @@ export function ListCharacters() {
|
|||||||
return window['go']['main']['App']['ListCharacters']();
|
return window['go']['main']['App']['ListCharacters']();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SetDestination(arg1, arg2, arg3) {
|
export function PostRouteForAllByNames(arg1, arg2) {
|
||||||
return window['go']['main']['App']['SetDestination'](arg1, arg2, arg3);
|
return window['go']['main']['App']['PostRouteForAllByNames'](arg1, arg2);
|
||||||
}
|
|
||||||
|
|
||||||
export function SetDestinationByName(arg1, arg2, arg3) {
|
|
||||||
return window['go']['main']['App']['SetDestinationByName'](arg1, arg2, arg3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SetDestinationForAll(arg1, arg2, arg3) {
|
export function SetDestinationForAll(arg1, arg2, arg3) {
|
||||||
|
Reference in New Issue
Block a user