Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,42 @@ export const CompactLocationsPage = () => {

const routeData = useLoaderData({ from: '/compact/' });

const { data: instances } = useQuery(getInstancesQueryOptions);

const allInstances = instances ?? routeData.instances;
const allTunnels = routeData.tunnels;

const queryInstanceId = useMemo(() => {
if (!isPresent(selection)) return routeData.instances[0].id;
if (selection.kind === 'instance') return selection.data.id;
return selection.data.instance_id;
}, [selection, routeData.instances]);
if (selection.kind === 'instance') return selection.id;
return (
allTunnels.find((t) => t.id === selection.id)?.instance_id ??
routeData.instances[0].id
);
}, [selection, routeData.instances, allTunnels]);

const { data: locations } = useQuery(getLocationsQueryOptions(queryInstanceId));

const { data: instances } = useQuery(getInstancesQueryOptions);

const instanceInfo = useMemo(() => {
const allInstances = instances ?? routeData.instances;
if (!isPresent(selection)) return allInstances[0];
if (selection.kind === 'instance')
return allInstances.find((i) => i.id === selection.data.id);
return allInstances.find((i) => i.id === selection.data.instance_id);
}, [selection, instances, routeData.instances]);
return allInstances.find((i) => i.id === selection.id);
const tunnel = allTunnels.find((t) => t.id === selection.id);
return tunnel ? allInstances.find((i) => i.id === tunnel.instance_id) : undefined;
}, [selection, allInstances, allTunnels]);

const displayedLocations = useMemo(() => {
if (!isPresent(selection) || selection.kind === 'instance') {
return locations ?? routeData.locations;
}
return [selection.data];
}, [selection, locations, routeData.locations]);
const tunnel = allTunnels.find((t) => t.id === selection.id);
return tunnel ? [tunnel] : [];
}, [selection, locations, routeData.locations, allTunnels]);

useEffect(() => {
if (selection?.kind === 'tunnel') return;
if (selection === null || instanceInfo === undefined) {
setViewSelection({ kind: 'instance', data: routeData.instances[0] });
setViewSelection({ kind: 'instance', id: routeData.instances[0].id });
}
}, [routeData.instances, instanceInfo, selection, setViewSelection]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const InstanceSwitcher = () => {
options: instances.map((instance) => ({
key: instance.id,
label: instance.name,
value: { kind: 'instance', data: instance },
value: { kind: 'instance', id: instance.id },
})),
};

Expand All @@ -38,7 +38,7 @@ export const InstanceSwitcher = () => {
options: tunnels.map((tunnel) => ({
key: tunnel.id ?? tunnel.name,
label: tunnel.name,
value: { kind: 'tunnel', data: tunnel },
value: { kind: 'tunnel', id: tunnel.id },
})),
};

Expand All @@ -54,13 +54,9 @@ export const InstanceSwitcher = () => {
if (!isPresent(selectedInstance)) return undefined;
for (const group of groups) {
const found = group.options.find((o) => {
if (selectedInstance.kind === 'instance' && o.value.kind === 'instance') {
return o.value.data.id === selectedInstance.data.id;
}
if (selectedInstance.kind === 'tunnel' && o.value.kind === 'tunnel') {
return o.value.data.id === selectedInstance.data.id;
}
return false;
return (
o.value.kind === selectedInstance.kind && o.value.id === selectedInstance.id
);
});
if (found) return found;
}
Expand Down
32 changes: 23 additions & 9 deletions new-ui/src/pages/full/OverviewPage/OverviewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,36 @@ export const OverviewPage = () => {
const { instances, tunnels } = useAppData();
const { viewSelection: selection } = useAppData();

const selectedTunnel = useMemo(
() =>
selection?.kind === 'tunnel'
? tunnels.find((t) => t.id === selection.id)
: undefined,
[selection, tunnels],
);

const selectedInstance = useMemo(
() =>
selection?.kind === 'instance'
? instances.find((i) => i.id === selection.id)
: undefined,
[selection, instances],
);

const queryInstanceId = useMemo(() => {
if (!isPresent(selection)) return instances[0].id;
if (selection.kind === 'instance') return selection.data.id;
return selection.data.instance_id;
}, [selection, instances]);
if (selection.kind === 'instance') return selection.id;
return selectedTunnel?.instance_id ?? instances[0].id;
}, [selection, instances, selectedTunnel]);

const { data: locations } = useQuery(getLocationsQueryOptions(queryInstanceId));

const displayedLocations = useMemo(() => {
if (!isPresent(selection) || selection.kind === 'instance') {
return locations ?? [];
}
return [selection.data];
}, [selection, locations]);
return selectedTunnel ? [selectedTunnel] : [];
}, [selection, locations, selectedTunnel]);

