diff --git a/nym-vpn-desktop/src-tauri/Cargo.toml b/nym-vpn-desktop/src-tauri/Cargo.toml index d8e1ceda46..7fe99c6764 100644 --- a/nym-vpn-desktop/src-tauri/Cargo.toml +++ b/nym-vpn-desktop/src-tauri/Cargo.toml @@ -12,7 +12,7 @@ tauri-build = { version = "1.5", features = [] } build-info-build = "0.0.34" [dependencies] -tauri = { version = "1.6.0", features = [ "os-all", "updater", "process-all", "shell-open"] } +tauri = { version = "1.6.0", features = [ "window-set-size", "os-all", "updater", "process-all", "shell-open"] } tokio = { version = "1.33", features = ["rt", "sync", "time", "fs"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/nym-vpn-desktop/src-tauri/src/db.rs b/nym-vpn-desktop/src-tauri/src/db.rs index 84ad903a3b..ffe4ed0c65 100644 --- a/nym-vpn-desktop/src-tauri/src/db.rs +++ b/nym-vpn-desktop/src-tauri/src/db.rs @@ -40,6 +40,8 @@ pub enum Key { EntryNodeLocation, #[strum(serialize = "exit_node_location")] ExitNodeLocation, + #[strum(serialize = "window_size")] + WindowSize, } impl Display for Key { diff --git a/nym-vpn-desktop/src-tauri/tauri.conf.json b/nym-vpn-desktop/src-tauri/tauri.conf.json index b46e4b9876..4467177f14 100644 --- a/nym-vpn-desktop/src-tauri/tauri.conf.json +++ b/nym-vpn-desktop/src-tauri/tauri.conf.json @@ -19,7 +19,8 @@ "all": false, "process": { "all": true }, "shell": { "all": false, "open": true }, - "os": { "all": true } + "os": { "all": true }, + "window": { "setSize": true } }, "bundle": { "active": true, diff --git a/nym-vpn-desktop/src/state/init.ts b/nym-vpn-desktop/src/state/init.ts index c39ef5586f..1596e02188 100644 --- a/nym-vpn-desktop/src/state/init.ts +++ b/nym-vpn-desktop/src/state/init.ts @@ -4,6 +4,7 @@ import { appWindow } from '@tauri-apps/api/window'; import { DefaultRootFontSize, DefaultThemeMode } from '../constants'; import { getJsLicenses, getRustLicenses } from '../data'; import { kvGet } from '../kvStore'; +import logu from '../log'; import { CodeDependency, ConnectionState, @@ -13,6 +14,7 @@ import { ThemeMode, UiTheme, VpnMode, + WindowSize, } from '../types'; import fireRequests, { TauriReq } from './helper'; @@ -206,6 +208,18 @@ async function init(dispatch: StateDispatch) { }, }; + const getWindowSizeRq: TauriReq<() => Promise> = { + name: 'getWindowSize', + request: () => kvGet('WindowSize'), + onFulfilled: (size) => { + if (size) { + appWindow.setSize(size); + dispatch({ type: 'set-window-size', size }); + logu.debug(`restored window size to ${size.width}x${size.height}`); + } + }, + }; + const getDepsRustRq: TauriReq<() => Promise> = { name: 'getDepsRustRq', request: () => getRustLicenses(), @@ -245,6 +259,7 @@ async function init(dispatch: StateDispatch) { getMonitoringRq, getDepsRustRq, getDepsJsRq, + getWindowSizeRq, ]); } diff --git a/nym-vpn-desktop/src/state/main.ts b/nym-vpn-desktop/src/state/main.ts index 612d94f36c..a87e11e7cf 100644 --- a/nym-vpn-desktop/src/state/main.ts +++ b/nym-vpn-desktop/src/state/main.ts @@ -15,6 +15,7 @@ import { ThemeMode, UiTheme, VpnMode, + WindowSize, } from '../types'; export type StateAction = @@ -48,7 +49,8 @@ export type StateAction = | { type: 'set-fastest-node-location'; country: Country } | { type: 'set-root-font-size'; size: number } | { type: 'set-code-deps-js'; dependencies: CodeDependency[] } - | { type: 'set-code-deps-rust'; dependencies: CodeDependency[] }; + | { type: 'set-code-deps-rust'; dependencies: CodeDependency[] } + | { type: 'set-window-size'; size: WindowSize }; export const initialState: AppState = { initialized: false, @@ -226,6 +228,11 @@ export function reducer(state: AppState, action: StateAction): AppState { ...state, codeDepsRust: action.dependencies, }; + case 'set-window-size': + return { + ...state, + windowSize: action.size, + }; case 'reset': return initialState; diff --git a/nym-vpn-desktop/src/state/provider.tsx b/nym-vpn-desktop/src/state/provider.tsx index ee5a7c86f9..149d287411 100644 --- a/nym-vpn-desktop/src/state/provider.tsx +++ b/nym-vpn-desktop/src/state/provider.tsx @@ -14,7 +14,7 @@ type Props = { export function MainStateProvider({ children }: Props) { const [state, dispatch] = useReducer(reducer, initialState); - useTauriEvents(dispatch); + useTauriEvents(dispatch, state); // initialize app state useEffect(() => { diff --git a/nym-vpn-desktop/src/state/useTauriEvents.ts b/nym-vpn-desktop/src/state/useTauriEvents.ts index 1d7a4a3db0..e7c50bc3e2 100644 --- a/nym-vpn-desktop/src/state/useTauriEvents.ts +++ b/nym-vpn-desktop/src/state/useTauriEvents.ts @@ -1,11 +1,15 @@ +import * as _ from 'lodash-es'; import { useCallback, useEffect } from 'react'; -import { listen } from '@tauri-apps/api/event'; -import { appWindow } from '@tauri-apps/api/window'; +import { EventCallback, listen } from '@tauri-apps/api/event'; +import { PhysicalSize, appWindow } from '@tauri-apps/api/window'; import dayjs from 'dayjs'; +import { kvSet } from '../kvStore'; import { + AppState, ConnectionEventPayload, ProgressEventPayload, StateDispatch, + WindowSize, } from '../types'; import { ConnectionEvent, ProgressEvent } from '../constants'; @@ -18,7 +22,7 @@ function handleError(dispatch: StateDispatch, error?: string | null) { dispatch({ type: 'set-error', error }); } -export function useTauriEvents(dispatch: StateDispatch) { +export function useTauriEvents(dispatch: StateDispatch, state: AppState) { const registerStateListener = useCallback(() => { return listen(ConnectionEvent, (event) => { console.log( @@ -74,20 +78,44 @@ export function useTauriEvents(dispatch: StateDispatch) { }); }, [dispatch]); + const registerWindowResizedListener = useCallback(() => { + return appWindow.onResized( + _.debounce>( + ({ payload }) => { + if ( + payload.width !== state.windowSize?.width || + payload.height !== state.windowSize?.height + ) { + kvSet('WindowSize', payload); + dispatch({ type: 'set-window-size', size: payload }); + } + }, + 200, + { + leading: false, + trailing: true, + }, + ), + ); + }, [dispatch, state.windowSize]); + // register/unregister event listener useEffect(() => { const unlistenState = registerStateListener(); const unlistenProgress = registerProgressListener(); const unlistenThemeChanges = registerThemeChangedListener(); + const unlistenWindowResized = registerWindowResizedListener(); return () => { unlistenState.then((f) => f()); unlistenProgress.then((f) => f()); unlistenThemeChanges.then((f) => f()); + unlistenWindowResized.then((f) => f()); }; }, [ registerStateListener, registerProgressListener, registerThemeChangedListener, + registerWindowResizedListener, ]); } diff --git a/nym-vpn-desktop/src/types/app-state.ts b/nym-vpn-desktop/src/types/app-state.ts index dfdca35027..09420e3f87 100644 --- a/nym-vpn-desktop/src/types/app-state.ts +++ b/nym-vpn-desktop/src/types/app-state.ts @@ -1,3 +1,4 @@ +import { PhysicalSize } from '@tauri-apps/api/window'; import { Dispatch } from 'react'; import { Dayjs } from 'dayjs'; import { StateAction } from '../state'; @@ -27,6 +28,8 @@ export type CodeDependency = { copyright?: string; }; +export type WindowSize = PhysicalSize; + export type AppState = { // initial loading phase when the app is starting and fetching data from the backend initialized: boolean; @@ -54,6 +57,7 @@ export type AppState = { rootFontSize: number; codeDepsJs: CodeDependency[]; codeDepsRust: CodeDependency[]; + windowSize?: WindowSize | null; }; export type ConnectionEventPayload = { diff --git a/nym-vpn-desktop/src/types/tauri-ipc.ts b/nym-vpn-desktop/src/types/tauri-ipc.ts index 287b8d9cea..02fd95f6e8 100644 --- a/nym-vpn-desktop/src/types/tauri-ipc.ts +++ b/nym-vpn-desktop/src/types/tauri-ipc.ts @@ -17,4 +17,5 @@ export type DbKey = | 'UiRootFontSize' | 'VpnMode' | 'EntryNodeLocation' - | 'ExitNodeLocation'; + | 'ExitNodeLocation' + | 'WindowSize';