<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Navbar from './components/Navbar'
import { AppsActions } from './store/apps'
import { Box, Flex, Type } from 'blockstack-ui'
import { Hover } from 'react-powerplug'
import { Spinner } from '@components/ui/components/spinner'

const AppsSection = ({ title, apps, limit, category, ...rest }) => {
  let appsList = apps
  if (limit) {
    appsList = appsList.filter((app, i) => i <= limit - 1)
  }
  if (category) {
    appsList = appsList.filter(app => app.category === category)
  }
  return (
    <Box
      width={1}
      maxWidth={1100}
      mx="auto"
      p={4}
      mb={4}
      borderBottom="1px solid rgba(15,15,15,0.1)"
      {...rest}
    >
      <Box textAlign="center">
        <p className="app-section-heading">{title}</p>
      </Box>
      <Flex justifyContent="center" alignItems="center" flexWrap="wrap">
        {appsList.map(app => (
          <AppIcon key={app.name} {...app} />
        ))}
      </Flex>
    </Box>
  )
}

function mapStateToProps(state) {
  return {
    apps: state.apps,
    appListLastUpdated: state.apps.lastUpdated,
    api: state.settings.api,
    instanceIdentifier: state.apps.instanceIdentifier,
    instanceCreationDate: state.apps.instanceCreationDate
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(Object.assign({}, AppsActions), dispatch)
}

const Image = props => (
  <Box
    is="img"
    borderRadius="15px"
    boxShadow="0px 0px 35px 0px rgba(0, 0, 0, 0.15)"
    bg="rgba(44, 150, 255, 0.15)"
    {...props}
  />
)

const AppIcon = ({ website, imgixImageUrl, name }) => {
  return (
    <Hover>
      {({ bind, hovered }) => (
        <Box
          maxWidth={[80, 100, 100]}
          width={[1 / 3, 1 / 4, 1 / 4, 1 / 4, 1 / 5]}
          p={2}
          m={[2, 3, 4, 4]}
          transition="0.15s all ease-in-out"
          transform={hovered ? 'translateY(-5px)' : 'none'}
          color="blue.dark"
          {...bind}
        >
          <Box
            maxWidth="100%"
            width="100%"
            display="block"
            is="a"
            href={website}
            target="_blank"
            style={{
              textDecoration: 'none',
              color: 'black !important',
              textOverflow: 'ellipsis'
            }}
          >
            <Image
              maxWidth="100%"
              width="100%"
              display="block"
              src={imgixImageUrl}
              alt={name}
              mb={3}
            />
            <Box textAlign="center">
              <Type
                is="h3"
                fontSize={[2]}
                lineHeight={1.45}
                color="black !important"
                style={{
                  whiteSpace: 'nowrap',
                  overflow: 'hidden',
                  textOverflow: 'ellipsis',
                  maxWidth: '100%'
                }}
              >
                {name}
              </Type>
            </Box>
          </Box>
        </Box>
      )}
    </Hover>
  )
}

AppIcon.propTypes = {
  launchLink: PropTypes.string.isRequired,
  iconImage: PropTypes.string.isRequired,
  displayName: PropTypes.string.isRequired,
  storageRequired: PropTypes.bool.isRequired
}

class HomeScreenPage extends Component {
  static propTypes = {
    apps: PropTypes.object.isRequired,
    refreshAppList: PropTypes.func.isRequired,
    appListLastUpdated: PropTypes.number,
    api: PropTypes.object.isRequired,
    instanceIdentifier: PropTypes.string,
    instanceCreationDate: PropTypes.number
  }

  componentWillMount() {
    // Refresh apps list every 12 hours
    if (
      this.props.appListLastUpdated === undefined ||
      this.props.appListLastUpdated < Date.now() - 43200000
    ) {
      this.props.refreshAppList(
        this.props.api.browserServerUrl,
        this.props.instanceIdentifier,
        this.props.instanceCreationDate
      )
    }
  }

  render() {
    const loading = this.props.apps.loading && !this.props.apps.topApps.length
    return (
      <div>
        <Navbar hideBackToHomeLink activeTab="home" />
        <div className="home-screen">
          {loading ? (
            <Flex
              flexDirection="column"
              alignItems="center"
              width={1}
              justifyContent="center"
              p={4}
            >
              <Box width="48px" mx="auto">
                <Spinner color="black" size={48} />
              </Box>
              <Box py={4} textAlign="center">
                <Type fontWeight="500" fontSize={2} opacity={0.5}>
                  Fetching apps...
                </Type>
              </Box>
            </Flex>
          ) : (
            <Box maxWidth={1200} width="100%" mx="auto" p={[1, 2, 4]}>
              <AppsSection
                title="Top Ranked Apps"
                apps={this.props.apps.topApps}
                limit={15}
              />
              {this.props.apps.appsByCategory.map(category => (
                <AppsSection
                  title={category.label}
                  apps={category.apps}
                  limit={15}
                />
              ))}
            </Box>
          )}
        </div>
      </div>
    )
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(HomeScreenPage)<commit_msg>clean up code a bit, class isn't needed, propTypes, etc<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Navbar from './components/Navbar'
import { AppsActions } from './store/apps'
import { Box, Flex, Type } from 'blockstack-ui'
import { Hover } from 'react-powerplug'
import { Spinner } from '@components/ui/components/spinner'

const Loading = ({ ...rest }) => (
  <Flex
    flexDirection="column"
    alignItems="center"
    width={1}
    justifyContent="center"
    p={4}
    {...rest}
  >
    <Box width="48px" mx="auto">
      <Spinner color="black" size={48} />
    </Box>
    <Box py={4} textAlign="center">
      <Type fontWeight="500" fontSize={2} opacity={0.5}>
        Fetching apps...
      </Type>
    </Box>
  </Flex>
)

const Content = ({ topApps, allApps, ...rest }) => (
  <Box maxWidth={1200} width="100%" mx="auto" p={[1, 2, 4]} {...rest}>
    <AppsSection title="Top Ranked Apps" apps={topApps} limit={15} />
    {allApps.map(category => (
      <AppsSection title={category.label} apps={category.apps} limit={15} />
    ))}
  </Box>
)

const AppsSection = ({ title, apps, limit, category, ...rest }) => {
  let appsList = apps
  if (limit) {
    appsList = appsList.filter((app, i) => i <= limit - 1)
  }
  if (category) {
    appsList = appsList.filter(app => app.category === category)
  }
  return (
    <Box
      width={1}
      maxWidth={1100}
      mx="auto"
      p={4}
      mb={4}
      borderBottom="1px solid rgba(15,15,15,0.1)"
      {...rest}
    >
      <Box textAlign="center">
        <p className="app-section-heading">{title}</p>
      </Box>
      <Flex justifyContent="center" alignItems="center" flexWrap="wrap">
        {appsList.map(app => (
          <AppItem
            key={app.name}
            name={app.name}
            imgixImageUrl={app.imgixImageUrl}
            website={app.website}
          />
        ))}
      </Flex>
    </Box>
  )
}

const Image = props => (
  <Box
    is="img"
    borderRadius="15px"
    boxShadow="0px 0px 35px 0px rgba(0, 0, 0, 0.15)"
    bg="rgba(44, 150, 255, 0.15)"
    {...props}
  />
)

const AppItem = ({ website, imgixImageUrl, name }) => {
  return (
    <Hover>
      {({ bind, hovered }) => (
        <Box
          maxWidth={[80, 100, 100]}
          width={[1 / 3, 1 / 4, 1 / 4, 1 / 4, 1 / 5]}
          p={2}
          m={[2, 3, 4, 4]}
          transition="0.15s all ease-in-out"
          transform={hovered ? 'translateY(-5px)' : 'none'}
          color="blue.dark"
          {...bind}
        >
          <Box
            maxWidth="100%"
            width="100%"
            display="block"
            is="a"
            href={website}
            target="_blank"
            style={{
              textDecoration: 'none',
              color: 'black !important',
              textOverflow: 'ellipsis'
            }}
          >
            <Image
              maxWidth="100%"
              width="100%"
              display="block"
              src={imgixImageUrl}
              alt={name}
              mb={3}
            />
            <Box textAlign="center">
              <Type
                is="h3"
                fontSize={[2]}
                lineHeight={1.45}
                color="black !important"
                style={{
                  whiteSpace: 'nowrap',
                  overflow: 'hidden',
                  textOverflow: 'ellipsis',
                  maxWidth: '100%'
                }}
              >
                {name}
              </Type>
            </Box>
          </Box>
        </Box>
      )}
    </Hover>
  )
}

const HomeScreenPage = props => {
  const loading = props.apps && props.apps.loading && !props.apps.topApps.length
  return (
    <div>
      <Navbar hideBackToHomeLink activeTab="home" />
      <div className="home-screen">
        {loading ? (
          <Loading />
        ) : (
          <Content
            allApps={props.apps.appsByCategory}
            topApps={props.apps.topApps}
          />
        )}
      </div>
    </div>
  )
}

Content.propTypes = {
  topApps: PropTypes.array.isRequired,
  allApps: PropTypes.array.isRequired
}

AppsSection.propTypes = {
  title: PropTypes.string.isRequired,
  apps: PropTypes.array.isRequired,
  limit: PropTypes.number.isRequired,
  category: PropTypes.string.isRequired
}
AppItem.propTypes = {
  website: PropTypes.string.isRequired,
  imgixImageUrl: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired
}

HomeScreenPage.propTypes = {
  apps: PropTypes.object.isRequired,
  refreshAppList: PropTypes.func.isRequired,
  doFetchApps: PropTypes.func.isRequired,
  appListLastUpdated: PropTypes.number,
  api: PropTypes.object.isRequired,
  instanceIdentifier: PropTypes.string,
  instanceCreationDate: PropTypes.number
}

const mapStateToProps = state => ({
  apps: state.apps,
  appListLastUpdated: state.apps.lastUpdated,
  api: state.settings.api,
  instanceIdentifier: state.apps.instanceIdentifier,
  instanceCreationDate: state.apps.instanceCreationDate
})

const mapDispatchToProps = dispatch =>
  bindActionCreators({ ...AppsActions }, dispatch)

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(HomeScreenPage)
{'fraction_non_alphanumeric': 0.14184661206254653, 'fraction_numerical': 0.02252419955323902, 'mean_word_length': 1.829120589784097, 'pattern_counts': {'":': 0, '<': 86, '<?xml version=': 0, '>': 104, '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': '2192489', 'n_tokens_mistral': 3454, 'n_tokens_neox': 3263, 'n_words': 653}