Farmbot-Web-App/frontend/resources/reducer.ts

226 lines
7.4 KiB
TypeScript

import { generateReducer } from "../redux/generate_reducer";
import { RestResources } from "./interfaces";
import {
indexUpsert,
mutateSpecialStatus,
findByUuid,
indexRemove,
initResourceReducer,
afterEach,
beforeEach,
folderIndexer,
reindexFolders,
} from "./reducer_support";
import { TaggedResource, SpecialStatus } from "farmbot";
import { Actions } from "../constants";
import { EditResourceParams } from "../api/interfaces";
import { defensiveClone, equals } from "../util";
import { merge } from "lodash";
import { SyncBodyContents } from "../sync/actions";
import { GeneralizedError } from "./actions";
import { initialState as helpState } from "../help/reducer";
import { initialState as designerState } from "../farm_designer/reducer";
import { farmwareState } from "../farmware/reducer";
import { initialState as regimenState } from "../regimens/reducer";
import { initialState as sequenceState } from "../sequences/reducer";
import { initialState as alertState } from "../messages/reducer";
import { ingest } from "../folders/data_transfer";
import { searchFolderTree } from "../folders/search_folder_tree";
export const emptyState = (): RestResources => {
return {
consumers: {
sequences: sequenceState,
regimens: regimenState,
farm_designer: designerState,
farmware: farmwareState,
help: helpState,
alerts: alertState
},
loaded: [],
index: {
all: {},
byKind: {
Alert: {},
Crop: {},
Device: {},
FarmEvent: {},
FarmwareEnv: {},
FarmwareInstallation: {},
FbosConfig: {},
FirmwareConfig: {},
Folder: {},
Image: {},
Log: {},
Peripheral: {},
PinBinding: {},
Plant: {},
PlantTemplate: {},
Point: {},
PointGroup: {},
Regimen: {},
SavedGarden: {},
Sensor: {},
SensorReading: {},
Sequence: {},
Tool: {},
User: {},
WebAppConfig: {},
WebcamFeed: {},
},
byKindAndId: {},
references: {},
sequenceMetas: {},
inUse: {
"Regimen.FarmEvent": {},
"Sequence.FarmEvent": {},
"Sequence.Regimen": {},
"Sequence.Sequence": {},
"Sequence.PinBinding": {},
"Sequence.FbosConfig": {}
},
sequenceFolders: {
localMetaAttributes: {},
folders: {
folders: [],
noFolder: [],
},
}
}
};
};
/** Responsible for all RESTful resources. */
export const resourceReducer =
generateReducer<RestResources>(emptyState())
.beforeEach(beforeEach)
.afterEach(afterEach)
.add<TaggedResource>(Actions.SAVE_RESOURCE_OK, (s, { payload }) => {
indexUpsert(s.index, [payload], "ongoing");
mutateSpecialStatus(payload.uuid, s.index, SpecialStatus.SAVED);
return s;
})
.add<EditResourceParams>(Actions.EDIT_RESOURCE, (s, { payload }) => {
const { update } = payload;
const target = findByUuid(s.index, payload.uuid);
const before = defensiveClone(target.body);
merge(target, { body: update });
const didChange = !equals(before, target.body);
if (didChange) {
mutateSpecialStatus(target.uuid, s.index, SpecialStatus.DIRTY);
indexUpsert(s.index, [target], "ongoing");
}
return s;
})
.add<EditResourceParams>(Actions.OVERWRITE_RESOURCE, (s, { payload }) => {
const { uuid, update, specialStatus } = payload;
const original = findByUuid(s.index, uuid);
original.body = update;
indexUpsert(s.index, [original], "ongoing");
mutateSpecialStatus(uuid, s.index, specialStatus);
return s;
})
.add<SyncBodyContents<TaggedResource>>(Actions.RESOURCE_READY, (s, { payload }) => {
!s.loaded.includes(payload.kind) && s.loaded.push(payload.kind);
indexUpsert(s.index, payload.body, "initial");
return s;
})
.add<TaggedResource>(Actions.REFRESH_RESOURCE_OK, (s, { payload }) => {
indexUpsert(s.index, [payload], "ongoing");
mutateSpecialStatus(payload.uuid, s.index);
return s;
})
.add<TaggedResource>(Actions.DESTROY_RESOURCE_OK, (s, { payload }) => {
indexRemove(s.index, payload);
folderIndexer(payload, s.index);
return s;
})
.add<GeneralizedError>(Actions._RESOURCE_NO, (s, { payload }) => {
merge(findByUuid(s.index, payload.uuid), payload);
mutateSpecialStatus(payload.uuid, s.index, payload.statusBeforeError);
return s;
})
.add<TaggedResource>(Actions.INIT_RESOURCE, initResourceReducer)
.add<string>(Actions.REFRESH_RESOURCE_START, (s, a) => {
mutateSpecialStatus(a.payload, s.index, SpecialStatus.SAVING);
return s;
})
.add<GeneralizedError>(Actions.REFRESH_RESOURCE_NO, (s, { payload }) => {
mutateSpecialStatus(payload.uuid, s.index);
return s;
})
.add<TaggedResource>(Actions.SAVE_RESOURCE_START, (s, { payload }) => {
mutateSpecialStatus(payload.uuid, s.index, SpecialStatus.SAVING);
return s;
})
.add<TaggedResource[]>(Actions.BATCH_INIT, (s, { payload }) => {
return payload.reduce((state, resource) => {
return initResourceReducer(state, {
type: Actions.INIT_RESOURCE,
payload: resource
});
}, s);
})
.add<{ id: number }>(Actions.FOLDER_TOGGLE, (s, { payload }) => {
const { localMetaAttributes } = s.index.sequenceFolders;
const record = localMetaAttributes[parseInt("" + payload.id)];
record.open = !(record.open ?? true);
reindexFolders(s.index);
return s;
})
.add<boolean>(Actions.FOLDER_TOGGLE_ALL, (s, { payload }) => {
const { localMetaAttributes } = s.index.sequenceFolders;
Object.keys(localMetaAttributes).map((x) => {
localMetaAttributes[parseInt("" + x)].open = payload;
});
reindexFolders(s.index);
return s;
})
.add<{ id: number }>(Actions.FOLDER_TOGGLE_EDIT, (s, { payload }) => {
const { localMetaAttributes } = s.index.sequenceFolders;
const record = localMetaAttributes[parseInt("" + payload.id)];
record.editing = !record.editing;
reindexFolders(s.index);
return s;
})
.add<string | undefined>(Actions.FOLDER_SEARCH, (s, { payload }) => {
s.index.sequenceFolders.searchTerm = payload;
if (payload) {
const folders = searchFolderTree({
references: s.index.references,
input: payload,
root: s.index.sequenceFolders.folders
});
const { localMetaAttributes } = s.index.sequenceFolders;
Object /** Expand all folders when searching. */
.keys(localMetaAttributes)
.map(x => {
s
.index
.sequenceFolders
.localMetaAttributes[x as unknown as number]
.open = true;
});
const nextFolder = ingest({
localMetaAttributes,
folders
});
nextFolder.noFolder = nextFolder.noFolder.filter(uuid => {
const sq = s.index.references[uuid];
if (sq && sq.kind === "Sequence") {
const n = sq.body.name.toLowerCase();
return n.includes(payload);
} else {
return false;
}
});
s.index.sequenceFolders.filteredFolders = nextFolder;
} else {
s.index.sequenceFolders.filteredFolders = undefined;
}
reindexFolders(s.index);
return s;
});