<commit_before>import React from "react"
import { NavigatorIOS, View, ViewProperties } from "react-native"
import { commitMutation, createFragmentContainer, graphql, RelayPaginationProp } from "react-relay"
import styled from "styled-components/native"

import { Schema, screenTrack, track } from "../../../utils/track"

import { Flex } from "../Elements/Flex"
import { Serif14, SerifItalic14, SerifSemibold14, SerifSemibold18 } from "../Elements/Typography"

import { BiddingThemeProvider } from "../Components/BiddingThemeProvider"
import { BidInfoRow } from "../Components/BidInfoRow"
import { Button } from "../Components/Button"
import { Checkbox } from "../Components/Checkbox"
import { Container } from "../Components/Containers"
import { Divider } from "../Components/Divider"
import { Timer } from "../Components/Timer"
import { Title } from "../Components/Title"

import SwitchBoard from "lib/NativeModules/SwitchBoard"
import { metaphysics } from "../../../metaphysics"

import { BidResultScreen } from "./BidResult"

import { ConfirmBid_sale_artwork } from "__generated__/ConfirmBid_sale_artwork.graphql"

interface Bid {
  display: string
  cents: number
}

export interface ConfirmBidProps extends ViewProperties {
  sale_artwork: ConfirmBid_sale_artwork
  bid: Bid
  relay?: RelayPaginationProp
  navigator?: NavigatorIOS
}

interface ConformBidState {
  conditionsOfSaleChecked: boolean
  isLoading: boolean
}

const MAX_POLL_ATTEMPTS = 20

const bidderPositionMutation = graphql`
  mutation ConfirmBidMutation($input: BidderPositionInput!) {
    createBidderPosition(input: $input) {
      result {
        position {
          id
        }
        status
        message_header
        message_description_md
      }
    }
  }
`
@screenTrack({
  context_screen: Schema.PageNames.BidFlowConfirmBidPage,
  context_screen_owner_type: null,
})
export class ConfirmBid extends React.Component<ConfirmBidProps, ConformBidState> {
  state = { conditionsOfSaleChecked: false, isLoading: false }

  private pollCount = 0

  onPressConditionsOfSale = () => {
    SwitchBoard.presentModalViewController(this, "/conditions-of-sale?present_modally=true")
  }

  @track({
    action_type: Schema.ActionTypes.Tap,
    action_name: Schema.ActionNames.BidFlowPlaceBid,
  })
  placeBid() {
    this.setState({ isLoading: true })

    commitMutation(this.props.relay.environment, {
      onCompleted: (results, errors) => {
        this.verifyBidPosition(results, errors)
      },
      onError: e => {
        this.setState({ isLoading: false })
        // TODO catch error!
        // this.verifyAndShowBidResult(null, e)
        console.error("error!", e, e.message)
      },
      mutation: bidderPositionMutation,
      variables: {
        input: {
          sale_id: this.props.sale_artwork.sale.id,
          artwork_id: this.props.sale_artwork.artwork.id,
          max_bid_amount_cents: this.props.bid.cents,
        },
      },
    })
  }

  queryForBidPosition(bidderPositionID: string) {
    const query = `
        {
          me {
            bidder_position(id: "${bidderPositionID}") {
              status
              message_header
              message_description_md
              position {
                id
                processed_at
                is_active
                suggested_next_bid {
                  cents
                  display
                }
              }
            }
          }
        }
      `
    return metaphysics({ query })
  }

  verifyBidPosition(results, errors) {
    // TODO: Need to handle if the results object is empty, for example if errors occurred and no request was made
    // TODO: add analytics for errors
    const status = results.createBidderPosition.result.status
    if (!errors && status === "SUCCESS") {
      this.bidPlacedSuccessfully(results)
    } else {
      const message_header = results.createBidderPosition.result.message_header
      const message_description_md = results.createBidderPosition.result.message_description_md
      this.showBidResult(false, status, message_header, message_description_md)
    }
  }

