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
2 changes: 1 addition & 1 deletion .github/workflows/build-nym-vpn-app-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24

- name: Install Protoc
uses: arduino/setup-protoc@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-nym-vpn-app-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24

- name: Install Protoc
uses: arduino/setup-protoc@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-nym-vpn-app-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
cache-dependency-path: nym-vpn-app/package-lock.json

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-nym-vpn-app-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
cache-dependency-path: nym-vpn-app/package-lock.json

Expand Down
1 change: 1 addition & 0 deletions nym-vpn-app/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fix a race condition at app startup that could lead to
selected nodes to be reset to default
- Fix incorrect JS kv store API types

## [1.17.1] - 2025-10-20

Expand Down
1,495 changes: 738 additions & 757 deletions nym-vpn-app/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions nym-vpn-app/src/contexts/node-list-state/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as NodeListStateProvider } from './provider';
export { useNodeListState } from './context';
export * from './types';
2 changes: 1 addition & 1 deletion nym-vpn-app/src/i18n/fr/node-location.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"offline": "Hors ligne"
},
"notes": {
"anti-censorship": "Activez le « protocole QUIC» dans les paramètres de lutte contre la censure pour utiliser cette fonctionnalité",
"anti-censorship": "<0>Activez le « protocole QUIC»</0> dans les paramètres de lutte contre la censure pour utiliser cette fonctionnalité",
"performance_with_time": "Score de performance calculé à partir de la charge du serveur et du temps de disponibilité. Temps de disponibilité des dernières 24 heures. Dernière mise à jour {{relativeTime}}.",
"performance": "Score de performance calculé à partir de la charge du serveur et de la durée de disponibilité comme moyenne des dernières 24 heures."
},
Expand Down
24 changes: 16 additions & 8 deletions nym-vpn-app/src/kvStore/kv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { DbKey } from '../types';
* @param k - Key
* @returns The value for that key if any
*/
export async function kvGet<V>(k: DbKey): Promise<V | undefined> {
export async function kvGet<V>(k: DbKey): Promise<V | null> {
try {
return await invoke<V>('db_get', { key: k });
} catch {}
} catch {
return null;
}
}

/**
Expand All @@ -20,10 +22,12 @@ export async function kvGet<V>(k: DbKey): Promise<V | undefined> {
* @param v - Value
* @returns The last value if it was set
*/
export async function kvSet<V>(k: DbKey, v: V): Promise<V | undefined> {
export async function kvSet<V>(k: DbKey, v: V): Promise<V | null> {
try {
return await invoke<V>('db_set', { key: k, value: v });
} catch {}
} catch {
return null;
}
}

/**
Expand All @@ -32,10 +36,12 @@ export async function kvSet<V>(k: DbKey, v: V): Promise<V | undefined> {
* @param k - Key
* @returns The previous value if any
*/
export async function kvDel<V>(k: DbKey): Promise<V | undefined> {
export async function kvDel<V>(k: DbKey): Promise<V | null> {
try {
return await invoke<V>('db_del', { key: k });
} catch {}
} catch {
return null;
}
}

