New channel config (#807)
* Channel config rework Add staged channel config with tabbed UI, import/export workflow, and global form state refactor * Improve import dialog config comparison and UI labels * Review fixes * Improve state handling * Fix default filter behaviour --------- Co-authored-by: philon- <philon-@users.noreply.github.com>
This commit is contained in:
co-authored by
philon-
parent
01fa030ef9
commit
1946000d14
@@ -8,11 +8,6 @@ export interface RasterSource {
|
||||
tileSize: number;
|
||||
}
|
||||
|
||||
interface ErrorState {
|
||||
field: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface AppState {
|
||||
selectedDeviceId: number;
|
||||
devices: {
|
||||
@@ -24,7 +19,6 @@ interface AppState {
|
||||
nodeNumToBeRemoved: number;
|
||||
connectDialogOpen: boolean;
|
||||
nodeNumDetails: number;
|
||||
errors: ErrorState[];
|
||||
|
||||
setRasterSources: (sources: RasterSource[]) => void;
|
||||
addRasterSource: (source: RasterSource) => void;
|
||||
@@ -36,15 +30,6 @@ interface AppState {
|
||||
setNodeNumToBeRemoved: (nodeNum: number) => void;
|
||||
setConnectDialogOpen: (open: boolean) => void;
|
||||
setNodeNumDetails: (nodeNum: number) => void;
|
||||
|
||||
// Error management
|
||||
hasErrors: () => boolean;
|
||||
getErrorMessage: (field: string) => string | undefined;
|
||||
hasFieldError: (field: string) => boolean;
|
||||
addError: (field: string, message: string) => void;
|
||||
removeError: (field: string) => void;
|
||||
clearErrors: () => void;
|
||||
setNewErrors: (newErrors: ErrorState[]) => void;
|
||||
}
|
||||
|
||||
export const useAppStore = create<AppState>()((set, get) => ({
|
||||
@@ -56,7 +41,6 @@ export const useAppStore = create<AppState>()((set, get) => ({
|
||||
connectDialogOpen: false,
|
||||
nodeNumToBeRemoved: 0,
|
||||
nodeNumDetails: 0,
|
||||
errors: [],
|
||||
|
||||
setRasterSources: (sources: RasterSource[]) => {
|
||||
set(
|
||||
@@ -114,47 +98,4 @@ export const useAppStore = create<AppState>()((set, get) => ({
|
||||
set(() => ({
|
||||
nodeNumDetails: nodeNum,
|
||||
})),
|
||||
hasErrors: () => {
|
||||
const state = get();
|
||||
return state.errors.length > 0;
|
||||
},
|
||||
getErrorMessage: (field: string) => {
|
||||
const state = get();
|
||||
return state.errors.find((err) => err.field === field)?.message;
|
||||
},
|
||||
hasFieldError: (field: string) => {
|
||||
const state = get();
|
||||
return state.errors.some((err) => err.field === field);
|
||||
},
|
||||
addError: (field: string, message: string) => {
|
||||
set(
|
||||
produce<AppState>((draft) => {
|
||||
draft.errors = [
|
||||
...draft.errors.filter((e) => e.field !== field),
|
||||
{ field, message },
|
||||
];
|
||||
}),
|
||||
);
|
||||
},
|
||||
removeError: (field: string) => {
|
||||
set(
|
||||
produce<AppState>((draft) => {
|
||||
draft.errors = draft.errors.filter((e) => e.field !== field);
|
||||
}),
|
||||
);
|
||||
},
|
||||
clearErrors: () => {
|
||||
set(
|
||||
produce<AppState>((draft) => {
|
||||
draft.errors = [];
|
||||
}),
|
||||
);
|
||||
},
|
||||
setNewErrors: (newErrors: ErrorState[]) => {
|
||||
set(
|
||||
produce<AppState>((draft) => {
|
||||
draft.errors = newErrors;
|
||||
}),
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -20,6 +20,7 @@ export const mockDeviceStore: Device = {
|
||||
moduleConfig: {} as Protobuf.LocalOnly.LocalModuleConfig,
|
||||
workingConfig: [],
|
||||
workingModuleConfig: [],
|
||||
workingChannelConfig: [],
|
||||
hardware: {} as Protobuf.Mesh.MyNodeInfo,
|
||||
metadata: new Map(),
|
||||
traceroutes: new Map(),
|
||||
@@ -57,6 +58,9 @@ export const mockDeviceStore: Device = {
|
||||
removeWorkingModuleConfig: vi.fn(),
|
||||
getEffectiveConfig: vi.fn(),
|
||||
getEffectiveModuleConfig: vi.fn(),
|
||||
setWorkingChannelConfig: vi.fn(),
|
||||
getWorkingChannelConfig: vi.fn(),
|
||||
removeWorkingChannelConfig: vi.fn(),
|
||||
setHardware: vi.fn(),
|
||||
setActiveNode: vi.fn(),
|
||||
setPendingSettingsChanges: vi.fn(),
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface Device {
|
||||
moduleConfig: Protobuf.LocalOnly.LocalModuleConfig;
|
||||
workingConfig: Protobuf.Config.Config[];
|
||||
workingModuleConfig: Protobuf.ModuleConfig.ModuleConfig[];
|
||||
workingChannelConfig: Protobuf.Channel.Channel[];
|
||||
hardware: Protobuf.Mesh.MyNodeInfo;
|
||||
metadata: Map<number, Protobuf.Mesh.DeviceMetadata>;
|
||||
traceroutes: Map<
|
||||
@@ -85,6 +86,11 @@ export interface Device {
|
||||
getEffectiveModuleConfig<K extends ValidModuleConfigType>(
|
||||
payloadVariant: K,
|
||||
): Protobuf.LocalOnly.LocalModuleConfig[K] | undefined;
|
||||
setWorkingChannelConfig: (channelNum: Protobuf.Channel.Channel) => void;
|
||||
getWorkingChannelConfig: (
|
||||
index: Types.ChannelNumber,
|
||||
) => Protobuf.Channel.Channel | undefined;
|
||||
removeWorkingChannelConfig: (channelNum?: Types.ChannelNumber) => void;
|
||||
setHardware: (hardware: Protobuf.Mesh.MyNodeInfo) => void;
|
||||
setActiveNode: (node: number) => void;
|
||||
setPendingSettingsChanges: (state: boolean) => void;
|
||||
@@ -139,6 +145,7 @@ export const useDeviceStore = createStore<PrivateDeviceState>((set, get) => ({
|
||||
moduleConfig: create(Protobuf.LocalOnly.LocalModuleConfigSchema),
|
||||
workingConfig: [],
|
||||
workingModuleConfig: [],
|
||||
workingChannelConfig: [],
|
||||
hardware: create(Protobuf.Mesh.MyNodeInfoSchema),
|
||||
metadata: new Map(),
|
||||
traceroutes: new Map(),
|
||||
@@ -443,6 +450,61 @@ export const useDeviceStore = createStore<PrivateDeviceState>((set, get) => ({
|
||||
};
|
||||
},
|
||||
|
||||
setWorkingChannelConfig: (config: Protobuf.Channel.Channel) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
const device = draft.devices.get(id);
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
const index = device.workingChannelConfig.findIndex(
|
||||
(wcc) => wcc.index === config.index,
|
||||
);
|
||||
|
||||
if (index !== -1) {
|
||||
device.workingChannelConfig[index] = config;
|
||||
} else {
|
||||
device.workingChannelConfig.push(config);
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
getWorkingChannelConfig: (channelNum: Types.ChannelNumber) => {
|
||||
const device = get().devices.get(id);
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workingChannelConfig = device.workingChannelConfig.find(
|
||||
(c) => c.index === channelNum,
|
||||
);
|
||||
|
||||
return workingChannelConfig;
|
||||
},
|
||||
removeWorkingChannelConfig: (channelNum?: Types.ChannelNumber) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
const device = draft.devices.get(id);
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelNum === undefined) {
|
||||
device.workingChannelConfig = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const index = device.workingChannelConfig.findIndex(
|
||||
(wcc: Protobuf.Channel.Channel) => wcc.index === channelNum,
|
||||
);
|
||||
|
||||
if (index !== -1) {
|
||||
device.workingChannelConfig.splice(index, 1);
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
setHardware: (hardware: Protobuf.Mesh.MyNodeInfo) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
|
||||
Reference in New Issue
Block a user