return (
<Fragment>
Expand All @@ -53,10 +69,8 @@ export const OverviewPage = () => {
<SizedBox height={ThemeSpacing.Xl} />
<div className="locations">
{displayedLocations.map((location) => {
let instance: InstanceInfo | undefined;
if (selection?.kind === 'instance') {
instance = selection.data;
}
const instance: InstanceInfo | undefined =
selection?.kind === 'instance' ? selectedInstance : undefined;
return (
<OverviewLocationCard
location={location}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ export const OverviewSelection = ({ instances, tunnels }: Props) => {

const isSelected = (candidate: OverviewViewSelection): boolean => {
if (!selection) return false;
if (candidate.kind !== selection.kind) return false;
return candidate.data.id === selection.data.id;
return candidate.kind === selection.kind && candidate.id === selection.id;
};

return (
Expand All @@ -44,10 +43,10 @@ export const OverviewSelection = ({ instances, tunnels }: Props) => {
<p className="label">Instances</p>
<div className="items">
{instances.map((instance) => {
const value: OverviewViewSelection = { kind: 'instance', data: instance };
const value: OverviewViewSelection = { kind: 'instance', id: instance.id };
return (
<SelectionItem
key={instance.id}
key={`instance-${instance.id}`}
label={instance.name}
selected={isSelected(value)}
onClick={() => setSelection(value)}
Expand All @@ -62,10 +61,10 @@ export const OverviewSelection = ({ instances, tunnels }: Props) => {
<p className="label">Tunnels</p>
<div className="items">
{tunnels.map((tunnel) => {
const value: OverviewViewSelection = { kind: 'tunnel', data: tunnel };
const value: OverviewViewSelection = { kind: 'tunnel', id: tunnel.id };
return (
<SelectionItem
key={tunnel.id ?? tunnel.name}
key={`tunnel-${tunnel.id ?? tunnel.name}`}
label={tunnel.name}
selected={isSelected(value)}
onClick={() => setSelection(value)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,95 @@ import { TunnelWizardStep, type TunnelWizardStepValue } from '../types';

type StoreValues = {
activeStep: TunnelWizardStepValue;
tunnelData: {
name: string;
pubkey: string;
prvkey: string;
address: string;
server_pubkey: string;
preshared_key: string;
allowed_ips?: string;
endpoint: string;
dns?: string;
persistent_keep_alive: number;
route_all_traffic: boolean;
pre_up?: string;
post_up?: string;
pre_down?: string;
post_down?: string;
};
};

const defaults: StoreValues = {
activeStep: TunnelWizardStep.GeneralInformation,
tunnelData: {
name: '',
address: '',
endpoint: '',
persistent_keep_alive: 25,
preshared_key: '',
prvkey: '',
pubkey: '',
route_all_traffic: false,
server_pubkey: '',
allowed_ips: '',
dns: '',
post_down: '',
post_up: '',
pre_down: '',
pre_up: '',
},
};

const nextStep = (step: TunnelWizardStepValue): TunnelWizardStepValue => {
switch (step) {
case TunnelWizardStep.GeneralInformation:
return TunnelWizardStep.Keys;
case TunnelWizardStep.Keys:
return TunnelWizardStep.VpnServer;
case TunnelWizardStep.VpnServer:
return TunnelWizardStep.AdvancedSettings;
case TunnelWizardStep.AdvancedSettings:
return TunnelWizardStep.Finish;
default:
return step;
}
};

const STEPS: TunnelWizardStepValue[] = [
TunnelWizardStep.GeneralInformation,
TunnelWizardStep.Keys,
TunnelWizardStep.VpnServer,
TunnelWizardStep.AdvancedSettings,
TunnelWizardStep.Finish,
];
const prevStep = (step: TunnelWizardStepValue): TunnelWizardStepValue => {
switch (step) {
case TunnelWizardStep.Keys:
return TunnelWizardStep.GeneralInformation;
case TunnelWizardStep.VpnServer:
return TunnelWizardStep.Keys;
case TunnelWizardStep.AdvancedSettings:
return TunnelWizardStep.VpnServer;
case TunnelWizardStep.Finish:
return TunnelWizardStep.AdvancedSettings;
default:
return step;
}
};

interface Store extends StoreValues {
next: () => void;
back: () => void;
next: (values?: Partial<StoreValues['tunnelData']>) => void;
back: (values?: Partial<StoreValues['tunnelData']>) => void;
reset: () => void;
}

export const useTunnelWizardStore = create<Store>()((set, get) => ({
...defaults,
next: () => {
const idx = STEPS.indexOf(get().activeStep);
if (idx < STEPS.length - 1) set({ activeStep: STEPS[idx + 1] });
next: (tunnelData) => {
set({
activeStep: nextStep(get().activeStep),
tunnelData: { ...get().tunnelData, ...tunnelData },
});
},
back: () => {
const idx = STEPS.indexOf(get().activeStep);
if (idx > 0) set({ activeStep: STEPS[idx - 1] });
back: (tunnelData) => {
set({
activeStep: prevStep(get().activeStep),
tunnelData: { ...get().tunnelData, ...tunnelData },
});
},
reset: () => set(defaults),
}));
Loading
Loading