/**
Expand All @@ -45,8 +51,10 @@ export async function kvDel<V>(k: DbKey): Promise<V | undefined> {
*
* @returns The number of bytes flushed during this call
*/
export async function kvFlush(): Promise<number | undefined> {
export async function kvFlush(): Promise<number | null> {
try {
return await invoke<number>('db_flush');
} catch {}
} catch {
return null;
}
}
2 changes: 1 addition & 1 deletion nym-vpn-app/src/screens/home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function Home() {
dispatch({ type: 'reset-error' });
dispatch({ type: 'connect' });
let savedQuic = await kvGet<boolean>('quic-enabled');
if (savedQuic === undefined) {
if (savedQuic === null) {
savedQuic = defaultQuic;
}
invoke('connect', {
Expand Down
19 changes: 16 additions & 3 deletions nym-vpn-app/src/screens/node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@ function Node({ node }: { node: NodeHop }) {

const { isOpen, close } = useDialog();
const { loading, error } = useNodeList();
const { setFocused, reset: resetSaved, addToExpanded } = useNodeListState();
const {
setFocused,
exit: exitNodeList,
entry: entryNodeList,
reset: resetSaved,
addToExpanded,
} = useNodeListState();
const expanded =
node === 'entry' ? entryNodeList.expanded : exitNodeList.expanded;
const focused =
node === 'entry' ? entryNodeList.focused : exitNodeList.focused;

const { tE } = useI18nError();

const quicFilter =
Expand All @@ -37,7 +48,7 @@ function Node({ node }: { node: NodeHop }) {

const { filter, nodes, gateways } = useFilterList();
const deferredNodes = useDeferredValue(nodes);
const deferredGws = useDeferredValue(gateways);
const deferredGateways = useDeferredValue(gateways);

const handleSelect = async (selected: SelectedUiNode) => {
const selectedNode = uiNodeToSelectedNode(selected);
Expand Down Expand Up @@ -151,11 +162,13 @@ function Node({ node }: { node: NodeHop }) {
{!loading && (
<NodeList
nodes={deferredNodes}
gateways={deferredGws}
gateways={deferredGateways}
onSelect={handleSelect}
onNodeDetails={handleNodeDetails}
hop={node}
vpnMode={vpnMode}
expanded={expanded}
focused={focused}
/>
)}
</PageAnim>
Expand Down
17 changes: 9 additions & 8 deletions nym-vpn-app/src/screens/node/list/NodeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Accordion } from '@base-ui-components/react';
import { useTranslation } from 'react-i18next';
import { motion } from 'motion/react';
import {
Focused,
SelectedKind,
SelectedUiNode,
UiCountry,
Expand All @@ -24,6 +25,8 @@ export type NodeListProps = {
onNodeDetails: (node: UiGateway) => void;
hop: NodeHop;
vpnMode: VpnMode;
expanded: string[];
focused: Focused | null;
};

const NodeList = memo(function NodeList({
Expand All @@ -33,17 +36,13 @@ const NodeList = memo(function NodeList({
hop,
vpnMode,
onNodeDetails,
expanded,
focused,
}: NodeListProps) {
const { backendFlags, quic } = useMainState();
const {
exit: exitState,
entry: entryState,
setExpanded,
} = useNodeListState();
const { setExpanded } = useNodeListState();
const { t } = useTranslation('nodeLocation');

const expanded = hop === 'entry' ? entryState.expanded : exitState.expanded;
const focused = hop === 'entry' ? entryState.focused : exitState.focused;
const countriesRef = useRef<Map<string, HTMLDivElement>>(null);
const regionsRef = useRef<Map<string, HTMLDivElement>>(null);
const gatewaysRef = useRef<Map<string, HTMLDivElement>>(null);
Expand Down Expand Up @@ -154,7 +153,7 @@ const NodeList = memo(function NodeList({
data-testid="node-list-accordion"
value={expanded}
onValueChange={onValueChange}
openMultiple
multiple
>
{nodes.map(({ i18n, isSelected, gateways, country, regions }) => (
<Accordion.Item
Expand Down Expand Up @@ -279,6 +278,8 @@ function arePropsEqual(
if (oldProps.vpnMode !== newProps.vpnMode) return false;
if (oldProps.gateways.length !== newProps.gateways.length) return false;
if (oldProps.nodes.length !== newProps.nodes.length) return false;
if (!dequal(oldProps.expanded, newProps.expanded)) return false;
if (!dequal(oldProps.focused, newProps.focused)) return false;
if (!dequal(oldProps.gateways, newProps.gateways)) return false;
if (!dequal(oldProps.nodes, newProps.nodes)) return false;
return true;
Expand Down
29 changes: 16 additions & 13 deletions nym-vpn-app/src/state/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { updateAccountState, updateTunnel } from './update';
import { TauriReq, fireRequests } from './helper';

const defaultNetStats = window._APP.defaultNetstats;
const defaultQuic = window._APP.defaultQuic;
const defaultDomFront = window._APP.defaultDomainFronting;

Expand Down Expand Up @@ -107,9 +108,7 @@ export async function initFirstBatch(
},
};

const getDesktopNotificationsRq: TauriReq<
() => Promise<boolean | undefined>
> = {
const getDesktopNotificationsRq: TauriReq<() => Promise<boolean | null>> = {
name: 'getDesktopNotificationsRq',
request: () => kvGet<boolean>('desktop-notifications'),
onFulfilled: (enabled) => {
Expand All @@ -120,7 +119,7 @@ export async function initFirstBatch(
},
};

const getRootFontSizeRq: TauriReq<() => Promise<number | undefined>> = {
const getRootFontSizeRq: TauriReq<() => Promise<number | null>> = {
name: 'getRootFontSize',
request: () => kvGet<number>('ui-root-font-size'),
onFulfilled: (size) => {
Expand Down Expand Up @@ -165,7 +164,7 @@ export async function initFirstBatch(
},
};

const getIpv6SupportRq: TauriReq<() => Promise<boolean | undefined>> = {
const getIpv6SupportRq: TauriReq<() => Promise<boolean | null>> = {
name: 'getIpv6Support',
request: () => kvGet<boolean>('disable-ipv6'),
onFulfilled: (disabled) => {
Expand All @@ -175,32 +174,36 @@ export async function initFirstBatch(
},
};

const getQuicRq: TauriReq<() => Promise<boolean | undefined>> = {
const getQuicRq: TauriReq<() => Promise<boolean | null>> = {
name: 'getQuicRq',
request: () => kvGet<boolean>('quic-enabled'),
onFulfilled: (enabled) => {
dispatch({ type: 'set-quic', enabled: enabled || defaultQuic });
dispatch({
type: 'set-quic',
enabled: enabled !== null ? enabled : defaultQuic,
});
},
};

const getDomainFrontingRq: TauriReq<() => Promise<boolean | undefined>> = {
const getDomainFrontingRq: TauriReq<() => Promise<boolean | null>> = {
name: 'getDomainFrontingRq',
request: () => kvGet<boolean>('domain-fronting-enabled'),
onFulfilled: (enabled) => {
dispatch({
type: 'set-domain-fronting',
enabled: enabled || defaultDomFront,
enabled: enabled !== null ? enabled : defaultDomFront,
});
},
};

const getNetworkStatsRq: TauriReq<() => Promise<boolean | undefined>> = {
const getNetworkStatsRq: TauriReq<() => Promise<boolean | null>> = {
name: 'getNetworkStats',
request: () => kvGet<boolean>('network-stats-enabled'),
onFulfilled: (enabled) => {
if (enabled !== undefined) {
dispatch({ type: 'set-network-stats', enabled });
}
dispatch({
type: 'set-network-stats',
enabled: enabled !== null ? enabled : defaultNetStats,
});
},
};

Expand Down
Loading