feat(app): implement character login and destination setting for multiple characters
This commit introduces several key features: - **Multiple Character Support**: The application can now handle multiple logged-in EVE Online characters. - **List Characters**: A new function `ListCharacters` allows retrieving a list of all authenticated characters. - **Set Destination for All**: The `SetDestinationForAll` function enables setting a destination for all logged-in characters simultaneously. - **UI Updates**: The frontend has been updated to display logged-in characters and to allow setting destinations for all characters. - **Backend Refinements**: The ESI SSO logic has been improved to support refreshing tokens for multiple characters and to handle the new multi-character functionality. - **Dependency Updates**: Dependencies have been updated to their latest versions. chore: update go module dependencies
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { StartESILogin, ESILoginStatus, ESILoggedIn } from 'wailsjs/go/main/App';
|
||||
import { StartESILogin, ESILoggedIn, ListCharacters } from 'wailsjs/go/main/App';
|
||||
|
||||
interface HeaderProps {
|
||||
title: string;
|
||||
@@ -21,24 +21,31 @@ interface HeaderProps {
|
||||
}>;
|
||||
}
|
||||
|
||||
interface CharacterInfo { character_id: number; character_name: string }
|
||||
|
||||
export const Header = ({ title, breadcrumbs = [] }: HeaderProps) => {
|
||||
const navigate = useNavigate();
|
||||
const [status, setStatus] = useState<string>('');
|
||||
const [chars, setChars] = useState<CharacterInfo[]>([]);
|
||||
|
||||
const refreshState = async () => {
|
||||
try {
|
||||
const list = await ListCharacters();
|
||||
setChars((list as any[]).map((c: any) => ({ character_id: c.character_id, character_name: c.character_name })));
|
||||
} catch {}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
ESILoginStatus().then(setStatus).catch(() => setStatus(''));
|
||||
refreshState();
|
||||
}, []);
|
||||
|
||||
const handleLogin = async () => {
|
||||
try {
|
||||
await StartESILogin();
|
||||
toast({ title: 'EVE Login', description: 'Complete login in your browser.' });
|
||||
// Poll a few times to update status after redirect callback
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const ok = await ESILoggedIn();
|
||||
if (ok) {
|
||||
const s = await ESILoginStatus();
|
||||
setStatus(s);
|
||||
await refreshState();
|
||||
break;
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
@@ -50,7 +57,6 @@ export const Header = ({ title, breadcrumbs = [] }: HeaderProps) => {
|
||||
|
||||
return (
|
||||
<div className="flex-shrink-0 py-4 px-4 border-b border-purple-500/20">
|
||||
{/* Breadcrumb Navigation */}
|
||||
{breadcrumbs.length > 0 && (
|
||||
<div className="mb-3">
|
||||
<Breadcrumb>
|
||||
@@ -79,11 +85,18 @@ export const Header = ({ title, breadcrumbs = [] }: HeaderProps) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Title + EVE SSO */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold text-white">{title}</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-slate-300">{status || 'EVE: not logged in'}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
{chars.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 max-w-[50vw] justify-end">
|
||||
{chars.map((c) => (
|
||||
<span key={c.character_id} className="px-2 py-1 rounded-full bg-purple-500/20 text-purple-200 border border-purple-400/40 text-xs whitespace-nowrap">
|
||||
{c.character_name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<Button size="sm" className="bg-purple-600 hover:bg-purple-700" onClick={handleLogin}>Log in</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { System } from '@/lib/types';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { StartESILogin, ESILoginStatus, SetDestinationByName } from 'wailsjs/go/main/App';
|
||||
import { StartESILogin, ListCharacters, SetDestinationForAll } from 'wailsjs/go/main/App';
|
||||
|
||||
interface SystemContextMenuProps {
|
||||
x: number;
|
||||
@@ -29,15 +29,24 @@ export const SystemContextMenu = ({ x, y, system, onRename, onDelete, onClearCon
|
||||
setIsRenaming(false);
|
||||
};
|
||||
|
||||
const handleSetDestination = async () => {
|
||||
const ensureLoggedInAny = async () => {
|
||||
try {
|
||||
const status = await ESILoginStatus();
|
||||
if (status.includes('not logged in')) {
|
||||
await StartESILogin();
|
||||
toast({ title: 'EVE Login', description: 'Please complete login in your browser, then retry.' });
|
||||
return;
|
||||
}
|
||||
await SetDestinationByName(system.solarSystemName, true, false);
|
||||
const list = await ListCharacters();
|
||||
if (Array.isArray(list) && list.length > 0) return true;
|
||||
await StartESILogin();
|
||||
toast({ title: 'EVE Login', description: 'Please complete login in your browser, then retry.' });
|
||||
return false;
|
||||
} catch {
|
||||
await StartESILogin();
|
||||
toast({ title: 'EVE Login', description: 'Please complete login in your browser, then retry.' });
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSetDestinationAll = async () => {
|
||||
try {
|
||||
if (!(await ensureLoggedInAny())) return;
|
||||
await SetDestinationForAll(system.solarSystemName, true, false);
|
||||
toast({ title: 'Destination set', description: `${system.solarSystemName}` });
|
||||
onClose();
|
||||
} catch (e: any) {
|
||||
@@ -102,8 +111,8 @@ export const SystemContextMenu = ({ x, y, system, onRename, onDelete, onClearCon
|
||||
</button>
|
||||
<div className="h-px bg-slate-700 my-1" />
|
||||
<button
|
||||
onClick={handleSetDestination}
|
||||
className="w-full px-3 py-1 text-left text-green-400 hover:bg-slate-700 rounded text-sm"
|
||||
onClick={handleSetDestinationAll}
|
||||
className="w-full px-3 py-1 text-left text-emerald-400 hover:bg-slate-700 rounded text-sm"
|
||||
>
|
||||
Set destination
|
||||
</button>
|
||||
|
5
frontend/wailsjs/go/main/App.d.ts
vendored
5
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
import {main} from '../models';
|
||||
|
||||
export function ESILoggedIn():Promise<boolean>;
|
||||
|
||||
@@ -7,8 +8,12 @@ export function ESILoginStatus():Promise<string>;
|
||||
|
||||
export function Greet(arg1:string):Promise<string>;
|
||||
|
||||
export function ListCharacters():Promise<Array<main.CharacterInfo>>;
|
||||
|
||||
export function SetDestination(arg1:number,arg2:boolean,arg3:boolean):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 StartESILogin():Promise<string>;
|
||||
|
@@ -14,6 +14,10 @@ export function Greet(arg1) {
|
||||
return window['go']['main']['App']['Greet'](arg1);
|
||||
}
|
||||
|
||||
export function ListCharacters() {
|
||||
return window['go']['main']['App']['ListCharacters']();
|
||||
}
|
||||
|
||||
export function SetDestination(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['SetDestination'](arg1, arg2, arg3);
|
||||
}
|
||||
@@ -22,6 +26,10 @@ export function SetDestinationByName(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['SetDestinationByName'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function SetDestinationForAll(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['SetDestinationForAll'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function StartESILogin() {
|
||||
return window['go']['main']['App']['StartESILogin']();
|
||||
}
|
||||
|
19
frontend/wailsjs/go/models.ts
Normal file
19
frontend/wailsjs/go/models.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export namespace main {
|
||||
|
||||
export class CharacterInfo {
|
||||
character_id: number;
|
||||
character_name: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new CharacterInfo(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.character_id = source["character_id"];
|
||||
this.character_name = source["character_name"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user