diff --git a/frontend/__tests__/external_urls_test.ts b/frontend/__tests__/external_urls_test.ts new file mode 100644 index 000000000..156e1932b --- /dev/null +++ b/frontend/__tests__/external_urls_test.ts @@ -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"); + }); +}); diff --git a/frontend/api/api.ts b/frontend/api/api.ts index 03d5a55d0..264242e62 100644 --- a/frontend/api/api.ts +++ b/frontend/api/api.ts @@ -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 */ diff --git a/frontend/apology.tsx b/frontend/apology.tsx index a00871358..bd2ac6328 100644 --- a/frontend/apology.tsx +++ b/frontend/apology.tsx @@ -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(_: {}) {
  • Send a report to our developer team via the  - FarmBot software + FarmBot software forum. Including additional information (such as steps leading up to the error) helps us identify solutions more quickly. diff --git a/frontend/auth/actions.ts b/frontend/auth/actions.ts index 76126927c..9527939f0 100644 --- a/frontend/auth/actions.ts +++ b/frontend/auth/actions.ts @@ -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)); diff --git a/frontend/constants.ts b/frontend/constants.ts index 7dd95c219..c76339cb7 100644 --- a/frontend/constants.ts +++ b/frontend/constants.ts @@ -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 diff --git a/frontend/crash_page.tsx b/frontend/crash_page.tsx index 031ff12ad..7f6b92dd7 100644 --- a/frontend/crash_page.tsx +++ b/frontend/crash_page.tsx @@ -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) {
  • Perform a "hard refresh" (CTRL + SHIFT + R on most machines).
  • Session.clear()}>Log out by clicking here.
  • Send the error information (below) to our developer team via the - FarmBot software + FarmBot software forum. Including additional information (such as steps leading up to the error) help us identify solutions more quickly.
  • diff --git a/frontend/demo/demo_iframe.tsx b/frontend/demo/demo_iframe.tsx index e0f5ba72c..16681552f 100644 --- a/frontend/demo/demo_iframe.tsx +++ b/frontend/demo/demo_iframe.tsx @@ -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
    - + diff --git a/frontend/devices/actions.ts b/frontend/devices/actions.ts index 67be2d969..edea6ce0f 100644 --- a/frontend/devices/actions.ts +++ b/frontend/devices/actions.ts @@ -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"]; diff --git a/frontend/devices/components/farmbot_os_settings.tsx b/frontend/devices/components/farmbot_os_settings.tsx index cb4b9f0de..cbfa9d10f 100644 --- a/frontend/devices/components/farmbot_os_settings.tsx +++ b/frontend/devices/components/farmbot_os_settings.tsx @@ -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 { state: FarmbotOsState = { allOsReleaseNotes: "" }; componentDidMount() { - this.fetchReleaseNotes(OS_RELEASE_NOTES_URL); + this.fetchReleaseNotes(ExternalUrl.osReleaseNotes); } get osMajorVersion() { diff --git a/frontend/devices/components/fbos_settings/fbos_details.tsx b/frontend/devices/components/fbos_settings/fbos_details.tsx index 43ad6a0c7..c06a4b627 100644 --- a/frontend/devices/components/fbos_settings/fbos_details.tsx +++ b/frontend/devices/components/fbos_settings/fbos_details.tsx @@ -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 : {shortCommit} } @@ -270,14 +271,15 @@ export function FbosDetails(props: FbosDetailsProps) { timeSettings={props.timeSettings} device={props.deviceAccount} />

    {t("Environment")}: {env}

    - +

    {t("Target")}: {target}

    {t("Node name")}: {last((node_name || "").split("@"))}

    {t("Device ID")}: {props.deviceAccount.body.id}

    {isString(private_ip) &&

    {t("Local IP address")}: {private_ip}

    }

    {t("Firmware")}: {reformatFwVersion(firmware_version)}

    + repo={FarmBotRepo.FarmBotArduinoFirmware} commit={firmwareCommit} />

    {t("Firmware code")}: {firmware_version}

    {isNumber(uptime) && } {isNumber(memory_usage) && diff --git a/frontend/devices/connectivity/truth_table.ts b/frontend/devices/connectivity/truth_table.ts index 2a95f5eaf..3ff3ebc08 100644 --- a/frontend/devices/connectivity/truth_table.ts +++ b/frontend/devices/connectivity/truth_table.ts @@ -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> = { // 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. diff --git a/frontend/devices/interfaces.ts b/frontend/devices/interfaces.ts index 8963b9f86..767ea8bd5 100644 --- a/frontend/devices/interfaces.ts +++ b/frontend/devices/interfaces.ts @@ -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>; export interface BotState { diff --git a/frontend/external_urls.ts b/frontend/external_urls.ts new file mode 100644 index 000000000..47e123e98 --- /dev/null +++ b/frontend/external_urls.ts @@ -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`; + } +} diff --git a/frontend/farm_designer/openfarm.ts b/frontend/farm_designer/openfarm.ts index 10899a263..4b07b09d9 100644 --- a/frontend/farm_designer/openfarm.ts +++ b/frontend/farm_designer/openfarm.ts @@ -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 { diff --git a/frontend/farm_designer/plants/crop_info.tsx b/frontend/farm_designer/plants/crop_info.tsx index e6bc6f63b..e56c69f50 100644 --- a/frontend/farm_designer/plants/crop_info.tsx +++ b/frontend/farm_designer/plants/crop_info.tsx @@ -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 }) =>
    {t("Edit on")}  - {"OpenFarm"} diff --git a/frontend/farm_designer/plants/openfarm_search_results.tsx b/frontend/farm_designer/plants/openfarm_search_results.tsx index 2efddc935..d6040a26f 100644 --- a/frontend/farm_designer/plants/openfarm_search_results.tsx +++ b/frontend/farm_designer/plants/openfarm_search_results.tsx @@ -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 { get text(): JSX.Element { return

    {`${t(Content.CROP_NOT_FOUND_INTRO)} `} - + {t(Content.CROP_NOT_FOUND_LINK)}

    ; diff --git a/frontend/farm_designer/util.ts b/frontend/farm_designer/util.ts index 40500c821..425fece84 100644 --- a/frontend/farm_designer/util.ts +++ b/frontend/farm_designer/util.ts @@ -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 => axios.get(url(q)); diff --git a/frontend/farmware/__tests__/actions_test.ts b/frontend/farmware/__tests__/actions_test.ts index a32929d45..d4bfde0b4 100644 --- a/frontend/farmware/__tests__/actions_test.ts +++ b/frontend/farmware/__tests__/actions_test.ts @@ -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" } ] }); }), diff --git a/frontend/farmware/actions.ts b/frontend/farmware/actions.ts index 0453e9d7f..c10135119 100644 --- a/frontend/farmware/actions.ts +++ b/frontend/farmware/actions.ts @@ -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(farmwareManifestUrl) + axios.get(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 diff --git a/frontend/farmware/interfaces.ts b/frontend/farmware/interfaces.ts index 1a9cfe4d1..32d7cb132 100644 --- a/frontend/farmware/interfaces.ts +++ b/frontend/farmware/interfaces.ts @@ -24,8 +24,6 @@ export interface FarmwareState { infoOpen: boolean; } -export type FarmwareManifestEntry = Record<"name" | "manifest", string>; - export interface FarmwareConfigMenuProps { show: boolean | undefined; dispatch: Function; diff --git a/frontend/front_page/laptop_splash.tsx b/frontend/front_page/laptop_splash.tsx index ec4308bfa..bc9ddb94f 100644 --- a/frontend/front_page/laptop_splash.tsx +++ b/frontend/front_page/laptop_splash.tsx @@ -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 }) =>
    @@ -8,7 +7,7 @@ export const LaptopSplash = ({ className }: { className: string }) =>
    diff --git a/frontend/nav/additional_menu.tsx b/frontend/nav/additional_menu.tsx index 83a15f9c8..4e0b1290f 100644 --- a/frontend/nav/additional_menu.tsx +++ b/frontend/nav/additional_menu.tsx @@ -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
    @@ -30,7 +31,7 @@ export const AdditionalMenu = (props: AccountMenuProps) => {
    diff --git a/frontend/open_farm/__tests__/icons_test.ts b/frontend/open_farm/__tests__/icons_test.ts index 499c8e618..153fd1e74 100644 --- a/frontend/open_farm/__tests__/icons_test.ts +++ b/frontend/open_farm/__tests__/icons_test.ts @@ -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", () => { diff --git a/frontend/open_farm/cached_crop.ts b/frontend/open_farm/cached_crop.ts index 24dc069ab..21d166b6a 100644 --- a/frontend/open_farm/cached_crop.ts +++ b/frontend/open_farm/cached_crop.ts @@ -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; type IconDictionary = Dictionary; @@ -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 diff --git a/frontend/open_farm/icons.ts b/frontend/open_farm/icons.ts index c79f93549..b2ed9f940 100644 --- a/frontend/open_farm/icons.ts +++ b/frontend/open_farm/icons.ts @@ -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; diff --git a/frontend/os_download/content.tsx b/frontend/os_download/content.tsx index c30d5c016..f9d0e66f9 100644 --- a/frontend/os_download/content.tsx +++ b/frontend/os_download/content.tsx @@ -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(LATEST_RELEASE_URL) + axios.get(ExternalUrl.latestRelease) .then(resp => this.setState({ tagName: resp.data.tag_name, diff --git a/frontend/tos_update/component.tsx b/frontend/tos_update/component.tsx index df93f2639..92d9a9336 100644 --- a/frontend/tos_update/component.tsx +++ b/frontend/tos_update/component.tsx @@ -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> {

    {t("Please send us an email at contact@farm.bot or see the ")} - + {t("FarmBot forum.")}

    diff --git a/frontend/ui/__tests__/doc_link_test.ts b/frontend/ui/__tests__/doc_link_test.ts index 5a1c61d06..bcb65f595 100644 --- a/frontend/ui/__tests__/doc_link_test.ts +++ b/frontend/ui/__tests__/doc_link_test.ts @@ -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"); }); }); diff --git a/frontend/ui/doc_link.ts b/frontend/ui/doc_link.ts index a65b5958c..8cbcc2edd 100644 --- a/frontend/ui/doc_link.ts +++ b/frontend/ui/doc_link.ts @@ -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 || ""}`;