frontend/farm_events/map_state_to_props.ts
import { Everything, TimeSettings } from "../interfaces";
import moment from "moment";
import { FarmEventProps } from "../farm_designer/interfaces";
import { joinFarmEventsToExecutable } from "./calendar/selectors";
import { Calendar } from "./calendar/index";
import { occurrence } from "./calendar/occurrence";
import {
findSequenceById,
maybeGetDevice,
maybeGetTimeSettings,
} from "../resources/selectors";
import { ResourceIndex } from "../resources/interfaces";
import {
FarmEventWithRegimen, FarmEventWithSequence,
} from "./calendar/interfaces";
import { scheduleForFarmEvent } from "./calendar/scheduler";
import { last } from "lodash";
import { RegimenItem } from "farmbot/dist/resources/api_resources";
/** Prepares a FarmEvent[] for use with <FBSelect /> */
export function mapStateToProps(state: Everything): FarmEventProps {
const timeSettings = maybeGetTimeSettings(state.resources.index);
const calendar =
mapResourcesToCalendar(state.resources.index, timeSettings, moment());
const calendarRows = calendar.getAll();
const timezoneIsSet = !!maybeGetDevice(state.resources.index)?.body.timezone;
return { calendarRows, timezoneIsSet };
}
export function mapResourcesToCalendar(
ri: ResourceIndex, timeSettings: TimeSettings, now = moment()): Calendar {
const x = joinFarmEventsToExecutable(ri);
const calendar = new Calendar();
const addRegimenToCalendar = regimenCalendarAdder(ri, timeSettings);
x.map(function (fe) {
switch (fe.executable_type) {
case "Regimen": return addRegimenToCalendar(fe, calendar, now);
case "Sequence":
return addSequenceToCalendar(fe, calendar, timeSettings, ri, now);
}
});
return calendar;
}
const fromEpoch = (ms: number, start_time: string, timeSettings: TimeSettings) =>
moment(start_time)
.utcOffset(timeSettings.utcOffset)
.startOf("day")
.add(ms, "ms");
const itemGracePeriod = (now: moment.Moment) =>
now.clone().subtract(1, "minute");
export const nextRegItemTimes =
(regimenItems: RegimenItem[],
startTime: string,
now: moment.Moment,
timeSettings: TimeSettings): moment.Moment[] => {
const times = regimenItems.map(ri =>
fromEpoch(ri.time_offset, startTime, timeSettings));
return times.filter(time => time.isSameOrAfter(itemGracePeriod(now))
&& time.isSameOrAfter(moment(startTime)));
};
const regimenCalendarAdder = (
index: ResourceIndex, timeSettings: TimeSettings) =>
(f: FarmEventWithRegimen, c: Calendar, now = moment()) => {
const { regimen_items } = f.executable;
const gracePeriod = itemGracePeriod(now);
const lastRI = last(regimen_items);
const lastRITime = lastRI &&
fromEpoch(lastRI.time_offset, f.start_time, timeSettings);
if (lastRITime && lastRITime.isSameOrAfter(gracePeriod)) {
const o = occurrence(moment(f.start_time), f, timeSettings, index);
o.heading = f.executable.name;
o.subheading = "";
c.insert(o);
}
regimen_items.map(ri => {
const time = fromEpoch(ri.time_offset, f.start_time, timeSettings);
if (time.isSameOrAfter(gracePeriod)
&& time.isSameOrAfter(moment(f.start_time))) {
const oo = occurrence(time, f, timeSettings, index);
const seq = findSequenceById(index, ri.sequence_id);
oo.heading = f.executable.name;
oo.subheading = seq.body.name;
c.insert(oo);
}
});
// Display empty regimens in UI so that they can be edited or deleted.
if (f.end_time && Object.keys(c.value).length === 0) {
c.insert(occurrence(moment(f.end_time), f, timeSettings, index,
{ empty: true }));
}
};
const addSequenceToCalendar =
(f: FarmEventWithSequence, c: Calendar, timeSettings: TimeSettings,
ri: ResourceIndex,
now = moment()) => {
const schedule = scheduleForFarmEvent(f, now);
// Display empty calendars in UI so that they can be edited or deleted.
if (f.end_time && schedule.items.length === 0) {
c.insert(occurrence(moment(f.end_time), f, timeSettings, ri,
{ empty: true }));
}
// Separate the last item from the calendar.
const lastItem = schedule.items.pop();
// Add all other items.
schedule.items.map(m => c.insert(occurrence(m, f, timeSettings, ri)));
if (schedule.shortenedBy > 0) {
// Indicate that not all items are displayed in the final item.
lastItem && c.insert(occurrence(
lastItem, f, timeSettings, ri, { numHidden: schedule.shortenedBy }));
} else {
// Add the final item. All items are displayed.
lastItem && c.insert(occurrence(lastItem, f, timeSettings, ri));
}
};