Show data consistency state via sync button
parent
4496cf3040
commit
356b745d6e
|
@ -19,7 +19,8 @@ describe("<App />: Controls Pop-Up", () => {
|
|||
loaded: [],
|
||||
logs: [],
|
||||
user: fakeUser(),
|
||||
bot: bot
|
||||
bot: bot,
|
||||
consistent: true
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -57,7 +58,8 @@ describe.skip("<App />: Loading", () => {
|
|||
loaded: [],
|
||||
logs: [],
|
||||
user: fakeUser(),
|
||||
bot: bot
|
||||
bot: bot,
|
||||
consistent: true
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -88,7 +90,8 @@ describe("<App />: NavBar", () => {
|
|||
loaded: [],
|
||||
logs: [],
|
||||
user: fakeUser(),
|
||||
bot: bot
|
||||
bot: bot,
|
||||
consistent: true
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ export interface AppProps {
|
|||
logs: Log[];
|
||||
user: TaggedUser | undefined;
|
||||
bot: BotState;
|
||||
consistent: boolean;
|
||||
}
|
||||
|
||||
function mapStateToProps(props: Everything): AppProps {
|
||||
|
@ -39,7 +40,8 @@ function mapStateToProps(props: Everything): AppProps {
|
|||
.sortBy("created_at")
|
||||
.reverse()
|
||||
.value(),
|
||||
loaded: props.resources.loaded
|
||||
loaded: props.resources.loaded,
|
||||
consistent: props.connectivity.consistent
|
||||
};
|
||||
}
|
||||
/** Time at which the app gives up and asks the user to refresh */
|
||||
|
@ -82,6 +84,7 @@ export class App extends React.Component<AppProps, {}> {
|
|||
return <div className="app">
|
||||
<HotKeys dispatch={this.props.dispatch} />
|
||||
<NavBar
|
||||
consistent={this.props.consistent}
|
||||
user={this.props.user}
|
||||
bot={this.props.bot}
|
||||
dispatch={this.props.dispatch}
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
import { AxiosRequestConfig } from "axios";
|
||||
import { get, set } from "lodash";
|
||||
import { uuid } from "farmbot";
|
||||
import { store } from "../redux/store";
|
||||
import { Actions } from "../constants";
|
||||
|
||||
const WHERE_WE_STORE_REQUEST_ID = "__FARMBOT_REQUEST_ID__";
|
||||
|
||||
function getRequestId(conf: AxiosRequestConfig): string {
|
||||
return get(conf, WHERE_WE_STORE_REQUEST_ID);
|
||||
}
|
||||
|
||||
export function setRequestId(conf: AxiosRequestConfig): string {
|
||||
const u = uuid();
|
||||
set(conf, [WHERE_WE_STORE_REQUEST_ID], u);
|
||||
return u;
|
||||
}
|
||||
const outstandingRequests: Set<AxiosRequestConfig> = new Set();
|
||||
(window as any)["outstanding_requests"] = outstandingRequests;
|
||||
|
||||
/**
|
||||
* PROBLEM: You save a sequence and click "RUN" very fast. The remote device
|
||||
|
@ -45,19 +36,27 @@ export function setRequestId(conf: AxiosRequestConfig): string {
|
|||
* and friends.
|
||||
*/
|
||||
export function startTracking(conf: AxiosRequestConfig) {
|
||||
const id = setRequestId(conf);
|
||||
if (id) {
|
||||
console.log(`START TRACKING ${conf.url}`);
|
||||
} else {
|
||||
throw new Error("NO ID");
|
||||
}
|
||||
outstandingRequests.add(conf);
|
||||
toggleConsistency(false);
|
||||
setTimeout(() => {
|
||||
stopTracking(conf);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
export function stopTracking(conf: AxiosRequestConfig) {
|
||||
const id = getRequestId(conf);
|
||||
if (id) {
|
||||
console.log(`STOP TRACKING ${conf.url}`);
|
||||
} else {
|
||||
throw new Error("NO ID");
|
||||
outstandingRequests.delete(conf);
|
||||
if (outstandingRequests.size < 1) {
|
||||
toggleConsistency(true);
|
||||
}
|
||||
}
|
||||
|
||||
export const toggleConsistency = (payload: boolean) => {
|
||||
const current = store.getState().connectivity.consistent === payload;
|
||||
const state = payload ? "consistent" : "inconsistent";
|
||||
if (current) {
|
||||
console.log(`Already ${state}`);
|
||||
} else {
|
||||
console.log(`Flipping to ${state} state`);
|
||||
store.dispatch({ type: Actions.SET_CONSISTENCY, payload });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -24,9 +24,15 @@ export interface ResourceReady {
|
|||
data: [DeviceAccountSettings];
|
||||
}
|
||||
|
||||
type StatusRecord = Record<Edge, ConnectionStatus | undefined>;
|
||||
/** Mapping of known connection status.
|
||||
* An `undefined` value means we don't know. */
|
||||
export type ConnectionState = Record<Edge, ConnectionStatus | undefined>;
|
||||
export interface ConnectionState extends StatusRecord {
|
||||
/** Have all API requests been acknowledged by external services?
|
||||
* This flag lets us know if it is safe to do data critical tasks with the bot
|
||||
*/
|
||||
consistent: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateMqttData {
|
||||
status: "UPDATE"
|
||||
|
|
|
@ -7,10 +7,15 @@ export const DEFAULT_STATE: ConnectionState = {
|
|||
"bot.mqtt": undefined,
|
||||
"user.mqtt": undefined,
|
||||
"user.api": undefined,
|
||||
consistent: true
|
||||
};
|
||||
|
||||
export let connectivityReducer =
|
||||
generateReducer<ConnectionState>(DEFAULT_STATE)
|
||||
.add<boolean>(Actions.SET_CONSISTENCY, (s, { payload }) => {
|
||||
s.consistent = payload;
|
||||
return s;
|
||||
})
|
||||
.add<EdgeStatus>(Actions.NETWORK_EDGE_CHANGE, (s, { payload }) => {
|
||||
s[payload.name] = payload.status;
|
||||
return s;
|
||||
|
|
|
@ -441,5 +441,6 @@ export enum Actions {
|
|||
|
||||
// Network
|
||||
NETWORK_EDGE_CHANGE = "NETWORK_EDGE_CHANGE",
|
||||
RESET_NETWORK = "RESET_NETWORK"
|
||||
RESET_NETWORK = "RESET_NETWORK",
|
||||
SET_CONSISTENCY = "SET_CONSISTENCY"
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ describe("NavBar", () => {
|
|||
it("has correct parent classname", () => {
|
||||
const wrapper = shallow(
|
||||
<NavBar
|
||||
consistent={true}
|
||||
logs={[log]}
|
||||
bot={bot}
|
||||
user={taggedUser}
|
||||
|
@ -22,6 +23,7 @@ describe("NavBar", () => {
|
|||
|
||||
it("closes nav menu", () => {
|
||||
const wrapper = shallow(<NavBar
|
||||
consistent={true}
|
||||
logs={[log]}
|
||||
bot={bot}
|
||||
user={taggedUser}
|
||||
|
|
|
@ -33,6 +33,19 @@ export class NavBar extends React.Component<NavBarProps, Partial<NavBarState>> {
|
|||
close = (name: keyof NavBarState) => () =>
|
||||
this.setState({ [name]: false });
|
||||
|
||||
syncButton = () => {
|
||||
if (this.props.consistent) {
|
||||
return <SyncButton
|
||||
bot={this.props.bot}
|
||||
user={this.props.user}
|
||||
dispatch={this.props.dispatch} />;
|
||||
} else {
|
||||
return <button className="nav-sync red fb-button"
|
||||
onClick={() => { }}>
|
||||
{t("Saving...")}
|
||||
</button>;
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const hasName = this.props.user && this.props.user.body.name;
|
||||
|
||||
|
@ -88,10 +101,7 @@ export class NavBar extends React.Component<NavBarProps, Partial<NavBarState>> {
|
|||
<EStopButton
|
||||
bot={this.props.bot}
|
||||
user={this.props.user} />
|
||||
<SyncButton
|
||||
bot={this.props.bot}
|
||||
user={this.props.user}
|
||||
dispatch={this.props.dispatch} />
|
||||
{this.syncButton()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,7 @@ export interface NavButtonProps {
|
|||
}
|
||||
|
||||
export interface NavBarProps {
|
||||
consistent: boolean;
|
||||
logs: Log[];
|
||||
bot: BotState;
|
||||
user: TaggedUser | undefined;
|
||||
|
|
Loading…
Reference in New Issue