starling-devex 0.1.2

Starling: a local dev orchestrator with a central daemon, shared named-URL proxy, and a k9s-style TUI (a Rust port of Tilt + portless)
import React, { Component } from "react"
import "./AnalyticsNudge.scss"
import { linkToTiltDocs, TiltDocsPage } from "./constants"

export const NUDGE_TIMEOUT_MS = 15000
const nudgeElem = (): JSX.Element => {
  return (
    <p>
      Welcome to Tilt! We collect anonymized usage data to help us improve. Is
      that OK? (
      <a
        href={linkToTiltDocs(TiltDocsPage.TelemetryFaq)}
        target="_blank"
        rel="noopener noreferrer"
      >
        Read more
      </a>
      )
    </p>
  )
}
const successOptInElem = (): JSX.Element => {
  return (
    <p data-testid="option-success">
      Thanks for helping us improve Tilt for you and everyone! (You can change
      your mind by running <code>tilt analytics opt out</code> in your
      terminal.)
    </p>
  )
}
const successOptOutElem = (): JSX.Element => {
  return (
    <p data-testid="optout-success">
      Okay, opting you out of telemetry. If you change your mind, you can run{" "}
      <code>tilt analytics opt in</code> in your terminal.
    </p>
  )
}
const errorElem = (respBody: string): JSX.Element => {
  return (
    <div role="alert">
      <p>Oh no, something went wrong! Request failed with:</p>
      <pre>{respBody}</pre>
      <p>
        <a
          href="https://tilt.dev/contact"
          target="_blank"
          rel="noopener noreferrer"
        >
          contact us
        </a>
      </p>
    </div>
  )
}

type AnalyticsNudgeProps = {
  needsNudge: boolean
}

type AnalyticsNudgeState = {
  requestMade: boolean
  optIn: boolean
  responseCode: number
  responseBody: string
  dismissed: boolean
  dismissTimeout: NodeJS.Timeout | null
}

class AnalyticsNudge extends Component<
  AnalyticsNudgeProps,
  AnalyticsNudgeState
> {
  constructor(props: AnalyticsNudgeProps) {
    super(props)

    this.state = {
      requestMade: false,
      optIn: false,
      responseCode: 0,
      responseBody: "",
      dismissed: false,
      dismissTimeout: null,
    }
  }

  componentWillUnmount() {
    if (this.state.dismissTimeout !== null) {
      clearTimeout(this.state.dismissTimeout)
    }
  }

  shouldShow(): boolean {
    return (
      (this.props.needsNudge && !this.state.dismissed) ||
      (!this.state.dismissed && this.state.requestMade)
    )
  }

  analyticsOpt(optIn: boolean) {
    let url = `//${window.location.host}/api/analytics_opt`

    let payload = { opt: optIn ? "opt-in" : "opt-out" }

    this.setState({ requestMade: true, optIn: optIn })

    fetch(url, {
      method: "post",
      body: JSON.stringify(payload),
    }).then((response: Response) => {
      response.text().then((body: string) => {
        this.setState({
          responseCode: response.status,
          responseBody: body,
        })
      })

      if (response.status === 200) {
        // if we successfully recorded the choice, dismiss the nudge after a few seconds
        const dismissTimeout = setTimeout(() => {
          this.setState({ dismissed: true, dismissTimeout: null })
        }, NUDGE_TIMEOUT_MS)

        this.setState({ dismissTimeout })
      }
    })
  }

  dismiss() {
    this.setState({ dismissed: true })
  }

  messageElem(): JSX.Element {
    if (this.state.responseCode) {
      if (this.state.responseCode === 200) {
        // Successfully called opt endpt.
        if (this.state.optIn) {
          // User opted in
          return (
            <>
              {successOptInElem()}
              <span>
                <button
                  className="AnalyticsNudge-button"
                  onClick={() => this.dismiss()}
                >
                  Dismiss
                </button>
              </span>
            </>
          )
        }
        // User opted out
        return (
          <>
            {successOptOutElem()}
            <span>
              <button
                className="AnalyticsNudge-button"
                onClick={() => this.dismiss()}
              >
                Dismiss
              </button>
            </span>
          </>
        )
      } else {
        return (
          // Error calling the opt endpt.
          <>
            {errorElem(this.state.responseBody)}
            <span>
              <button
                className="AnalyticsNudge-button"
                onClick={() => this.dismiss()}
              >
                Dismiss
              </button>
            </span>
          </>
        )
      }
    }

    if (this.state.requestMade) {
      // Request in progress
      return <p data-testid="opt-loading">Okay, making it so...</p>
    }
    return (
      <>
        {nudgeElem()}
        <span>
          <button
            className="AnalyticsNudge-button"
            onClick={() => this.analyticsOpt(false)}
          >
            Nope
          </button>
          <button
            className="AnalyticsNudge-button opt-in"
            onClick={() => this.analyticsOpt(true)}
          >
            I'm in
          </button>
        </span>
      </>
    )
  }
  render() {
    if (!this.shouldShow()) {
      return null
    }

    return (
      <aside
        aria-label="Tilt analytics options"
        className="AnalyticsNudge is-visible"
      >
        {this.messageElem()}
      </aside>
    )
  }
}

export default AnalyticsNudge