import { createStore, applyMiddleware } from "redux"; import thunk from "redux-thunk"; import actions from "./storeActions"; import update from "immutability-helper"; function reducer(previousState, action) { let newState; const createOrUpdateRoom = (room) => { if (!(room.id in newState.rooms)) { newState = update(newState, { rooms: { [room.id]: { devices: { $set: new Set() } } }, }); } else { newState = update(newState, { rooms: { [room.id]: { name: { $set: room.name }, image: { $set: room.image }, icon: { $set: room.icon }, }, }, }); } }; let change; switch (action.type) { case "LOGIN_UPDATE": newState = update(previousState, { login: { $set: action.login } }); break; case "USER_INFO_UPDATE": newState = update(previousState, { userInfo: { $set: action.user } }); break; case "ROOMS_UPDATE": newState = previousState; for (const room of action.rooms) { createOrUpdateRoom(room); } break; case "DEVICES_UPDATE": change = null; // if room is given, delete all devices in that room // and remove any join between that room and deleted // devices if (action.roomId) { change = { rooms: { [action.roomId]: { devices: { $set: new Set() } } }, devices: { $unset: [] }, }; const room = newState.rooms[action.roomId]; for (const deviceId of room.devices) { change.devices.$unset.push(deviceId); } } else if (action.partial) { // if the update is partial and caused by an operation on an input // device (like /switch/operate), iteratively remove deleted // devices and their join with their corresponding room. change = { devices: { $unset: [] }, rooms: {}, }; for (const device of action.devices) { const roomId = previousState.devices[device.id].roomId; change.rooms[roomId] = change.rooms[roomId] || { devices: { $remove: [] }, }; change.rooms[roomId].devices.$remove.push(device.id); change.devices.$unset.push(device.id); } } else { // otherwise, just delete all devices and all joins // between rooms and devices change = { devices: { $set: {} }, rooms: {}, }; for (const room of newState.rooms) { change.rooms[room.id].devices = { $set: new Set() }; } } newState = update(previousState, change); change = { devices: {}, rooms: {}, }; for (const device of action.devices) { change.devices[device.id] = { $set: device }; if (device.roomId in newState.rooms) { const devices = change.rooms[device.roomId].devices; devices.$add = devices.$add || []; devices.$add.push(device.id); } else { console.warn( "Cannot join device", device, `in room ${device.roomId} since that room has not been fetched` ); } } newState = update(newState, change); break; case "ROOM_SAVE": createOrUpdateRoom(action.room); break; case "ROOM_DELETE": if (!(actions.roomId in previousState.rooms)) { console.warn(`Room to delete ${actions.roomId} does not exist`); break; } // This update does not ensure the consistent update of switchId/dimmerId properties // on output devices connected to an input device in this room. Please manually request // all devices again if consistent update is desired change = { devices: { $unset: [] } }; for (const id of previousState.rooms[action.roomId].devices) { change.devices.$unset.push(id); } change.rooms = { $unset: actions.roomId }; newState = update(previousState, change); break; case "DEVICE_DELETE": if (!(actions.deviceId in previousState.devices)) { console.warn(`Device to delete ${actions.deviceId} does not exist`); break; } newState = update(previousState, { devices: { $unset: actions.deviceId }, rooms: { [previousState.devices[actions.deviceId].roomId]: { devices: { $remove: actions.deviceId }, }, }, }); break; case "LOGOUT": newState = { login: { loggedIn: false, token: null }, rooms: [], devices: [], userInfo: null, }; break; default: console.warn(`Action type ${action.type} unknown`, action); return previousState; } console.log("new state: ", newState); return newState; } function createSmartHutStore() { const token = localStorage.getItem("token"); const exp = localStorage.getItem("exp"); const initialState = { errors: {}, login: { token: token, }, userInfo: null, /** @type {[integer]Room} */ rooms: {}, /** @type {[integer]Device} */ devices: {}, }; initialState.login.loggedIn = !!(token && exp > new Date().getTime()); if (!initialState.login.loggedIn) { localStorage.removeItem("token"); localStorage.removeItem("exp"); initialState.login.token = null; } return createStore(reducer, initialState, applyMiddleware(thunk)); } const smartHutStore = createSmartHutStore(); export default smartHutStore;