refactor external urls
parent
cf0af59e42
commit
66b5e3c962
|
@ -0,0 +1,33 @@
|
||||||
|
jest.unmock("../external_urls");
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
|
/* tslint:disable:max-line-length */
|
||||||
|
|
||||||
|
describe("ExternalUrl", () => {
|
||||||
|
it("returns urls", () => {
|
||||||
|
expect(ExternalUrl.featureMinVersions)
|
||||||
|
.toEqual("https://raw.githubusercontent.com/FarmBot/farmbot_os/FEATURE_MIN_VERSIONS.json");
|
||||||
|
expect(ExternalUrl.osReleaseNotes)
|
||||||
|
.toEqual("https://raw.githubusercontent.com/FarmBot/farmbot_os/RELEASE_NOTES.md");
|
||||||
|
expect(ExternalUrl.latestRelease)
|
||||||
|
.toEqual("https://api.github.com/repos/FarmBot/farmbot_os/releases/latest");
|
||||||
|
expect(ExternalUrl.webAppRepo)
|
||||||
|
.toEqual("https://github.com/FarmBot/Farmbot-Web-App");
|
||||||
|
expect(ExternalUrl.gitHubFarmBot)
|
||||||
|
.toEqual("https://github.com/FarmBot");
|
||||||
|
expect(ExternalUrl.softwareDocs)
|
||||||
|
.toEqual("https://software.farm.bot/docs");
|
||||||
|
expect(ExternalUrl.softwareForum)
|
||||||
|
.toEqual("http://forum.farmbot.org/c/software");
|
||||||
|
expect(ExternalUrl.OpenFarm.cropApi)
|
||||||
|
.toEqual("https://openfarm.cc/api/v1/crops/");
|
||||||
|
expect(ExternalUrl.OpenFarm.cropBrowse)
|
||||||
|
.toEqual("https://openfarm.cc/crops/");
|
||||||
|
expect(ExternalUrl.OpenFarm.newCrop)
|
||||||
|
.toEqual("https://openfarm.cc/en/crops/new");
|
||||||
|
expect(ExternalUrl.Videos.desktop)
|
||||||
|
.toEqual("https://cdn.shopify.com/s/files/1/2040/0289/files/Farm_Designer_Loop.mp4?9552037556691879018");
|
||||||
|
expect(ExternalUrl.Videos.mobile)
|
||||||
|
.toEqual("https://cdn.shopify.com/s/files/1/2040/0289/files/Controls.png?9668345515035078097");
|
||||||
|
});
|
||||||
|
});
|
|
@ -158,6 +158,10 @@ export class API {
|
||||||
get farmwareInstallationPath() {
|
get farmwareInstallationPath() {
|
||||||
return `${this.baseUrl}/api/farmware_installations/`;
|
return `${this.baseUrl}/api/farmware_installations/`;
|
||||||
}
|
}
|
||||||
|
/** /api/first_party_farmwares */
|
||||||
|
get firstPartyFarmwarePath() {
|
||||||
|
return `${this.baseUrl}/api/first_party_farmwares`;
|
||||||
|
}
|
||||||
/** /api/alerts/:id */
|
/** /api/alerts/:id */
|
||||||
get alertPath() { return `${this.baseUrl}/api/alerts/`; }
|
get alertPath() { return `${this.baseUrl}/api/alerts/`; }
|
||||||
/** /api/global_bulletins/:id */
|
/** /api/global_bulletins/:id */
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Session } from "./session";
|
import { Session } from "./session";
|
||||||
|
import { ExternalUrl } from "./external_urls";
|
||||||
|
|
||||||
const OUTER_STYLE: React.CSSProperties = {
|
const OUTER_STYLE: React.CSSProperties = {
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
|
@ -47,7 +48,7 @@ export function Apology(_: {}) {
|
||||||
<li>
|
<li>
|
||||||
<span>
|
<span>
|
||||||
Send a report to our developer team via the
|
Send a report to our developer team via the
|
||||||
<a href="http://forum.farmbot.org/c/software">FarmBot software
|
<a href={ExternalUrl.softwareForum}>FarmBot software
|
||||||
forum</a>. Including additional information (such as steps leading up
|
forum</a>. Including additional information (such as steps leading up
|
||||||
to the error) helps us identify solutions more quickly.
|
to the error) helps us identify solutions more quickly.
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {
|
import {
|
||||||
fetchReleases, fetchMinOsFeatureData, FEATURE_MIN_VERSIONS_URL,
|
fetchReleases, fetchMinOsFeatureData,
|
||||||
fetchLatestGHBetaRelease
|
fetchLatestGHBetaRelease
|
||||||
} from "../devices/actions";
|
} from "../devices/actions";
|
||||||
import { AuthState } from "./interfaces";
|
import { AuthState } from "./interfaces";
|
||||||
|
@ -16,6 +16,7 @@ import { Actions } from "../constants";
|
||||||
import { connectDevice } from "../connectivity/connect_device";
|
import { connectDevice } from "../connectivity/connect_device";
|
||||||
import { getFirstPartyFarmwareList } from "../farmware/actions";
|
import { getFirstPartyFarmwareList } from "../farmware/actions";
|
||||||
import { readOnlyInterceptor } from "../read_only_mode";
|
import { readOnlyInterceptor } from "../read_only_mode";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
export function didLogin(authState: AuthState, dispatch: Function) {
|
export function didLogin(authState: AuthState, dispatch: Function) {
|
||||||
API.setBaseUrl(authState.token.unencoded.iss);
|
API.setBaseUrl(authState.token.unencoded.iss);
|
||||||
|
@ -24,7 +25,7 @@ export function didLogin(authState: AuthState, dispatch: Function) {
|
||||||
beta_os_update_server && beta_os_update_server != "NOT_SET" &&
|
beta_os_update_server && beta_os_update_server != "NOT_SET" &&
|
||||||
dispatch(fetchLatestGHBetaRelease(beta_os_update_server));
|
dispatch(fetchLatestGHBetaRelease(beta_os_update_server));
|
||||||
dispatch(getFirstPartyFarmwareList());
|
dispatch(getFirstPartyFarmwareList());
|
||||||
dispatch(fetchMinOsFeatureData(FEATURE_MIN_VERSIONS_URL));
|
dispatch(fetchMinOsFeatureData(ExternalUrl.featureMinVersions));
|
||||||
dispatch(setToken(authState));
|
dispatch(setToken(authState));
|
||||||
Sync.fetchSyncData(dispatch);
|
Sync.fetchSyncData(dispatch);
|
||||||
dispatch(connectDevice(authState));
|
dispatch(connectDevice(authState));
|
||||||
|
|
|
@ -952,8 +952,7 @@ export namespace DiagnosticMessages {
|
||||||
but we have no recent record of FarmBot connecting to the internet.
|
but we have no recent record of FarmBot connecting to the internet.
|
||||||
This usually happens because of poor WiFi connectivity in the garden,
|
This usually happens because of poor WiFi connectivity in the garden,
|
||||||
a bad password during configuration, a very long power outage, or
|
a bad password during configuration, a very long power outage, or
|
||||||
blocked ports on FarmBot's local network. Please refer IT staff to
|
blocked ports on FarmBot's local network. Please refer IT staff to:`);
|
||||||
https://software.farm.bot/docs/for-it-security-professionals`);
|
|
||||||
|
|
||||||
export const NO_WS_AVAILABLE = trim(`You are either offline, using a web
|
export const NO_WS_AVAILABLE = trim(`You are either offline, using a web
|
||||||
browser that does not support WebSockets, or are behind a firewall that
|
browser that does not support WebSockets, or are behind a firewall that
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||||
import { get } from "lodash";
|
import { get } from "lodash";
|
||||||
import { Page } from "./ui/index";
|
import { Page } from "./ui/index";
|
||||||
import { Session } from "./session";
|
import { Session } from "./session";
|
||||||
|
import { ExternalUrl } from "./external_urls";
|
||||||
|
|
||||||
/** Use currying to pass down `error` object for now. */
|
/** Use currying to pass down `error` object for now. */
|
||||||
export function crashPage(error: object) {
|
export function crashPage(error: object) {
|
||||||
|
@ -24,7 +25,7 @@ export function crashPage(error: object) {
|
||||||
<li>Perform a "hard refresh" (<strong>CTRL + SHIFT + R</strong> on most machines).</li>
|
<li>Perform a "hard refresh" (<strong>CTRL + SHIFT + R</strong> on most machines).</li>
|
||||||
<li><span><a onClick={() => Session.clear()}>Log out by clicking here.</a></span></li>
|
<li><span><a onClick={() => Session.clear()}>Log out by clicking here.</a></span></li>
|
||||||
<li>Send the error information (below) to our developer team via the
|
<li>Send the error information (below) to our developer team via the
|
||||||
<a href="http://forum.farmbot.org/c/software">FarmBot software
|
<a href={ExternalUrl.softwareForum}>FarmBot software
|
||||||
forum</a>. Including additional information (such as steps leading up
|
forum</a>. Including additional information (such as steps leading up
|
||||||
to the error) help us identify solutions more quickly. </li>
|
to the error) help us identify solutions more quickly. </li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
|
@ -2,16 +2,13 @@ import { connect, MqttClient } from "mqtt";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { uuid } from "farmbot";
|
import { uuid } from "farmbot";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
error: Error | undefined;
|
error: Error | undefined;
|
||||||
stage: string;
|
stage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VIDEO_URL =
|
|
||||||
"https://cdn.shopify.com/s/files/1/2040/0289/files/Farm_Designer_Loop.mp4?9552037556691879018";
|
|
||||||
const PHONE_URL =
|
|
||||||
"https://cdn.shopify.com/s/files/1/2040/0289/files/Controls.png?9668345515035078097";
|
|
||||||
const WS_CONFIG = {
|
const WS_CONFIG = {
|
||||||
username: "farmbot_demo",
|
username: "farmbot_demo",
|
||||||
password: "required, but not used.",
|
password: "required, but not used.",
|
||||||
|
@ -63,9 +60,9 @@ export class DemoIframe extends React.Component<{}, State> {
|
||||||
|
|
||||||
return <div className="demo-container">
|
return <div className="demo-container">
|
||||||
<video muted={true} autoPlay={true} loop={true} className="demo-video">
|
<video muted={true} autoPlay={true} loop={true} className="demo-video">
|
||||||
<source src={VIDEO_URL} type="video/mp4" />
|
<source src={ExternalUrl.Videos.desktop} type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
<img className="demo-phone" src={PHONE_URL} />
|
<img className="demo-phone" src={ExternalUrl.Videos.mobile} />
|
||||||
<button className="demo-button" onClick={this.requestAccount}>
|
<button className="demo-button" onClick={this.requestAccount}>
|
||||||
{this.state.stage}
|
{this.state.stage}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -26,9 +26,6 @@ import { t } from "../i18next_wrapper";
|
||||||
|
|
||||||
const ON = 1, OFF = 0;
|
const ON = 1, OFF = 0;
|
||||||
export type ConfigKey = keyof McuParams;
|
export type ConfigKey = keyof McuParams;
|
||||||
export const FEATURE_MIN_VERSIONS_URL =
|
|
||||||
"https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/" +
|
|
||||||
"FEATURE_MIN_VERSIONS.json";
|
|
||||||
// Already filtering messages in FarmBot OS and the API- this is just for
|
// Already filtering messages in FarmBot OS and the API- this is just for
|
||||||
// an additional layer of safety.
|
// an additional layer of safety.
|
||||||
const BAD_WORDS = ["WPA", "PSK", "PASSWORD", "NERVES"];
|
const BAD_WORDS = ["WPA", "PSK", "PASSWORD", "NERVES"];
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { AutoUpdateRow } from "./fbos_settings/auto_update_row";
|
||||||
import { AutoSyncRow } from "./fbos_settings/auto_sync_row";
|
import { AutoSyncRow } from "./fbos_settings/auto_sync_row";
|
||||||
import { PowerAndReset } from "./fbos_settings/power_and_reset";
|
import { PowerAndReset } from "./fbos_settings/power_and_reset";
|
||||||
import { BootSequenceSelector } from "./fbos_settings/boot_sequence_selector";
|
import { BootSequenceSelector } from "./fbos_settings/boot_sequence_selector";
|
||||||
|
import { ExternalUrl } from "../../external_urls";
|
||||||
|
|
||||||
export enum ColWidth {
|
export enum ColWidth {
|
||||||
label = 3,
|
label = 3,
|
||||||
|
@ -22,15 +23,12 @@ export enum ColWidth {
|
||||||
button = 2
|
button = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
const OS_RELEASE_NOTES_URL =
|
|
||||||
"https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/RELEASE_NOTES.md";
|
|
||||||
|
|
||||||
export class FarmbotOsSettings
|
export class FarmbotOsSettings
|
||||||
extends React.Component<FarmbotOsProps, FarmbotOsState> {
|
extends React.Component<FarmbotOsProps, FarmbotOsState> {
|
||||||
state: FarmbotOsState = { allOsReleaseNotes: "" };
|
state: FarmbotOsState = { allOsReleaseNotes: "" };
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchReleaseNotes(OS_RELEASE_NOTES_URL);
|
this.fetchReleaseNotes(ExternalUrl.osReleaseNotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
get osMajorVersion() {
|
get osMajorVersion() {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { timeFormatString } from "../../../util";
|
||||||
import { TimeSettings } from "../../../interfaces";
|
import { TimeSettings } from "../../../interfaces";
|
||||||
import { StringConfigKey } from "farmbot/dist/resources/configs/fbos";
|
import { StringConfigKey } from "farmbot/dist/resources/configs/fbos";
|
||||||
import { boardType, FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
import { boardType, FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
||||||
|
import { ExternalUrl, FarmBotRepo } from "../../../external_urls";
|
||||||
|
|
||||||
/** Return an indicator color for the given temperature (C). */
|
/** Return an indicator color for the given temperature (C). */
|
||||||
export const colorFromTemp = (temp: number | undefined): string => {
|
export const colorFromTemp = (temp: number | undefined): string => {
|
||||||
|
@ -170,7 +171,7 @@ const shortenCommit = (longCommit: string) => (longCommit || "").slice(0, 8);
|
||||||
|
|
||||||
interface CommitDisplayProps {
|
interface CommitDisplayProps {
|
||||||
title: string;
|
title: string;
|
||||||
repo: string;
|
repo: FarmBotRepo;
|
||||||
commit: string;
|
commit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +185,7 @@ const CommitDisplay = (
|
||||||
{shortCommit === "---"
|
{shortCommit === "---"
|
||||||
? shortCommit
|
? shortCommit
|
||||||
: <a
|
: <a
|
||||||
href={`https://github.com/FarmBot/${repo}/tree/${shortCommit}`}
|
href={`${ExternalUrl.gitHubFarmBot}/${repo}/tree/${shortCommit}`}
|
||||||
target="_blank">
|
target="_blank">
|
||||||
{shortCommit}
|
{shortCommit}
|
||||||
</a>}
|
</a>}
|
||||||
|
@ -270,14 +271,15 @@ export function FbosDetails(props: FbosDetailsProps) {
|
||||||
timeSettings={props.timeSettings}
|
timeSettings={props.timeSettings}
|
||||||
device={props.deviceAccount} />
|
device={props.deviceAccount} />
|
||||||
<p><b>{t("Environment")}: </b>{env}</p>
|
<p><b>{t("Environment")}: </b>{env}</p>
|
||||||
<CommitDisplay title={t("Commit")} repo={"farmbot_os"} commit={commit} />
|
<CommitDisplay title={t("Commit")}
|
||||||
|
repo={FarmBotRepo.FarmBotOS} commit={commit} />
|
||||||
<p><b>{t("Target")}: </b>{target}</p>
|
<p><b>{t("Target")}: </b>{target}</p>
|
||||||
<p><b>{t("Node name")}: </b>{last((node_name || "").split("@"))}</p>
|
<p><b>{t("Node name")}: </b>{last((node_name || "").split("@"))}</p>
|
||||||
<p><b>{t("Device ID")}: </b>{props.deviceAccount.body.id}</p>
|
<p><b>{t("Device ID")}: </b>{props.deviceAccount.body.id}</p>
|
||||||
{isString(private_ip) && <p><b>{t("Local IP address")}: </b>{private_ip}</p>}
|
{isString(private_ip) && <p><b>{t("Local IP address")}: </b>{private_ip}</p>}
|
||||||
<p><b>{t("Firmware")}: </b>{reformatFwVersion(firmware_version)}</p>
|
<p><b>{t("Firmware")}: </b>{reformatFwVersion(firmware_version)}</p>
|
||||||
<CommitDisplay title={t("Firmware commit")}
|
<CommitDisplay title={t("Firmware commit")}
|
||||||
repo={"farmbot-arduino-firmware"} commit={firmwareCommit} />
|
repo={FarmBotRepo.FarmBotArduinoFirmware} commit={firmwareCommit} />
|
||||||
<p><b>{t("Firmware code")}: </b>{firmware_version}</p>
|
<p><b>{t("Firmware code")}: </b>{firmware_version}</p>
|
||||||
{isNumber(uptime) && <UptimeDisplay uptime_sec={uptime} />}
|
{isNumber(uptime) && <UptimeDisplay uptime_sec={uptime} />}
|
||||||
{isNumber(memory_usage) &&
|
{isNumber(memory_usage) &&
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import { Dictionary } from "farmbot";
|
import { Dictionary } from "farmbot";
|
||||||
import { DiagnosticMessages } from "../../constants";
|
import { DiagnosticMessages } from "../../constants";
|
||||||
|
import { docLink } from "../../ui/doc_link";
|
||||||
|
import { trim } from "../../util/util";
|
||||||
|
|
||||||
|
const DiagnosticMessagesWiFiOrConfig =
|
||||||
|
trim(`${DiagnosticMessages.WIFI_OR_CONFIG}
|
||||||
|
${docLink("for-it-security-professionals")}`);
|
||||||
|
|
||||||
// I don't like this at all.
|
// I don't like this at all.
|
||||||
// If anyone has a cleaner solution, I'd love to hear it.
|
// If anyone has a cleaner solution, I'd love to hear it.
|
||||||
|
@ -16,13 +22,13 @@ export const TRUTH_TABLE: Readonly<Dictionary<string | undefined>> = {
|
||||||
// 17: No MQTT connections.
|
// 17: No MQTT connections.
|
||||||
[0b10001]: DiagnosticMessages.NO_WS_AVAILABLE,
|
[0b10001]: DiagnosticMessages.NO_WS_AVAILABLE,
|
||||||
// 24: Browser is connected to API and MQTT.
|
// 24: Browser is connected to API and MQTT.
|
||||||
[0b11000]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b11000]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 9: At least the browser is connected to MQTT.
|
// 9: At least the browser is connected to MQTT.
|
||||||
[0b01001]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b01001]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 8: At least the browser is connected to MQTT.
|
// 8: At least the browser is connected to MQTT.
|
||||||
[0b01000]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b01000]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 25: Farmbot offline.
|
// 25: Farmbot offline.
|
||||||
[0b11001]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b11001]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 2: Browser offline. Farmbot last seen by the API recently.
|
// 2: Browser offline. Farmbot last seen by the API recently.
|
||||||
[0b00010]: DiagnosticMessages.NO_WS_AVAILABLE,
|
[0b00010]: DiagnosticMessages.NO_WS_AVAILABLE,
|
||||||
// 18: Farmbot last seen by the API recently.
|
// 18: Farmbot last seen by the API recently.
|
||||||
|
|
|
@ -93,7 +93,7 @@ export enum Feature {
|
||||||
variables = "variables",
|
variables = "variables",
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Object fetched from FEATURE_MIN_VERSIONS_URL. */
|
/** Object fetched from ExternalUrl.featureMinVersions. */
|
||||||
export type MinOsFeatureLookup = Partial<Record<Feature, string>>;
|
export type MinOsFeatureLookup = Partial<Record<Feature, string>>;
|
||||||
|
|
||||||
export interface BotState {
|
export interface BotState {
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
enum Org {
|
||||||
|
FarmBot = "FarmBot",
|
||||||
|
FarmBotLabs = "FarmBot-Labs",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FarmBotRepo {
|
||||||
|
FarmBotWebApp = "Farmbot-Web-App",
|
||||||
|
FarmBotOS = "farmbot_os",
|
||||||
|
FarmBotArduinoFirmware = "farmbot-arduino-firmware",
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FbosFile {
|
||||||
|
featureMinVersions = "FEATURE_MIN_VERSIONS.json",
|
||||||
|
osReleaseNotes = "RELEASE_NOTES.md",
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace ExternalUrl {
|
||||||
|
const GITHUB = "https://github.com";
|
||||||
|
const GITHUB_RAW = "https://raw.githubusercontent.com";
|
||||||
|
const GITHUB_API = "https://api.github.com";
|
||||||
|
const OPENFARM = "https://openfarm.cc";
|
||||||
|
const SOFTWARE_DOCS = "https://software.farm.bot";
|
||||||
|
const FORUM = "http://forum.farmbot.org";
|
||||||
|
const SHOPIFY_CDN = "https://cdn.shopify.com/s/files/1/2040/0289/files";
|
||||||
|
|
||||||
|
const FBOS_RAW = `${GITHUB_RAW}/${Org.FarmBot}/${FarmBotRepo.FarmBotOS}`;
|
||||||
|
export const featureMinVersions = `${FBOS_RAW}/${FbosFile.featureMinVersions}`;
|
||||||
|
export const osReleaseNotes = `${FBOS_RAW}/${FbosFile.osReleaseNotes}`;
|
||||||
|
|
||||||
|
export const latestRelease =
|
||||||
|
`${GITHUB_API}/repos/${Org.FarmBot}/${FarmBotRepo.FarmBotOS}/releases/latest`;
|
||||||
|
|
||||||
|
export const gitHubFarmBot = `${GITHUB}/${Org.FarmBot}`;
|
||||||
|
export const webAppRepo =
|
||||||
|
`${GITHUB}/${Org.FarmBot}/${FarmBotRepo.FarmBotWebApp}`;
|
||||||
|
|
||||||
|
export const softwareDocs = `${SOFTWARE_DOCS}/docs`;
|
||||||
|
export const softwareForum = `${FORUM}/c/software`;
|
||||||
|
|
||||||
|
export namespace OpenFarm {
|
||||||
|
export const cropApi = `${OPENFARM}/api/v1/crops/`;
|
||||||
|
export const cropBrowse = `${OPENFARM}/crops/`;
|
||||||
|
export const newCrop = `${OPENFARM}/en/crops/new`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Videos {
|
||||||
|
export const desktop =
|
||||||
|
`${SHOPIFY_CDN}/Farm_Designer_Loop.mp4?9552037556691879018`;
|
||||||
|
export const mobile = `${SHOPIFY_CDN}/Controls.png?9668345515035078097`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,9 +67,6 @@ export namespace OpenFarm {
|
||||||
type: string;
|
type: string;
|
||||||
attributes: ImageAttrs;
|
attributes: ImageAttrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cropUrl = "https://openfarm.cc/api/v1/crops";
|
|
||||||
export const browsingCropUrl = "https://openfarm.cc/crops/";
|
|
||||||
}
|
}
|
||||||
/** Returned by https://openfarm.cc/api/v1/crops?filter=q */
|
/** Returned by https://openfarm.cc/api/v1/crops?filter=q */
|
||||||
export interface CropSearchResult {
|
export interface CropSearchResult {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
import { startCase, isArray, chain, isNumber } from "lodash";
|
import { startCase, isArray, chain, isNumber } from "lodash";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { Panel } from "../panel_header";
|
import { Panel } from "../panel_header";
|
||||||
|
import { ExternalUrl } from "../../external_urls";
|
||||||
|
|
||||||
interface InfoFieldProps {
|
interface InfoFieldProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -170,7 +171,7 @@ const CropDragInfoTile =
|
||||||
const EditOnOpenFarm = ({ slug }: { slug: string }) =>
|
const EditOnOpenFarm = ({ slug }: { slug: string }) =>
|
||||||
<div className="edit-on-openfarm">
|
<div className="edit-on-openfarm">
|
||||||
<span>{t("Edit on")} </span>
|
<span>{t("Edit on")} </span>
|
||||||
<a href={OpenFarm.browsingCropUrl + slug} target="_blank"
|
<a href={ExternalUrl.OpenFarm.cropBrowse + slug} target="_blank"
|
||||||
title={t("Open OpenFarm.cc in a new tab")}>
|
title={t("Open OpenFarm.cc in a new tab")}>
|
||||||
{"OpenFarm"}
|
{"OpenFarm"}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
} from "../../ui/empty_state_wrapper";
|
} from "../../ui/empty_state_wrapper";
|
||||||
import { Content } from "../../constants";
|
import { Content } from "../../constants";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import { ExternalUrl } from "../../external_urls";
|
||||||
|
|
||||||
/** A stripped down version of OFSearchResult */
|
/** A stripped down version of OFSearchResult */
|
||||||
interface Result {
|
interface Result {
|
||||||
|
@ -24,7 +25,7 @@ export class OpenFarmResults extends React.Component<SearchResultProps, {}> {
|
||||||
|
|
||||||
get text(): JSX.Element {
|
get text(): JSX.Element {
|
||||||
return <p>{`${t(Content.CROP_NOT_FOUND_INTRO)} `}
|
return <p>{`${t(Content.CROP_NOT_FOUND_INTRO)} `}
|
||||||
<a href="https://openfarm.cc/en/crops/new" target="_blank">
|
<a href={ExternalUrl.OpenFarm.newCrop} target="_blank">
|
||||||
{t(Content.CROP_NOT_FOUND_LINK)}
|
{t(Content.CROP_NOT_FOUND_LINK)}
|
||||||
</a>
|
</a>
|
||||||
</p>;
|
</p>;
|
||||||
|
|
|
@ -4,8 +4,10 @@ import { DEFAULT_ICON } from "../open_farm/icons";
|
||||||
import { Actions } from "../constants";
|
import { Actions } from "../constants";
|
||||||
import { ExecutableType } from "farmbot/dist/resources/api_resources";
|
import { ExecutableType } from "farmbot/dist/resources/api_resources";
|
||||||
import { get } from "lodash";
|
import { get } from "lodash";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
const url = (q: string) => `${OpenFarm.cropUrl}?include=pictures&filter=${q}`;
|
const url = (q: string) =>
|
||||||
|
`${ExternalUrl.OpenFarm.cropApi}?include=pictures&filter=${q}`;
|
||||||
const openFarmSearchQuery = (q: string): AxiosPromise<CropSearchResult> =>
|
const openFarmSearchQuery = (q: string): AxiosPromise<CropSearchResult> =>
|
||||||
axios.get<CropSearchResult>(url(q));
|
axios.get<CropSearchResult>(url(q));
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ jest.mock("axios", () => ({
|
||||||
get: jest.fn(() => {
|
get: jest.fn(() => {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: [
|
data: [
|
||||||
{ manifest: "url", name: "farmware0" },
|
{ package: "farmware0" },
|
||||||
{ manifest: "url", name: "farmware1" }
|
{ package: "farmware1" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { FarmwareManifestEntry } from "./interfaces";
|
|
||||||
import { Actions } from "../constants";
|
import { Actions } from "../constants";
|
||||||
import { urlFor } from "../api/crud";
|
import { urlFor } from "../api/crud";
|
||||||
|
import { API } from "../api";
|
||||||
const farmwareManifestUrl =
|
import { FarmwareManifest } from "farmbot";
|
||||||
"https://raw.githubusercontent.com/FarmBot-Labs/farmware_manifests" +
|
|
||||||
"/master/manifest.json";
|
|
||||||
|
|
||||||
export const getFirstPartyFarmwareList = () => {
|
export const getFirstPartyFarmwareList = () => {
|
||||||
return (dispatch: Function) => {
|
return (dispatch: Function) => {
|
||||||
axios.get<FarmwareManifestEntry[]>(farmwareManifestUrl)
|
axios.get<FarmwareManifest[]>(API.current.firstPartyFarmwarePath)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
const names = r.data.map((fw: FarmwareManifestEntry) => fw.name);
|
const names = r.data.map(fw => fw.package);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: Actions.FETCH_FIRST_PARTY_FARMWARE_NAMES_OK,
|
type: Actions.FETCH_FIRST_PARTY_FARMWARE_NAMES_OK,
|
||||||
payload: names
|
payload: names
|
||||||
|
|
|
@ -24,8 +24,6 @@ export interface FarmwareState {
|
||||||
infoOpen: boolean;
|
infoOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FarmwareManifestEntry = Record<"name" | "manifest", string>;
|
|
||||||
|
|
||||||
export interface FarmwareConfigMenuProps {
|
export interface FarmwareConfigMenuProps {
|
||||||
show: boolean | undefined;
|
show: boolean | undefined;
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
const VIDEO_URL = "https://cdn.shopify.com/s/files/1/2040/0289/files/" +
|
import { ExternalUrl } from "../external_urls";
|
||||||
"Farm_Designer_Loop.mp4?9552037556691879018";
|
|
||||||
|
|
||||||
export const LaptopSplash = ({ className }: { className: string }) =>
|
export const LaptopSplash = ({ className }: { className: string }) =>
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
|
@ -8,7 +7,7 @@ export const LaptopSplash = ({ className }: { className: string }) =>
|
||||||
<div className="laptop">
|
<div className="laptop">
|
||||||
<div className="laptop-screen">
|
<div className="laptop-screen">
|
||||||
<video muted autoPlay loop>
|
<video muted autoPlay loop>
|
||||||
<source src={VIDEO_URL} type="video/mp4" />
|
<source src={ExternalUrl.Videos.desktop} type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
<span className="laptop-shine" />
|
<span className="laptop-shine" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { AccountMenuProps } from "./interfaces";
|
||||||
import { Link } from "../link";
|
import { Link } from "../link";
|
||||||
import { shortRevision } from "../util";
|
import { shortRevision } from "../util";
|
||||||
import { t } from "../i18next_wrapper";
|
import { t } from "../i18next_wrapper";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
export const AdditionalMenu = (props: AccountMenuProps) => {
|
export const AdditionalMenu = (props: AccountMenuProps) => {
|
||||||
return <div className="nav-additional-menu">
|
return <div className="nav-additional-menu">
|
||||||
|
@ -30,7 +31,7 @@ export const AdditionalMenu = (props: AccountMenuProps) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="app-version">
|
<div className="app-version">
|
||||||
<label>{t("VERSION")}</label>:
|
<label>{t("VERSION")}</label>:
|
||||||
<a href="https://github.com/FarmBot/Farmbot-Web-App" target="_blank">
|
<a href={ExternalUrl.webAppRepo} target="_blank">
|
||||||
{shortRevision().slice(0, 8)}
|
{shortRevision().slice(0, 8)}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
import { OpenFarmAPI, svgToUrl } from "../icons";
|
import { svgToUrl } from "../icons";
|
||||||
|
|
||||||
describe("OpenFarmAPI", () => {
|
|
||||||
it("has a base URL", () => {
|
|
||||||
expect(OpenFarmAPI.OFBaseURL).toContain("openfarm.cc");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("svgToUrl()", () => {
|
describe("svgToUrl()", () => {
|
||||||
it("returns svg url", () => {
|
it("returns svg url", () => {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import axios, { AxiosResponse } from "axios";
|
import axios, { AxiosResponse } from "axios";
|
||||||
import { Dictionary } from "farmbot";
|
import { Dictionary } from "farmbot";
|
||||||
import { isObject } from "lodash";
|
import { isObject } from "lodash";
|
||||||
import { OFCropAttrs, OFCropResponse, OpenFarmAPI, svgToUrl } from "./icons";
|
import { OFCropAttrs, OFCropResponse, svgToUrl } from "./icons";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
export type OFIcon = Readonly<OFCropAttrs>;
|
export type OFIcon = Readonly<OFCropAttrs>;
|
||||||
type IconDictionary = Dictionary<OFIcon | undefined>;
|
type IconDictionary = Dictionary<OFIcon | undefined>;
|
||||||
|
@ -57,7 +58,7 @@ const cacheTheIcon = (slug: string) =>
|
||||||
};
|
};
|
||||||
|
|
||||||
function HTTPIconFetch(slug: string) {
|
function HTTPIconFetch(slug: string) {
|
||||||
const url = OpenFarmAPI.OFBaseURL + slug;
|
const url = ExternalUrl.OpenFarm.cropApi + slug;
|
||||||
// Avoid duplicate requests.
|
// Avoid duplicate requests.
|
||||||
if (promiseCache[url]) { return promiseCache[url]; }
|
if (promiseCache[url]) { return promiseCache[url]; }
|
||||||
promiseCache[url] = axios
|
promiseCache[url] = axios
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const BASE = "https://openfarm.cc/api/v1/crops/";
|
|
||||||
export const DATA_URI = "data:image/svg+xml;utf8,";
|
export const DATA_URI = "data:image/svg+xml;utf8,";
|
||||||
export const DEFAULT_ICON = "/app-resources/img/generic-plant.svg";
|
export const DEFAULT_ICON = "/app-resources/img/generic-plant.svg";
|
||||||
|
|
||||||
|
@ -20,10 +19,6 @@ export interface OFCropResponse {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace OpenFarmAPI {
|
|
||||||
export const OFBaseURL = BASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function svgToUrl(xml: string | undefined): string {
|
export function svgToUrl(xml: string | undefined): string {
|
||||||
return xml ?
|
return xml ?
|
||||||
(DATA_URI + encodeURIComponent(xml)) : DEFAULT_ICON;
|
(DATA_URI + encodeURIComponent(xml)) : DEFAULT_ICON;
|
||||||
|
|
|
@ -3,9 +3,7 @@ import axios from "axios";
|
||||||
import { t } from "../i18next_wrapper";
|
import { t } from "../i18next_wrapper";
|
||||||
import { GithubRelease } from "../devices/interfaces";
|
import { GithubRelease } from "../devices/interfaces";
|
||||||
import { Content } from "../constants";
|
import { Content } from "../constants";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
const LATEST_RELEASE_URL =
|
|
||||||
"https://api.github.com/repos/farmbot/farmbot_os/releases/latest";
|
|
||||||
|
|
||||||
interface OsDownloadState {
|
interface OsDownloadState {
|
||||||
tagName: string;
|
tagName: string;
|
||||||
|
@ -49,7 +47,7 @@ export class OsDownload extends React.Component<{}, OsDownloadState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchLatestRelease = () =>
|
fetchLatestRelease = () =>
|
||||||
axios.get<GithubRelease>(LATEST_RELEASE_URL)
|
axios.get<GithubRelease>(ExternalUrl.latestRelease)
|
||||||
.then(resp =>
|
.then(resp =>
|
||||||
this.setState({
|
this.setState({
|
||||||
tagName: resp.data.tag_name,
|
tagName: resp.data.tag_name,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { API } from "../api";
|
||||||
import { Row, Col, Widget, WidgetHeader, WidgetBody } from "../ui";
|
import { Row, Col, Widget, WidgetHeader, WidgetBody } from "../ui";
|
||||||
import { TermsCheckbox } from "../front_page/terms_checkbox";
|
import { TermsCheckbox } from "../front_page/terms_checkbox";
|
||||||
import { t } from "../i18next_wrapper";
|
import { t } from "../i18next_wrapper";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
interface Props { }
|
interface Props { }
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -86,7 +87,7 @@ export class TosUpdate extends React.Component<Props, Partial<State>> {
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{t("Please send us an email at contact@farm.bot or see the ")}
|
{t("Please send us an email at contact@farm.bot or see the ")}
|
||||||
<a href="http://forum.farmbot.org/">
|
<a href={ExternalUrl.softwareForum}>
|
||||||
{t("FarmBot forum.")}
|
{t("FarmBot forum.")}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { docLink, BASE_URL } from "../doc_link";
|
import { docLink } from "../doc_link";
|
||||||
|
import { ExternalUrl } from "../../external_urls";
|
||||||
|
|
||||||
describe("docLink", () => {
|
describe("docLink", () => {
|
||||||
it("creates doc links", () => {
|
it("creates doc links", () => {
|
||||||
expect(docLink()).toEqual(BASE_URL);
|
expect(docLink()).toEqual(ExternalUrl.softwareDocs + "/");
|
||||||
expect(docLink("farmware")).toEqual(BASE_URL + "farmware");
|
expect(docLink("farmware")).toEqual(ExternalUrl.softwareDocs + "/farmware");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const BASE_URL = "https://software.farm.bot/docs/";
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
/** A centralized list of all documentation slugs in the app makes it easier to
|
/** A centralized list of all documentation slugs in the app makes it easier to
|
||||||
* rename / move links in the future. */
|
* rename / move links in the future. */
|
||||||
|
@ -7,11 +7,13 @@ export const DOC_SLUGS = {
|
||||||
"camera-calibration": "Camera Calibration",
|
"camera-calibration": "Camera Calibration",
|
||||||
"the-farmbot-web-app": "Web App",
|
"the-farmbot-web-app": "Web App",
|
||||||
"farmware": "Farmware",
|
"farmware": "Farmware",
|
||||||
"connecting-farmbot-to-the-internet": "Connecting FarmBot to the Internet"
|
"connecting-farmbot-to-the-internet": "Connecting FarmBot to the Internet",
|
||||||
|
"for-it-security-professionals": "For IT Security Professionals",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DocSlug = keyof typeof DOC_SLUGS;
|
export type DocSlug = keyof typeof DOC_SLUGS;
|
||||||
|
|
||||||
/** WHY?: The function keeps things DRY. It also makes life easier when the
|
/** WHY?: The function keeps things DRY. It also makes life easier when the
|
||||||
* documentation URL / slug name changes. */
|
* documentation URL / slug name changes. */
|
||||||
export const docLink = (slug?: DocSlug) => BASE_URL + (slug || "");
|
export const docLink = (slug?: DocSlug) =>
|
||||||
|
`${ExternalUrl.softwareDocs}/${slug || ""}`;
|
||||||
|
|
Loading…
Reference in New Issue