  @track({
    action_type: Schema.ActionTypes.Success,
    action_name: Schema.ActionNames.BidFlowPlaceBid,
  })
  bidPlacedSuccessfully(results) {
    const positionId = results.createBidderPosition.result.position.id
    this.queryForBidPosition(positionId).then(this.checkBidPosition.bind(this))
  }

  checkBidPosition(result) {
    const bidderPosition = result.data.me.bidder_position.position
    const status = result.data.me.bidder_position.status
    if (status === "WINNING") {
      this.showBidResult(true, "WINNING")
    } else if (status === "PENDING") {
      if (this.pollCount > MAX_POLL_ATTEMPTS) {
        const md = `We're receiving a high volume of traffic and your bid is still processing.  \
If you don’t receive an update soon, please contact [<EMAIL>](mailto:<EMAIL>). `

        this.showBidResult(false, "PROCESSING", "Bid Processing", md)
      } else {
        // initiating new request here (vs setInterval) to make sure we wait for the previus calls to return before making a new one
        setTimeout(() => {
          this.queryForBidPosition(bidderPosition.id).then(this.checkBidPosition.bind(this))
        }, 1000)
        this.pollCount += 1
      }
    } else {
      this.showBidResult(
        false,
        status,
        result.data.me.bidder_position.message_header,
        result.data.me.bidder_position.message_description_md,
        result.data.me.bidder_position.position.suggested_next_bid
      )
    }
  }

