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
1 change: 1 addition & 0 deletions scripts/build_cgo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ msg_info "▶ Generating UI index"
./ui_index.gen.sh

msg_info "▶ Building native library"
git config --global --add safe.directory "$BUILD_DIR/_deps/lvgl-src"
VERBOSE=1 cmake -B "${BUILD_DIR}" \
-DCMAKE_SYSTEM_PROCESSOR=armv7l \
-DCMAKE_SYSTEM_NAME=Linux \
Expand Down
21 changes: 11 additions & 10 deletions ui/package-lock.json

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

2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"tslog": "^4.10.2",
"usehooks-ts": "^3.1.1",
"validator": "^13.15.23",
"zustand": "^4.5.2"
"zustand": "^5.0.12"
},
"devDependencies": {
"@inlang/cli": "^3.0.12",
Expand Down
7 changes: 4 additions & 3 deletions ui/src/hooks/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,9 @@ export interface NetworkState {
ipv6_gateway?: string;
dhcp_lease?: DhcpLease;
hostname?: string;
}

interface NetworkStateStore extends NetworkState {
setNetworkState: (state: NetworkState) => void;
setDhcpLease: (lease: NetworkState["dhcp_lease"]) => void;
setDhcpLeaseExpiry: (expiry: Date) => void;
Expand Down Expand Up @@ -881,7 +883,7 @@ export interface NetworkSettings {
time_sync_http_urls?: string[];
}

export const useNetworkStateStore = create<NetworkState>((set, get) => ({
export const useNetworkStateStore = create<NetworkStateStore>((set, get) => ({
setNetworkState: (state: NetworkState) => set(state),
setDhcpLease: (lease: NetworkState["dhcp_lease"]) => set({ dhcp_lease: lease }),
setDhcpLeaseExpiry: (expiry: Date) => {
Expand All @@ -891,8 +893,7 @@ export const useNetworkStateStore = create<NetworkState>((set, get) => ({
return;
}

lease.lease_expiry = expiry;
set({ dhcp_lease: lease });
set({ dhcp_lease: { ...lease, lease_expiry: expiry } });
},
}));

Expand Down
10 changes: 5 additions & 5 deletions ui/src/routes/devices.$id.settings.general._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from "react";

import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
import { useShallow } from "zustand/shallow";
import { useDeviceStore } from "@hooks/stores";
import { Button } from "@components/Button";
import Checkbox from "@components/Checkbox";
Expand All @@ -17,11 +18,10 @@ export default function SettingsGeneralRoute() {
const { send } = useJsonRpc();
const { navigateTo } = useDeviceUiNavigation();
const [autoUpdate, setAutoUpdate] = useState(true);
const currentVersions = useDeviceStore(state => {
const { appVersion, systemVersion } = state;
if (!appVersion || !systemVersion) return null;
return { appVersion, systemVersion };
});
const { appVersion, systemVersion } = useDeviceStore(
useShallow(state => ({ appVersion: state.appVersion, systemVersion: state.systemVersion })),
);
const currentVersions = appVersion && systemVersion ? { appVersion, systemVersion } : null;

useEffect(() => {
send("getAutoUpdateState", {}, (resp: JsonRpcResponse) => {
Expand Down
54 changes: 25 additions & 29 deletions ui/src/routes/devices.$id.settings.network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import validator from "validator";

import PublicIPCard from "@components/PublicIPCard";
import TailscaleCard from "@components/TailscaleCard";
import { NetworkSettings, NetworkState, useNetworkStateStore, useRTCStore } from "@hooks/stores";
import { NetworkSettings, NetworkState, useNetworkStateStore } from "@hooks/stores";
import { useShallow } from "zustand/shallow";
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import AutoHeight from "@components/AutoHeight";
import { Button } from "@components/Button";
Expand All @@ -33,32 +34,13 @@ dayjs.extend(relativeTime);

const isLLDPAvailable = false; // LLDP is not supported yet

const resolveOnRtcReady = () => {
return new Promise(resolve => {
// Check if RTC is already connected
const currentState = useRTCStore.getState();
if (currentState.rpcDataChannel?.readyState === "open") {
// Already connected, fetch data immediately
return resolve(void 0);
}

// Not connected yet, subscribe to state changes
const unsubscribe = useRTCStore.subscribe(state => {
if (state.rpcDataChannel?.readyState === "open") {
unsubscribe(); // Clean up subscription
return resolve(void 0);
}
});
});
};

export function LifeTimeLabel({ lifetime }: Readonly<{ lifetime: string }>) {
const [remaining, setRemaining] = useState<string | null>(null);

// rrecalculate remaining time every 30 seconds
// recalculate remaining time every 30 seconds
useEffect(() => {
// schedule immediate initial update
setInterval(() => setRemaining(dayjs(lifetime).fromNow()), 0);
// immediate initial update
setRemaining(dayjs(lifetime).fromNow());

const interval = setInterval(() => {
setRemaining(dayjs(lifetime).fromNow());
Expand All @@ -85,7 +67,16 @@ const NonCustomDomainOptions = ["dhcp", "local"];
export default function SettingsNetworkRoute() {
const { send } = useJsonRpc();

const networkState = useNetworkStateStore(state => state);
const networkState = useNetworkStateStore(
useShallow(state => ({
mac_address: state.mac_address,
hostname: state.hostname,
dhcp_lease: state.dhcp_lease,
ipv6_addresses: state.ipv6_addresses,
ipv6_link_local: state.ipv6_link_local,
ipv6_gateway: state.ipv6_gateway,
})),
);
const setNetworkState = useNetworkStateStore(state => state.setNetworkState);

// Some input needs direct state management. Mostly options that open more details
Expand Down Expand Up @@ -164,8 +155,6 @@ export default function SettingsNetworkRoute() {
mode: "onBlur",

defaultValues: async () => {
// Ensure data channel is ready, before fetching network data from the device
await resolveOnRtcReady();
const { settings } = await fetchNetworkData();
return settings;
},
Expand Down Expand Up @@ -215,7 +204,6 @@ export default function SettingsNetworkRoute() {
} catch (error) {
console.error("Failed to fetch network data:", error);
}
notifications.success(m.network_dhcp_lease_renew_success());
}
});
},
Expand Down Expand Up @@ -270,7 +258,11 @@ export default function SettingsNetworkRoute() {
});
}

if (dirty.ipv4_static?.dns && dirty.ipv4_static.dns.length > 0 && dirty.ipv4_static.dns.every(dirty => dirty)) {
if (
dirty.ipv4_static?.dns &&
dirty.ipv4_static.dns.length > 0 &&
dirty.ipv4_static.dns.every(dirty => dirty)
) {
changes.push({
label: m.network_ipv4_dns(),
from: initialSettingsRef.current?.ipv4_static?.dns.join(", ").toString() ?? "",
Expand Down Expand Up @@ -302,7 +294,11 @@ export default function SettingsNetworkRoute() {
});
}

if (dirty.ipv6_static?.dns && dirty.ipv6_static.dns.length > 0 && dirty.ipv6_static.dns.every(dirty => dirty)) {
if (
dirty.ipv6_static?.dns &&
dirty.ipv6_static.dns.length > 0 &&
dirty.ipv6_static.dns.every(dirty => dirty)
) {
changes.push({
label: m.network_ipv6_dns(),
from: initialSettingsRef.current?.ipv6_static?.dns.join(", ").toString() ?? "",
Expand Down
Loading