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() {
|
||||
return `${this.baseUrl}/api/farmware_installations/`;
|
||||
}
|
||||
/** /api/first_party_farmwares */
|
||||
get firstPartyFarmwarePath() {
|
||||
return `${this.baseUrl}/api/first_party_farmwares`;
|
||||
}
|
||||
/** /api/alerts/:id */
|
||||
get alertPath() { return `${this.baseUrl}/api/alerts/`; }
|
||||
/** /api/global_bulletins/:id */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { Session } from "./session";
|
||||
import { ExternalUrl } from "./external_urls";
|
||||
|
||||
const OUTER_STYLE: React.CSSProperties = {
|
||||
borderRadius: "10px",
|
||||
|
@ -47,7 +48,7 @@ export function Apology(_: {}) {
|
|||
<li>
|
||||
<span>
|
||||
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
|
||||
to the error) helps us identify solutions more quickly.
|
||||
</span>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import axios from "axios";
|
||||
import {
|
||||
fetchReleases, fetchMinOsFeatureData, FEATURE_MIN_VERSIONS_URL,
|
||||
fetchReleases, fetchMinOsFeatureData,
|
||||
fetchLatestGHBetaRelease
|
||||
} from "../devices/actions";
|
||||
import { AuthState } from "./interfaces";
|
||||
|
@ -16,6 +16,7 @@ import { Actions } from "../constants";
|
|||
import { connectDevice } from "../connectivity/connect_device";
|
||||
import { getFirstPartyFarmwareList } from "../farmware/actions";
|
||||
import { readOnlyInterceptor } from "../read_only_mode";
|
||||
import { ExternalUrl } from "../external_urls";
|
||||
|
||||
export function didLogin(authState: AuthState, dispatch: Function) {
|
||||
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" &&
|
||||
dispatch(fetchLatestGHBetaRelease(beta_os_update_server));
|
||||
dispatch(getFirstPartyFarmwareList());
|
||||
dispatch(fetchMinOsFeatureData(FEATURE_MIN_VERSIONS_URL));
|
||||
dispatch(fetchMinOsFeatureData(ExternalUrl.featureMinVersions));
|
||||
dispatch(setToken(authState));
|
||||
Sync.fetchSyncData(dispatch);
|
||||
dispatch(connectDevice(authState));
|
||||
|
|
|
@ -952,8 +952,7 @@ export namespace DiagnosticMessages {
|
|||
but we have no recent record of FarmBot connecting to the internet.
|
||||
This usually happens because of poor WiFi connectivity in the garden,
|
||||
a bad password during configuration, a very long power outage, or
|
||||
blocked ports on FarmBot's local network. Please refer IT staff to
|
||||
https://software.farm.bot/docs/for-it-security-professionals`);
|
||||
blocked ports on FarmBot's local network. Please refer IT staff to:`);
|
||||
|
||||
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
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||
import { get } from "lodash";
|
||||
import { Page } from "./ui/index";
|
||||
import { Session } from "./session";
|
||||
import { ExternalUrl } from "./external_urls";
|
||||
|
||||
/** Use currying to pass down `error` object for now. */
|
||||
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><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
|
||||
<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
|
||||
to the error) help us identify solutions more quickly. </li>
|
||||
</ol>
|
||||
|
|
|
@ -2,16 +2,13 @@ import { connect, MqttClient } from "mqtt";
|
|||
import React from "react";
|
||||
import { uuid } from "farmbot";
|
||||
import axios from "axios";
|
||||
import { ExternalUrl } from "../external_urls";
|
||||
|
||||
interface State {
|
||||
error: Error | undefined;
|
||||
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 = {
|
||||
username: "farmbot_demo",
|
||||
password: "required, but not used.",
|
||||
|
@ -63,9 +60,9 @@ export class DemoIframe extends React.Component<{}, State> {
|
|||
|
||||
return <div className="demo-container">
|
||||
<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>
|
||||
<img className="demo-phone" src={PHONE_URL} />
|
||||
<img className="demo-phone" src={ExternalUrl.Videos.mobile} />
|
||||
<button className="demo-button" onClick={this.requestAccount}>
|
||||
{this.state.stage}
|
||||
</button>
|
||||
|
|
|
@ -26,9 +26,6 @@ import { t } from "../i18next_wrapper";
|
|||
|
||||
const ON = 1, OFF = 0;
|
||||
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
|
||||
// an additional layer of safety.
|
||||
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 { PowerAndReset } from "./fbos_settings/power_and_reset";
|
||||
import { BootSequenceSelector } from "./fbos_settings/boot_sequence_selector";
|
||||
import { ExternalUrl } from "../../external_urls";
|
||||
|
||||
export enum ColWidth {
|
||||
label = 3,
|
||||
|
@ -22,15 +23,12 @@ export enum ColWidth {
|
|||
button = 2
|
||||
}
|
||||
|
||||
const OS_RELEASE_NOTES_URL =
|
||||
"https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/RELEASE_NOTES.md";
|
||||
|
||||
export class FarmbotOsSettings
|
||||
extends React.Component<FarmbotOsProps, FarmbotOsState> {
|
||||
state: FarmbotOsState = { allOsReleaseNotes: "" };
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchReleaseNotes(OS_RELEASE_NOTES_URL);
|
||||
this.fetchReleaseNotes(ExternalUrl.osReleaseNotes);
|
||||
}
|
||||
|
||||
get osMajorVersion() {
|
||||
|
|
|
@ -14,6 +14,7 @@ import { timeFormatString } from "../../../util";
|
|||
import { TimeSettings } from "../../../interfaces";
|
||||
import { StringConfigKey } from "farmbot/dist/resources/configs/fbos";
|
||||
import { boardType, FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
||||
import { ExternalUrl, FarmBotRepo } from "../../../external_urls";
|
||||
|
||||
/** Return an indicator color for the given temperature (C). */
|
||||
export const colorFromTemp = (temp: number | undefined): string => {
|
||||
|
@ -170,7 +171,7 @@ const shortenCommit = (longCommit: string) => (longCommit || "").slice(0, 8);
|
|||
|
||||
interface CommitDisplayProps {
|
||||
title: string;
|
||||
repo: string;
|
||||
repo: FarmBotRepo;
|
||||
commit: string;
|
||||
}
|
||||
|
||||
|
@ -184,7 +185,7 @@ const CommitDisplay = (
|
|||
{shortCommit === "---"
|
||||
? shortCommit
|
||||
: <a
|
||||
href={`https://github.com/FarmBot/${repo}/tree/${shortCommit}`}
|
||||
href={`${ExternalUrl.gitHubFarmBot}/${repo}/tree/${shortCommit}`}
|
||||
target="_blank">
|
||||
{shortCommit}
|
||||
</a>}
|
||||
|
@ -270,14 +271,15 @@ export function FbosDetails(props: FbosDetailsProps) {
|
|||
timeSettings={props.timeSettings}
|
||||
device={props.deviceAccount} />
|
||||
<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("Node name")}: </b>{last((node_name || "").split("@"))}</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>}
|
||||
<p><b>{t("Firmware")}: </b>{reformatFwVersion(firmware_version)}</p>
|
||||
<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>
|
||||
{isNumber(uptime) && <UptimeDisplay uptime_sec={uptime} />}
|
||||
{isNumber(memory_usage) &&
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { Dictionary } from "farmbot";
|
||||
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.
|
||||
// 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.
|
||||
[0b10001]: DiagnosticMessages.NO_WS_AVAILABLE,
|
||||
// 24: Browser is connected to API and MQTT.
|
||||
[0b11000]: DiagnosticMessages.WIFI_OR_CONFIG,
|
||||
[0b11000]: DiagnosticMessagesWiFiOrConfig,
|
||||
// 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.
|
||||
[0b01000]: DiagnosticMessages.WIFI_OR_CONFIG,
|
||||
[0b01000]: DiagnosticMessagesWiFiOrConfig,
|
||||
// 25: Farmbot offline.
|
||||
[0b11001]: DiagnosticMessages.WIFI_OR_CONFIG,
|
||||
[0b11001]: DiagnosticMessagesWiFiOrConfig,
|
||||
// 2: Browser offline. Farmbot last seen by the API recently.
|
||||
[0b00010]: DiagnosticMessages.NO_WS_AVAILABLE,
|
||||
// 18: Farmbot last seen by the API recently.
|
||||
|
|
|
@ -93,7 +93,7 @@ export enum Feature {
|
|||
variables = "variables",
|
||||
}
|
||||
|
||||
/** Object fetched from FEATURE_MIN_VERSIONS_URL. */
|
||||
/** Object fetched from ExternalUrl.featureMinVersions. */
|
||||
export type MinOsFeatureLookup = Partial<Record<Feature, string>>;
|
||||
|
||||
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;
|
||||
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 */
|
||||
export interface CropSearchResult {
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
import { startCase, isArray, chain, isNumber } from "lodash";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { Panel } from "../panel_header";
|
||||
import { ExternalUrl } from "../../external_urls";
|
||||
|
||||
interface InfoFieldProps {
|
||||
title: string;
|
||||
|
@ -170,7 +171,7 @@ const CropDragInfoTile =
|
|||
const EditOnOpenFarm = ({ slug }: { slug: string }) =>
|
||||
<div className="edit-on-openfarm">
|
||||
<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")}>
|
||||
{"OpenFarm"}
|
||||
</a>
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
} from "../../ui/empty_state_wrapper";
|
||||
import { Content } from "../../constants";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { ExternalUrl } from "../../external_urls";
|
||||
|
||||
/** A stripped down version of OFSearchResult */
|
||||
interface Result {
|
||||
|
@ -24,7 +25,7 @@ export class OpenFarmResults extends React.Component<SearchResultProps, {}> {
|
|||
|
||||
get text(): JSX.Element {
|
||||
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)}
|
||||
</a>
|
||||
</p>;
|
||||
|
|
|
@ -4,8 +4,10 @@ import { DEFAULT_ICON } from "../open_farm/icons";
|
|||
import { Actions } from "../constants";
|
||||
import { ExecutableType } from "farmbot/dist/resources/api_resources";
|
||||
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> =>
|
||||
axios.get<CropSearchResult>(url(q));
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ jest.mock("axios", () => ({
|
|||
get: jest.fn(() => {
|
||||
return Promise.resolve({
|
||||
data: [
|
||||
{ manifest: "url", name: "farmware0" },
|
||||
{ manifest: "url", name: "farmware1" }
|
||||
{ package: "farmware0" },
|
||||
{ package: "farmware1" }
|
||||
]
|
||||
});
|
||||
}),
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import axios from "axios";
|
||||
import { FarmwareManifestEntry } from "./interfaces";
|
||||
import { Actions } from "../constants";
|
||||
import { urlFor } from "../api/crud";
|
||||
|
||||
const farmwareManifestUrl =
|
||||
"https://raw.githubusercontent.com/FarmBot-Labs/farmware_manifests" +
|
||||
"/master/manifest.json";
|
||||
import { API } from "../api";
|
||||
import { FarmwareManifest } from "farmbot";
|
||||
|
||||
export const getFirstPartyFarmwareList = () => {
|
||||
return (dispatch: Function) => {
|
||||
axios.get<FarmwareManifestEntry[]>(farmwareManifestUrl)
|
||||
axios.get<FarmwareManifest[]>(API.current.firstPartyFarmwarePath)
|
||||
.then(r => {
|
||||
const names = r.data.map((fw: FarmwareManifestEntry) => fw.name);
|
||||
const names = r.data.map(fw => fw.package);
|
||||
dispatch({
|
||||
type: Actions.FETCH_FIRST_PARTY_FARMWARE_NAMES_OK,
|
||||
payload: names
|
||||
|
|
|
@ -24,8 +24,6 @@ export interface FarmwareState {
|
|||
infoOpen: boolean;
|
||||
}
|
||||
|
||||
export type FarmwareManifestEntry = Record<"name" | "manifest", string>;
|
||||
|
||||
export interface FarmwareConfigMenuProps {
|
||||
show: boolean | undefined;
|
||||
dispatch: Function;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as React from "react";
|
||||
const VIDEO_URL = "https://cdn.shopify.com/s/files/1/2040/0289/files/" +
|
||||
"Farm_Designer_Loop.mp4?9552037556691879018";
|
||||
import { ExternalUrl } from "../external_urls";
|
||||
|
||||
export const LaptopSplash = ({ className }: { className: string }) =>
|
||||
<div className={className}>
|
||||
|
@ -8,7 +7,7 @@ export const LaptopSplash = ({ className }: { className: string }) =>
|
|||
<div className="laptop">
|
||||
<div className="laptop-screen">
|
||||
<video muted autoPlay loop>
|
||||
<source src={VIDEO_URL} type="video/mp4" />
|
||||
<source src={ExternalUrl.Videos.desktop} type="video/mp4" />
|
||||
</video>
|
||||
<span className="laptop-shine" />
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { AccountMenuProps } from "./interfaces";
|
|||
import { Link } from "../link";
|
||||
import { shortRevision } from "../util";
|
||||
import { t } from "../i18next_wrapper";
|
||||
import { ExternalUrl } from "../external_urls";
|
||||
|
||||
export const AdditionalMenu = (props: AccountMenuProps) => {
|
||||
return <div className="nav-additional-menu">
|
||||
|
@ -30,7 +31,7 @@ export const AdditionalMenu = (props: AccountMenuProps) => {
|
|||
</div>
|
||||
<div className="app-version">
|
||||
<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)}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import { OpenFarmAPI, svgToUrl } from "../icons";
|
||||
|
||||
describe("OpenFarmAPI", () => {
|
||||
it("has a base URL", () => {
|
||||
expect(OpenFarmAPI.OFBaseURL).toContain("openfarm.cc");
|
||||
});
|
||||
});
|
||||
import { svgToUrl } from "../icons";
|
||||
|
||||
describe("svgToUrl()", () => {
|
||||
it("returns svg url", () => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import axios, { AxiosResponse } from "axios";
|
||||
import { Dictionary } from "farmbot";
|
||||
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>;
|
||||
type IconDictionary = Dictionary<OFIcon | undefined>;
|
||||
|
@ -57,7 +58,7 @@ const cacheTheIcon = (slug: string) =>
|
|||
};
|
||||
|
||||
function HTTPIconFetch(slug: string) {
|
||||
const url = OpenFarmAPI.OFBaseURL + slug;
|
||||
const url = ExternalUrl.OpenFarm.cropApi + slug;
|
||||
// Avoid duplicate requests.
|
||||
if (promiseCache[url]) { return promiseCache[url]; }
|
||||
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 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 {
|
||||
return xml ?
|
||||
(DATA_URI + encodeURIComponent(xml)) : DEFAULT_ICON;
|
||||
|
|
|
@ -3,9 +3,7 @@ import axios from "axios";
|
|||
import { t } from "../i18next_wrapper";
|
||||
import { GithubRelease } from "../devices/interfaces";
|
||||
import { Content } from "../constants";
|
||||
|
||||
const LATEST_RELEASE_URL =
|
||||
"https://api.github.com/repos/farmbot/farmbot_os/releases/latest";
|
||||
import { ExternalUrl } from "../external_urls";
|
||||
|
||||
interface OsDownloadState {
|
||||
tagName: string;
|
||||
|
@ -49,7 +47,7 @@ export class OsDownload extends React.Component<{}, OsDownloadState> {
|
|||
}
|
||||
|
||||
fetchLatestRelease = () =>
|
||||
axios.get<GithubRelease>(LATEST_RELEASE_URL)
|
||||
axios.get<GithubRelease>(ExternalUrl.latestRelease)
|
||||
.then(resp =>
|
||||
this.setState({
|
||||
tagName: resp.data.tag_name,
|
||||
|
|
|
@ -8,6 +8,7 @@ import { API } from "../api";
|
|||
import { Row, Col, Widget, WidgetHeader, WidgetBody } from "../ui";
|
||||
import { TermsCheckbox } from "../front_page/terms_checkbox";
|
||||
import { t } from "../i18next_wrapper";
|
||||
import { ExternalUrl } from "../external_urls";
|
||||
|
||||
interface Props { }
|
||||
interface State {
|
||||
|
@ -86,7 +87,7 @@ export class TosUpdate extends React.Component<Props, Partial<State>> {
|
|||
</p>
|
||||
<p>
|
||||
{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.")}
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { docLink, BASE_URL } from "../doc_link";
|
||||
import { docLink } from "../doc_link";
|
||||
import { ExternalUrl } from "../../external_urls";
|
||||
|
||||
describe("docLink", () => {
|
||||
it("creates doc links", () => {
|
||||
expect(docLink()).toEqual(BASE_URL);
|
||||
expect(docLink("farmware")).toEqual(BASE_URL + "farmware");
|
||||
expect(docLink()).toEqual(ExternalUrl.softwareDocs + "/");
|
||||
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
|
||||
* rename / move links in the future. */
|
||||
|
@ -7,11 +7,13 @@ export const DOC_SLUGS = {
|
|||
"camera-calibration": "Camera Calibration",
|
||||
"the-farmbot-web-app": "Web App",
|
||||
"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;
|
||||
|
||||
/** WHY?: The function keeps things DRY. It also makes life easier when the
|
||||
* 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