  showBidResult(
    winning: boolean,
    status: string,
    messageHeader?: string,
    messageDescriptionMd?: string,
    suggestedNextBid?: Bid
  ) {
    this.props.navigator.push({
      component: BidResultScreen,
      title: "",
      passProps: {
        sale_artwork: this.props.sale_artwork,
        status,<commit_msg>Move dependency-free functions and constants out of ConfirmBid<commit_after>import React from "react"
import { NavigatorIOS, View, ViewProperties } from "react-native"
import { commitMutation, createFragmentContainer, graphql, RelayPaginationProp } from "react-relay"
import styled from "styled-components/native"

import { Schema, screenTrack, track } from "../../../utils/track"

import { Flex } from "../Elements/Flex"
import { Serif14, SerifItalic14, SerifSemibold14, SerifSemibold18 } from "../Elements/Typography"

import { BiddingThemeProvider } from "../Components/BiddingThemeProvider"
import { BidInfoRow } from "../Components/BidInfoRow"
import { Button } from "../Components/Button"
import { Checkbox } from "../Components/Checkbox"
import { Container } from "../Components/Containers"
import { Divider } from "../Components/Divider"
import { Timer } from "../Components/Timer"
import { Title } from "../Components/Title"

import SwitchBoard from "lib/NativeModules/SwitchBoard"
import { metaphysics } from "../../../metaphysics"

import { BidResultScreen } from "./BidResult"

import { ConfirmBid_sale_artwork } from "__generated__/ConfirmBid_sale_artwork.graphql"

interface Bid {
  display: string
  cents: number
}

export interface ConfirmBidProps extends ViewProperties {
  sale_artwork: ConfirmBid_sale_artwork
  bid: Bid
  relay?: RelayPaginationProp
  navigator?: NavigatorIOS
}

interface ConformBidState {
  conditionsOfSaleChecked: boolean
  isLoading: boolean
}

const MAX_POLL_ATTEMPTS = 20

const bidderPositionMutation = graphql`
  mutation ConfirmBidMutation($input: BidderPositionInput!) {
    createBidderPosition(input: $input) {
      result {
        position {
          id
        }
        status
        message_header
        message_description_md
      }
    }
  }
`

const queryForBidPosition = (bidderPositionID: string) => {
  return metaphysics({
    query: `
      {
        me {
          bidder_position(id: "${bidderPositionID}") {
            status
            message_header
            message_description_md
            position {
              id
              processed_at
              is_active
              suggested_next_bid {
                cents
                display
              }
            }
          }
        }
      }
    `,
  })
}

const messageForPollingTimeout = `
  We’re receiving a high volume of traffic
  and your bid is still processing.

  If you don’t receive an update soon,
  please contact [<EMAIL>](mailto:<EMAIL>).
`

@screenTrack({
  context_screen: Schema.PageNames.BidFlowConfirmBidPage,
  context_screen_owner_type: null,
})
export class ConfirmBid extends React.Component<ConfirmBidProps, ConformBidState> {
  state = { conditionsOfSaleChecked: false, isLoading: false }

  private pollCount = 0

  onPressConditionsOfSale = () => {
    SwitchBoard.presentModalViewController(this, "/conditions-of-sale?present_modally=true")
  }

  @track({
    action_type: Schema.ActionTypes.Tap,
    action_name: Schema.ActionNames.BidFlowPlaceBid,
  })
  placeBid() {
    this.setState({ isLoading: true })

    commitMutation(this.props.relay.environment, {
      onCompleted: (results, errors) => {
        this.verifyBidPosition(results, errors)
      },
      onError: e => {
        this.setState({ isLoading: false })
        // TODO catch error!
        // this.verifyAndShowBidResult(null, e)
        console.error("error!", e, e.message)
      },
      mutation: bidderPositionMutation,
      variables: {
        input: {
          sale_id: this.props.sale_artwork.sale.id,
          artwork_id: this.props.sale_artwork.artwork.id,
          max_bid_amount_cents: this.props.bid.cents,
        },
      },
    })
  }

  verifyBidPosition(results, errors) {
    // TODO: Need to handle if the results object is empty, for example if errors occurred and no request was made
    // TODO: add analytics for errors
    const status = results.createBidderPosition.result.status
    if (!errors && status === "SUCCESS") {
      this.bidPlacedSuccessfully(results.createBidderPosition.result.position.id)
    } else {
      const message_header = results.createBidderPosition.result.message_header
      const message_description_md = results.createBidderPosition.result.message_description_md
      this.showBidResult(false, status, message_header, message_description_md)
    }
  }

  @track({
    action_type: Schema.ActionTypes.Success,
    action_name: Schema.ActionNames.BidFlowPlaceBid,
  })
  bidPlacedSuccessfully(positionId) {
    queryForBidPosition(positionId).then(this.checkBidPosition.bind(this))
  }

  checkBidPosition(result) {
    const bidderPosition = result.data.me.bidder_position.position
    const status = result.data.me.bidder_position.status
    if (status === "WINNING") {
      this.showBidResult(true, "WINNING")
    } else if (status === "PENDING") {
      if (this.pollCount > MAX_POLL_ATTEMPTS) {
        this.showBidResult(false, "PROCESSING", "Bid Processing", messageForPollingTimeout)
      } else {
        // initiating new request here (vs setInterval) to make sure we wait for the previus calls to return before making a new one
        setTimeout(() => {
          queryForBidPosition(bidderPosition.id).then(this.checkBidPosition.bind(this))
        }, 1000)
        this.pollCount += 1
      }
    } else {
      this.showBidResult(
        false,
        status,
        result.data.me.bidder_position.message_header,
        result.data.me.bidder_position.message_description_md,
        result.data.me.bidder_position.position.suggested_next_bid
      )
    }
  }

  showBidResult(
    winning: boolean,
    status: string,
    messageHeader?: string,
    messageDescriptionMd?: string,
    suggestedNextBid?: Bid
  ) {
    this.props.navigator.push({
      component: BidResultScreen,
      title: "",
      passProps: {
        sale_artwork: this.props.sale_artwork,
        status,
{'fraction_non_alphanumeric': 0.10088794926004228, 'fraction_numerical': 0.0027061310782241013, 'mean_word_length': 2.9236894492368943, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7052814', 'n_tokens_mistral': 3575, 'n_tokens_neox': 3504, 'n_words': 737}