frontend/smart-hut/src/store.js

247 lines
6.8 KiB
JavaScript
Raw Normal View History

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import update from "immutability-helper";
2020-04-07 10:46:55 +00:00
function reducer(previousState, action) {
let newState;
2020-04-07 10:46:55 +00:00
const createOrUpdateRoom = (room) => {
2020-04-10 15:25:52 +00:00
if (!newState.rooms[room.id]) {
newState = update(newState, {
2020-04-10 15:25:52 +00:00
rooms: { [room.id]: { $set: { ...room, devices: new Set() } } },
});
2020-04-07 10:46:55 +00:00
} else {
newState = update(newState, {
rooms: {
[room.id]: {
name: { $set: room.name },
image: { $set: room.image },
icon: { $set: room.icon },
},
},
});
2020-04-07 10:46:55 +00:00
}
2020-04-10 15:52:02 +00:00
if (newState.pendingJoins.rooms[room.id]) {
newState = update(newState, {
pendingJoins: { rooms: { $unset: [room.id] } },
rooms: {
[room.id]: {
devices: {
$add: [...newState.pendingJoins.rooms[room.id]],
},
},
},
});
}
2020-04-07 10:46:55 +00:00
};
let change;
2020-04-07 10:46:55 +00:00
switch (action.type) {
case "LOGIN_UPDATE":
newState = update(previousState, { login: { $set: action.login } });
2020-04-07 10:46:55 +00:00
break;
case "USER_INFO_UPDATE":
newState = update(previousState, { userInfo: { $set: action.user } });
2020-04-07 10:46:55 +00:00
break;
case "ROOMS_UPDATE":
newState = previousState;
2020-04-07 10:46:55 +00:00
for (const room of action.rooms) {
createOrUpdateRoom(room);
}
break;
case "DEVICES_UPDATE":
change = null;
2020-04-07 10:46:55 +00:00
// 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: [] },
};
2020-04-07 10:46:55 +00:00
const room = newState.rooms[action.roomId];
for (const deviceId of room.devices) {
change.devices.$unset.push(deviceId);
2020-04-07 10:46:55 +00:00
}
} 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: {},
};
2020-04-07 10:46:55 +00:00
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);
2020-04-07 10:46:55 +00:00
}
} else {
// otherwise, just delete all devices and all joins
// between rooms and devices
change = {
devices: { $set: {} },
rooms: {},
};
2020-04-10 15:25:52 +00:00
for (const room of Object.values(previousState.rooms)) {
if (change.rooms[room.id]) {
change.rooms[room.id].devices = { $set: new Set() };
}
2020-04-07 10:46:55 +00:00
}
}
newState = update(previousState, change);
2020-04-07 10:46:55 +00:00
change = {
devices: {},
rooms: {},
2020-04-10 15:52:02 +00:00
pendingJoins: { rooms: {} },
};
2020-04-07 10:46:55 +00:00
for (const device of action.devices) {
change.devices[device.id] = { $set: device };
2020-04-07 10:46:55 +00:00
if (device.roomId in newState.rooms) {
change.rooms[device.roomId] = change.rooms[device.roomId] || {};
change.rooms[device.roomId].devices =
change.rooms[device.roomId].devices || {};
const devices = change.rooms[device.roomId].devices;
devices.$add = devices.$add || [];
devices.$add.push(device.id);
2020-04-07 10:46:55 +00:00
} else {
2020-04-10 15:52:02 +00:00
// room does not exist yet, so add to the list of pending
// joins
if (!change.pendingJoins.rooms[device.roomId]) {
change.pendingJoins.rooms[device.roomId] = {
$set: new Set([device.id]),
};
} else {
change.pendingJoins.rooms[device.roomId].$set.add(device.id);
}
2020-04-07 10:46:55 +00:00
}
}
newState = update(newState, change);
2020-04-07 10:46:55 +00:00
break;
case "ROOM_SAVE":
2020-04-11 11:57:03 +00:00
newState = previousState;
2020-04-07 10:46:55 +00:00
createOrUpdateRoom(action.room);
break;
2020-04-10 15:25:52 +00:00
case "DEVICE_SAVE":
newState = update(previousState, {
devices: { [action.device.id]: { $set: action.device } },
});
break;
2020-04-07 10:46:55 +00:00
case "ROOM_DELETE":
2020-04-10 15:25:52 +00:00
if (!(action.roomId in previousState.rooms)) {
console.warn(`Room to delete ${action.roomId} does not exist`);
2020-04-07 10:46:55 +00:00
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);
2020-04-07 10:46:55 +00:00
}
2020-04-10 15:25:52 +00:00
change.rooms = { $unset: [action.roomId] };
2020-04-11 11:57:03 +00:00
if (previousState.active.activeRoom === action.roomId) {
change.active = { activeRoom: {$set: -1}};
}
newState = update(previousState, change);
2020-04-07 10:46:55 +00:00
break;
case "DEVICE_DELETE":
2020-04-10 15:25:52 +00:00
if (!(action.deviceId in previousState.devices)) {
console.warn(`Device to delete ${action.deviceId} does not exist`);
2020-04-07 10:46:55 +00:00
break;
}
2020-04-10 15:25:52 +00:00
change = {
devices: { $unset: [action.deviceId] },
};
if (previousState.rooms[previousState.devices[action.deviceId].roomId]) {
change.rooms = {
[previousState.devices[action.deviceId].roomId]: {
devices: { $remove: [action.deviceId] },
},
2020-04-10 15:25:52 +00:00
};
}
newState = update(previousState, change);
2020-04-07 10:46:55 +00:00
break;
case "LOGOUT":
2020-04-09 15:24:30 +00:00
newState = update(initState, {});
break;
case "SET_ACTIVE_ROOM":
newState = update(previousState, {
active: {
activeRoom: {
2020-04-10 15:25:52 +00:00
$set: action.activeRoom,
2020-04-09 15:24:30 +00:00
},
},
});
2020-04-07 10:46:55 +00:00
break;
default:
console.warn(`Action type ${action.type} unknown`, action);
return previousState;
2020-04-07 10:46:55 +00:00
}
console.log("new state: ", newState);
2020-04-07 10:46:55 +00:00
return newState;
}
2020-04-09 15:24:30 +00:00
const initState = {
errors: {},
2020-04-10 15:52:02 +00:00
pendingJoins: {
rooms: {},
},
2020-04-09 15:24:30 +00:00
active: {
activeRoom: -1,
},
login: {
loggedIn: false,
token: null,
},
userInfo: null,
/** @type {[integer]Room} */
rooms: {},
/** @type {[integer]Device} */
devices: {},
};
function createSmartHutStore() {
const token = localStorage.getItem("token");
const exp = localStorage.getItem("exp");
2020-04-09 15:24:30 +00:00
const initialState = update(initState, {
login: {
2020-04-09 15:24:30 +00:00
token: { $set: token },
loggedIn: { $set: !!(token && exp > new Date().getTime()) },
},
2020-04-09 15:24:30 +00:00
});
if (!initialState.login.loggedIn) {
localStorage.removeItem("token");
localStorage.removeItem("exp");
initialState.login.token = null;
}
return createStore(reducer, initialState, applyMiddleware(thunk));
}
2020-04-07 10:46:55 +00:00
const smartHutStore = createSmartHutStore();
2020-04-07 10:46:55 +00:00
export default smartHutStore;