Skip to content
Merged
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
1 change: 1 addition & 0 deletions new-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-log": "^2.8.0",
"@tauri-apps/plugin-os": "^2.3.2",
"@uidotdev/usehooks": "^2.4.1",
"byte-size": "^9.0.1",
"chart.js": "^4.5.1",
Expand Down
10 changes: 10 additions & 0 deletions new-ui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions new-ui/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { RouterProvider } from '@tanstack/react-router';
import { MainBackground } from '../shared/components/MainBackground/MainBackground';
import { TauriEventProvider } from '../shared/providers/TauriEventProvider';
import { queryClient } from './query';
import { router } from './router';

Expand All @@ -11,9 +10,7 @@ function App() {
<MainBackground />
<div id="app-content">
<QueryClientProvider client={queryClient}>
<TauriEventProvider>
<RouterProvider router={router} />
</TauriEventProvider>
<RouterProvider router={router} />
</QueryClientProvider>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import './style.scss';
import { useQuery } from '@tanstack/react-query';
import { useLoaderData } from '@tanstack/react-router';
import { platform } from '@tauri-apps/plugin-os';
import clsx from 'clsx';
import { useEffect, useMemo } from 'react';
import { Button } from '../../../shared/components/Button/Button';
import { ButtonVariant } from '../../../shared/components/Button/types';
Expand All @@ -19,6 +21,8 @@ import { CompactPage } from '../CompactPage/CompactPage';
import { InstanceSwitcher } from './components/InstanceSwitcher';
import { useCompactLocationStore } from './hooks/useCompactLocationsStore';

const isWindows = platform() === 'windows';

export const CompactLocationsPage = () => {
const selection = useCompactLocationStore((s) => s.compactViewSelection);
const openLocation = useCompactLocationStore((s) => s.expandedLocation);
Expand Down Expand Up @@ -65,7 +69,11 @@ export const CompactLocationsPage = () => {
}}
>
<WindowHeader variant="compact" />
<div className="scroll-wrap">
<div
className={clsx('scroll-wrap', {
windows: isWindows,
})}
>
<InstanceSwitcher />
<div className="locations">
{isPresent(instanceInfo) &&
Expand Down
13 changes: 12 additions & 1 deletion new-ui/src/pages/compact/CompactLocationsPage/style.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#compact-locations-page {
display: flex;
flex-flow: column;
min-height: 100dvh;
height: 100dvh;

> .compact-footer {
display: flex;
Expand All @@ -21,11 +21,22 @@
min-height: 0;
row-gap: var(--spacing-sm);

&.windows {
scrollbar-gutter: stable;
overflow-y: scroll;

> .locations {
padding-right: 6px;
}
}

> .locations {
display: flex;
flex-flow: column;
row-gap: var(--spacing-sm);
width: 100%;
box-sizing: border-box;
padding-right: 6px;
}
}
}
7 changes: 6 additions & 1 deletion new-ui/src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { QueryClient } from '@tanstack/react-query';
import { createRootRouteWithContext, Outlet } from '@tanstack/react-router';
import { TauriEventProvider } from '../shared/providers/TauriEventProvider';

interface RouterContext {
queryClient: QueryClient;
Expand All @@ -12,5 +13,9 @@ export const Route = createRootRouteWithContext<RouterContext>()({
});

function RootComponent() {
return <Outlet />;
return (
<TauriEventProvider>
<Outlet />
</TauriEventProvider>
);
}
17 changes: 15 additions & 2 deletions new-ui/src/routes/empty.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { createFileRoute } from '@tanstack/react-router';
import { useQuery } from '@tanstack/react-query';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { useEffect } from 'react';

import { hasAnyVisibleLocationsQueryOptions } from '../shared/rust-api/query';

export const Route = createFileRoute('/empty')({
component: RouteComponent,
});

function RouteComponent() {
return <div>Hello "/empty"!</div>;
const navigate = useNavigate();
const { data: hasLocations } = useQuery(hasAnyVisibleLocationsQueryOptions);

useEffect(() => {
if (hasLocations === true) {
void navigate({ to: '/' });
}
}, [hasLocations, navigate]);

return <div></div>;
}
3 changes: 3 additions & 0 deletions new-ui/src/shared/providers/TauriEventProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ export const TauriEventProvider = ({ children }: PropsWithChildren) => {
listen(TauriEvent.InstanceUpdate, () => {
void queryClient.invalidateQueries({ queryKey: ['instances'] });
void queryClient.invalidateQueries({ queryKey: ['locations'] });
void queryClient.invalidateQueries({ queryKey: ['has-any-visible-locations'] });
}),

listen(TauriEvent.LocationUpdate, () => {
void queryClient.invalidateQueries({ queryKey: ['locations'] });
void queryClient.invalidateQueries({ queryKey: ['location-details'] });
void queryClient.invalidateQueries({ queryKey: ['has-any-visible-locations'] });
}),

listen(TauriEvent.AppVersionFetch, () => {
Expand All @@ -41,6 +43,7 @@ export const TauriEventProvider = ({ children }: PropsWithChildren) => {
void queryClient.invalidateQueries({ queryKey: ['settings'] });
void queryClient.invalidateQueries({ queryKey: ['provisioning-config'] });
void queryClient.invalidateQueries({ queryKey: ['instances'] });
void queryClient.invalidateQueries({ queryKey: ['has-any-visible-locations'] });
}),

listen<DeadConnectionDroppedPayload>(TauriEvent.DeadConnectionDropped, () => {
Expand Down
4 changes: 4 additions & 0 deletions new-ui/src/shared/rust-api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const saveDeviceConfig = (args: SaveConfigArgs): Promise<SaveDeviceConfigRespons
const getLocations = (instanceId: number): Promise<LocationInfo[]> =>
invoke(TauriCommand.AllLocations, { instanceId });

const hasAnyVisibleLocations = (): Promise<boolean> =>
invoke(TauriCommand.HasAnyVisibleLocations);

const getLocationDetails = (args: LocationDetailsArgs): Promise<LocationDetails> =>
invoke(TauriCommand.LocationInterfaceDetails, args);

Expand Down Expand Up @@ -139,6 +142,7 @@ export const api = {
saveDeviceConfig,
// Locations
getLocations,
hasAnyVisibleLocations,
getLocationDetails,
updateLocationRouting,
setLocationMfaMethod,
Expand Down
5 changes: 5 additions & 0 deletions new-ui/src/shared/rust-api/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export const getLocationsQueryOptions = (instanceId: number) =>
queryFn: () => api.getLocations(instanceId),
});

export const hasAnyVisibleLocationsQueryOptions = queryOptions({
queryKey: ['has-any-visible-locations'] as const,
queryFn: () => api.hasAnyVisibleLocations(),
});

export const getLocationDetailsQueryOptions = (args: LocationDetailsArgs) =>
queryOptions({
queryKey: ['location-details', args.locationId, args.connectionType] as const,
Expand Down
1 change: 1 addition & 0 deletions new-ui/src/shared/rust-api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const TauriCommand = {
SaveDeviceConfig: 'save_device_config',
// Locations
AllLocations: 'all_locations',
HasAnyVisibleLocations: 'has_any_visible_locations',
LocationInterfaceDetails: 'location_interface_details',
UpdateLocationRouting: 'update_location_routing',
SetLocationMfaMethod: 'set_location_mfa_method',
Expand Down
18 changes: 18 additions & 0 deletions new-ui/src/shared/scss/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,21 @@ ol {
margin: 0;
padding: 0;
}

::-webkit-scrollbar {
width: 4px;
}

::-webkit-scrollbar-track {
background: transparent;
}

::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 100px;
}

* {
scrollbar-width: thin;
scrollbar-color: var(--fg-white-50) transparent;
}
1 change: 1 addition & 0 deletions src-tauri/permissions/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ identifier = "allow-app-commands"
description = "Allow all application commands for both UI windows (old-ui and new-ui)."
commands.allow = [
"all_locations",
"has_any_visible_locations",
"save_device_config",
"all_instances",
"connect",
Expand Down
16 changes: 13 additions & 3 deletions src-tauri/src/bin/defguard-client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ fn main() {
let app = Builder::default()
.invoke_handler(tauri::generate_handler![
all_locations,
has_any_visible_locations,
save_device_config,
all_instances,
connect,
Expand Down Expand Up @@ -194,8 +195,8 @@ fn main() {
])
.on_window_event(|window, event| {
if let WindowEvent::CloseRequested { api, .. } = event {
// Only prevent close on the tray (new-ui) window; let other windows close normally.
if window.label() == NEW_UI_WINDOW_ID {
let label = window.label();
if label == NEW_UI_WINDOW_ID || label == OLD_UI_WINDOW_ID {
#[cfg(not(target_os = "macos"))]
let _ = window.hide();

Expand Down Expand Up @@ -351,6 +352,15 @@ fn main() {
let state = AppState::new(config, provisioning_config);
app.manage(state);

// Pre-build both windows hidden so they can be shown/hidden without recreation.
if let Err(e) = WindowManager::build_tray_window(app_handle) {
warn!("Failed to pre-build tray window: {e}");
}
if let Err(e) = WindowManager::build_full_window(app_handle) {
warn!("Failed to pre-build full window: {e}");
}

// Decide which window to show based on platform and available locations.
#[cfg(target_os = "linux")]
{
let _ = WindowManager::open_full_view(app_handle);
Expand All @@ -363,7 +373,7 @@ fn main() {
if has_locations {
WindowManager::open_tray(app_handle)?;
} else {
info!("No locations found, spawning full view on startup.");
info!("No locations found, showing full view on startup.");
let _ = WindowManager::open_full_view(app_handle);
}
}
Expand Down
20 changes: 20 additions & 0 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,26 @@ pub async fn all_locations(instance_id: Id) -> Result<Vec<LocationInfo>, Error>
Ok(location_info)
}

/// Returns `true` if there is at least one visible (non-service) location across all instances.
/// Shares the same visibility filter as [`all_locations`] (`include_service_locations = false`).
#[tauri::command(async)]
pub async fn has_any_visible_locations() -> Result<bool, Error> {
trace!("Checking whether any visible locations exist.");
let instances = Instance::all(&*DB_POOL).await?;
for instance in &instances {
let locations = Location::find_by_instance_id(&*DB_POOL, instance.id, false).await?;
if !locations.is_empty() {
trace!(
"Found at least one visible location in instance {}.",
instance.name
);
return Ok(true);
}
}
trace!("No visible locations found.");
Ok(false)
}

#[derive(Serialize, Debug)]
pub struct LocationInterfaceDetails {
pub location_id: Id,
Expand Down
Loading
Loading