<commit_before>/**
 @ngdoc overview
 @name  github.api

 @description
 Module which provides an interface for the GitHub Api.
 */
angular.module('github.api', ['restangular'])

.factory('GitHubRestangular',  function(Restangular) {
   return Restangular.withConfig(function(RestangularConfigurer) {
      RestangularConfigurer.setBaseUrl('https://api.github.com');
   });
})

/**
 @ngdoc service
 @name  github.api.gh

 @description
 Angular service `gh` which provides tools for accessing GitHub API's
 */

.service('gh', function gh($q, GitHubRestangular) {
   var
   response_extractors  = [],
   token;

   // Extend the API set the access token when we have one
   GitHubRestangular.setFullRequestInterceptor(function(element, operation, what,
                                                        url, headers, params) {
      var options = {
         element:   element,
         operation: operation,
         what:      what,
         url:       url,
         headers:   headers,
         params:    params
      };

      // Ensure that access token is always available
      _.defaults(options.params, {access_token: getAccessToken()});

      // Do not let the browser cache calls so that we always get the latest data
      // from GitHub.
      _.defaults(options.params, {'_cache_bust': new Date().getTime()});

      return options;
   });

   /**
    @ngdoc    function
    @name     addResponseExtractor
    @methodOf github.api.gh

    @description
    XXX

    @param {function} handler Function to invoke with the current response.
           The parameters for the function are the same as
           `Restangular.setResponseExtractor` handlers.
    */
   this.addResponseExtractor = function(handler) {
      response_extractors.push(handler);
   };

   // Configure Restangular to allows us to mutate the GitHub results as needed.
   GitHubRestangular.setResponseExtractor(function(response, operation, what,
                                                   url, headers, params) {
      var handler_args = arguments;

      _.each(response_extractors, function(handler) {
         var handler_res = handler.apply(null, handler_args);
         if (handler_res) {
            response = handler_res;
         }
      });

      // Return the response
      return response;
   });

   /**
    @ngdoc function
    @name  getAccessToken
    @methodOf github.api.gh

    @description
    Helper which will read the token from storage if they token is not
    currently set.

    @private

    @returns {string} The token or falsy if not found.
    */
   function getAccessToken() {
      return token;
   }

   /**
    @ngdoc    function
    @name     hasAccessToken
    @methodOf github.api.gh

    @description
    Predicate determining if the service has token to use when accessing
    resources on GitHub.
    */
   this.hasAccessToken = function() {
      return !!token;
   };

   /**
    @ngdoc    function
    @name     setAccessToken
    @methodOf github.api.gh

    @description
    Allows setting the API token to use when communicating with the GitHub API.

    @param {string} newToken The new token to use for the service.  Pass a falsy
           value to clear the token.
    */
   this.setAccessToken = function(newToken) {
      // Update our internal reference to the token
      token = newToken ? newToken : null;
   };

   this.getUserDetails = function() {
      return GitHubRestangular.one('user').get();
   };

   /**
    @ngdoc    function
    @name     listRepoAssignees
    @methodOf github.api.gh

    @description
    Returns the list if users which can be assigned to an issue

    @see http://developer.github.com/v3/issues/assignees/

    @param {string} owner The owner of the repository
    @param {string} repo  The name of the repository

    @returns {Promise} When resolved the list of all assignees
    */
   this.listRepoAssignees = function(owner, repo) {
      var url = ['repos', owner, repo, 'assignees'].join('/');
      return GitHubRestangular.one(url).get({per_page: 100});
   };

   /**
    @ngdoc    function
    @name     listRepoIssues
    @methodOf github.api.gh

    @description
    Returns the list if issues for a repository.

    - [ ] handle the case where there are more then one page of issues.

    @see http://developer.github.com/v3/issues/#list-issues-for-a-repository

    @param {string} owner             The owner of the repository
    @param {string} repo              The name of the repository
    @param {array.string} args.labels If set then the list of labels to return
           issues for.

    @returns {Promise} When resolved the list of all issues.
    */
   this.listRepoIssues = function(owner, repo, args) {
      // XXX look at how to get more then 100 uses
      // - paging
      var headers = {
         Accept: 'application/vnd.github.full+json'
      };
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues'].join('/'))
         .get(angular.extend(args || {}, {per_page: 100}), headers);
   };

   this.listRepos = function() {
      return GitHubRestangular
         .one(['user', 'repos'].join('/'))
         .get({per_page: 100});
   };

   this.listOrgs = function() {
      return GitHubRestangular
         .one(['user', 'orgs'].join('/'))
         .get({per_page: 100});
   };

   this.listOrgRepos = function(org) {
      return GitHubRestangular
         .one(['orgs', org, 'repos'].join('/'))
         .get({per_page: 100});
   };

   this.listAllOrgRepos = function() {
      var me = this;

      var d = $q.defer();

      // Get all of the users orginizations
      var p = this.listOrgs();

      // Once we have all of the orgs
      // - Loop over each one and retrieve the repos for the org
      p.then(function(orgs) {
         // Kick of a search for each org in parallel
         var all_orgs_repos = $q.all(_.map(orgs, function(org) {
            return me.listOrgRepos(org.login);
         }));

         // Fire our deferred when the calls finish
         all_orgs_repos.then(function(repos) {
            // Flatten all of the repos into one array
            var all_repos = _.flatten(repos);

            // Resolve the deferred
            d.resolve(all_repos);
         }, d.reject);
      });

      // Return our internal deferred so that anyone waiting on this method
      // gets all of the results.
      return d.promise;
   };

   this.getRepos = function(owner, repo) {
      return GitHubRestangular
         .one(['repos', owner, repo].join('/'))
         .get();
   };

   this.listAllRepos = function() {
      var me = this;

      return $q.all([
         // We want all of the users repos
         me.listRepos(),
         // And all of the repos their orgs have also
         me.listAllOrgRepos()
      ]).then(function(repos) {
         // Merge all of the repos together
         return _.flatten(repos);
      }, function onError() {
         console.error(arguments);
      });
   };

   this.extractRepoInfo = function(url) {
      var matches = /https:\/\/github\.com\/([^/]*)\/([^/]*)/.exec(url);
      if (!matches) {throw new Error('Url did match GitHub syntax');}
      return {owner: matches[1], repo: matches[2]};
   };

   this.getIssue = function(owner, repo, issueNumber) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues', issueNumber].join('/'))
         .get();
   };

   this.updateIssue = function(owner, repo, issueNumber, fields) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues', issueNumber].join('/'))
         .patch(fields);
   };

   /**
   * Create an issue.
   * owner, repo, and title are required.
   */
   this.createIssue = function(owner, repo, title, body) {
      return GitHubRestangular
         .all(['repos', owner, repo, 'issues'].join('/'))
         .post({title: title, body: body});
   };

   /**
   * Return list of all comments on the issue.
   */
   this.getIssueComments = function(owner, repo, issueNumber, options) {
      var headers = {};
      if (options.asHtml) {
         headers.Accept = 'application/vnd.github.html+json';
      }
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues', issueNumber, 'comments'].join('/'))
         .get({}, headers);
   };

   /**
   * Return list of all pull comments (the code review ones)
   */
   this.getPullComments = function(owner, repo, issueNumber, options) {
      var headers = {};
      if (options.asHtml) {
         headers.Accept = 'application/vnd.github.html+json';
      }
      return GitHubRestangular
         .one(['repos', owner, repo, 'pulls', issueNumber, 'comments'].join('/'))
         .get({}, headers);
   };

   /**
    @ngdoc    function
    @name     searchIssues
    @method   searchIssues
    @methodOf github.api.gh

    @description
    Allows for searching the issues the user has access based on certain fields.

    This API is in flux and as such the method may break in the furture.

    http://developer.github.com/v3/search/#search-issues

    @param {string} options.title When set only issues containing this string
           are returned.
    */
   this.searchIssues = function(options) {
      // XXX: Would this be faster since it is a single repos?
      //  - /legacy/issues/search/:owner/:repository/:state/:keyword
      //  http://developer.github.com/v3/search/legacy/#search-issues
      //
      // Build up the search string by looking at the supplied options
      var query = '';
      if (options.title) {
         query = query +options.title+'+in:title';
      }
      return GitHubRestangular.one('search/issues').get(
                                   {q: query},
                                   {'Accept': 'application/vnd.github.preview'});
   };

   this.getPull = function(owner, repo, issueNumber) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'pulls', issueNumber].join('/'))
         .get();
   };

   this.createPullFromIssue = function(owner, repo, issueNumber, base, head) {
      return GitHubRestangular
         .all(['repos', owner, repo, 'pulls'].join('/'))
         .post({issue: issueNumber, base: base, head: head });
   };

   this.getStatus = function(owner, repo, ref) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'statuses', ref].join('/'))
         .get();
   };

   this.listMilestones = function(owner, repo, args) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'milestones'].join('/'))
         .get(_.defaults({}, args, {state: 'open'}));
   };

   this.listLabels = function(owner, repo) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'labels'].join('/'))
         .get();
   };

   this.getBranches = function(owner, repo) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'branches'].join('/'))
         .get();
   };

   this.compareCommits = function(owner, repo, base, head) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'compare', base + '...' + head].join('/'))
         .get();
   };

   /**
    Helper function to convert GitHub's multiple string base 64 encoding into
    the actual string it represents.
    */
   function ghB64Decode(str) {
      // Decode each line seperatly and join them as a single string
      var lines = _.map(str.split('\n'), function(b64Str) {
         return window.atob(b64Str);
      });
      return lines.join('');
   }

   /**
    @ngdoc function
    @name  getFile
    @descrption
    Downloads a file from Github and returns the file contents as a string.

    @method   getFile
    @methodOf github.api.gh

    @param {string} owner The owner of the repository to download the file from
    @param {string} repo The name of the repository to download the file from
    */
   this.getFile = function(owner, repo, path) {
      return GitHubRestangular.one(['repos', owner, repo, 'contents', path].join('/')).get()
         .then(function(file) {
            return ghB64Decode(file.content);
         });
   };

});
<commit_msg>Remove cache busting so that we do not burn through as many API calls<commit_after>/**
 @ngdoc overview
 @name  github.api

 @description
 Module which provides an interface for the GitHub Api.
 */
angular.module('github.api', ['restangular'])

.factory('GitHubRestangular',  function(Restangular) {
   return Restangular.withConfig(function(RestangularConfigurer) {
      RestangularConfigurer.setBaseUrl('https://api.github.com');
   });
})

/**
 @ngdoc service
 @name  github.api.gh

 @description
 Angular service `gh` which provides tools for accessing GitHub API's
 */

.service('gh', function gh($q, GitHubRestangular) {
   var
   response_extractors  = [],
   token;

   // Extend the API set the access token when we have one
   GitHubRestangular.setFullRequestInterceptor(function(element, operation, what,
                                                        url, headers, params) {
      var options = {
         element:   element,
         operation: operation,
         what:      what,
         url:       url,
         headers:   headers,
         params:    params
      };

      // Ensure that access token is always available
      _.defaults(options.params, {access_token: getAccessToken()});

      return options;
   });

   /**
    @ngdoc    function
    @name     addResponseExtractor
    @methodOf github.api.gh

    @description
    XXX

    @param {function} handler Function to invoke with the current response.
           The parameters for the function are the same as
           `Restangular.setResponseExtractor` handlers.
    */
   this.addResponseExtractor = function(handler) {
      response_extractors.push(handler);
   };

   // Configure Restangular to allows us to mutate the GitHub results as needed.
   GitHubRestangular.setResponseExtractor(function(response, operation, what,
                                                   url, headers, params) {
      var handler_args = arguments;

      _.each(response_extractors, function(handler) {
         var handler_res = handler.apply(null, handler_args);
         if (handler_res) {
            response = handler_res;
         }
      });

      // Return the response
      return response;
   });

   /**
    @ngdoc function
    @name  getAccessToken
    @methodOf github.api.gh

    @description
    Helper which will read the token from storage if they token is not
    currently set.

    @private

    @returns {string} The token or falsy if not found.
    */
   function getAccessToken() {
      return token;
   }

   /**
    @ngdoc    function
    @name     hasAccessToken
    @methodOf github.api.gh

    @description
    Predicate determining if the service has token to use when accessing
    resources on GitHub.
    */
   this.hasAccessToken = function() {
      return !!token;
   };

   /**
    @ngdoc    function
    @name     setAccessToken
    @methodOf github.api.gh

    @description
    Allows setting the API token to use when communicating with the GitHub API.

    @param {string} newToken The new token to use for the service.  Pass a falsy
           value to clear the token.
    */
   this.setAccessToken = function(newToken) {
      // Update our internal reference to the token
      token = newToken ? newToken : null;
   };

   this.getUserDetails = function() {
      return GitHubRestangular.one('user').get();
   };

   /**
    @ngdoc    function
    @name     listRepoAssignees
    @methodOf github.api.gh

    @description
    Returns the list if users which can be assigned to an issue

    @see http://developer.github.com/v3/issues/assignees/

    @param {string} owner The owner of the repository
    @param {string} repo  The name of the repository

    @returns {Promise} When resolved the list of all assignees
    */
   this.listRepoAssignees = function(owner, repo) {
      var url = ['repos', owner, repo, 'assignees'].join('/');
      return GitHubRestangular.one(url).get({per_page: 100});
   };

   /**
    @ngdoc    function
    @name     listRepoIssues
    @methodOf github.api.gh

    @description
    Returns the list if issues for a repository.

    - [ ] handle the case where there are more then one page of issues.

    @see http://developer.github.com/v3/issues/#list-issues-for-a-repository

    @param {string} owner             The owner of the repository
    @param {string} repo              The name of the repository
    @param {array.string} args.labels If set then the list of labels to return
           issues for.

    @returns {Promise} When resolved the list of all issues.
    */
   this.listRepoIssues = function(owner, repo, args) {
      // XXX look at how to get more then 100 uses
      // - paging
      var headers = {
         Accept: 'application/vnd.github.full+json'
      };
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues'].join('/'))
         .get(angular.extend(args || {}, {per_page: 100}), headers);
   };

   this.listRepos = function() {
      return GitHubRestangular
         .one(['user', 'repos'].join('/'))
         .get({per_page: 100});
   };

   this.listOrgs = function() {
      return GitHubRestangular
         .one(['user', 'orgs'].join('/'))
         .get({per_page: 100});
   };

   this.listOrgRepos = function(org) {
      return GitHubRestangular
         .one(['orgs', org, 'repos'].join('/'))
         .get({per_page: 100});
   };

   this.listAllOrgRepos = function() {
      var me = this;

      var d = $q.defer();

      // Get all of the users orginizations
      var p = this.listOrgs();

      // Once we have all of the orgs
      // - Loop over each one and retrieve the repos for the org
      p.then(function(orgs) {
         // Kick of a search for each org in parallel
         var all_orgs_repos = $q.all(_.map(orgs, function(org) {
            return me.listOrgRepos(org.login);
         }));

         // Fire our deferred when the calls finish
         all_orgs_repos.then(function(repos) {
            // Flatten all of the repos into one array
            var all_repos = _.flatten(repos);

            // Resolve the deferred
            d.resolve(all_repos);
         }, d.reject);
      });

      // Return our internal deferred so that anyone waiting on this method
      // gets all of the results.
      return d.promise;
   };

   this.getRepos = function(owner, repo) {
      return GitHubRestangular
         .one(['repos', owner, repo].join('/'))
         .get();
   };

   this.listAllRepos = function() {
      var me = this;

      return $q.all([
         // We want all of the users repos
         me.listRepos(),
         // And all of the repos their orgs have also
         me.listAllOrgRepos()
      ]).then(function(repos) {
         // Merge all of the repos together
         return _.flatten(repos);
      }, function onError() {
         console.error(arguments);
      });
   };

   this.extractRepoInfo = function(url) {
      var matches = /https:\/\/github\.com\/([^/]*)\/([^/]*)/.exec(url);
      if (!matches) {throw new Error('Url did match GitHub syntax');}
      return {owner: matches[1], repo: matches[2]};
   };

   this.getIssue = function(owner, repo, issueNumber) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues', issueNumber].join('/'))
         .get();
   };

   this.updateIssue = function(owner, repo, issueNumber, fields) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues', issueNumber].join('/'))
         .patch(fields);
   };

   /**
   * Create an issue.
   * owner, repo, and title are required.
   */
   this.createIssue = function(owner, repo, title, body) {
      return GitHubRestangular
         .all(['repos', owner, repo, 'issues'].join('/'))
         .post({title: title, body: body});
   };

   /**
   * Return list of all comments on the issue.
   */
   this.getIssueComments = function(owner, repo, issueNumber, options) {
      var headers = {};
      if (options.asHtml) {
         headers.Accept = 'application/vnd.github.html+json';
      }
      return GitHubRestangular
         .one(['repos', owner, repo, 'issues', issueNumber, 'comments'].join('/'))
         .get({}, headers);
   };

   /**
   * Return list of all pull comments (the code review ones)
   */
   this.getPullComments = function(owner, repo, issueNumber, options) {
      var headers = {};
      if (options.asHtml) {
         headers.Accept = 'application/vnd.github.html+json';
      }
      return GitHubRestangular
         .one(['repos', owner, repo, 'pulls', issueNumber, 'comments'].join('/'))
         .get({}, headers);
   };

   /**
    @ngdoc    function
    @name     searchIssues
    @method   searchIssues
    @methodOf github.api.gh

    @description
    Allows for searching the issues the user has access based on certain fields.

    This API is in flux and as such the method may break in the furture.

    http://developer.github.com/v3/search/#search-issues

    @param {string} options.title When set only issues containing this string
           are returned.
    */
   this.searchIssues = function(options) {
      // XXX: Would this be faster since it is a single repos?
      //  - /legacy/issues/search/:owner/:repository/:state/:keyword
      //  http://developer.github.com/v3/search/legacy/#search-issues
      //
      // Build up the search string by looking at the supplied options
      var query = '';
      if (options.title) {
         query = query +options.title+'+in:title';
      }
      return GitHubRestangular.one('search/issues').get(
                                   {q: query},
                                   {'Accept': 'application/vnd.github.preview'});
   };

   this.getPull = function(owner, repo, issueNumber) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'pulls', issueNumber].join('/'))
         .get();
   };

   this.createPullFromIssue = function(owner, repo, issueNumber, base, head) {
      return GitHubRestangular
         .all(['repos', owner, repo, 'pulls'].join('/'))
         .post({issue: issueNumber, base: base, head: head });
   };

   this.getStatus = function(owner, repo, ref) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'statuses', ref].join('/'))
         .get();
   };

   this.listMilestones = function(owner, repo, args) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'milestones'].join('/'))
         .get(_.defaults({}, args, {state: 'open'}));
   };

   this.listLabels = function(owner, repo) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'labels'].join('/'))
         .get();
   };

   this.getBranches = function(owner, repo) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'branches'].join('/'))
         .get();
   };

   this.compareCommits = function(owner, repo, base, head) {
      return GitHubRestangular
         .one(['repos', owner, repo, 'compare', base + '...' + head].join('/'))
         .get();
   };

   /**
    Helper function to convert GitHub's multiple string base 64 encoding into
    the actual string it represents.
    */
   function ghB64Decode(str) {
      // Decode each line seperatly and join them as a single string
      var lines = _.map(str.split('\n'), function(b64Str) {
         return window.atob(b64Str);
      });
      return lines.join('');
   }

   /**
    @ngdoc function
    @name  getFile
    @descrption
    Downloads a file from Github and returns the file contents as a string.

    @method   getFile
    @methodOf github.api.gh

    @param {string} owner The owner of the repository to download the file from
    @param {string} repo The name of the repository to download the file from
    */
   this.getFile = function(owner, repo, path) {
      return GitHubRestangular.one(['repos', owner, repo, 'contents', path].join('/')).get()
         .then(function(file) {
            return ghB64Decode(file.content);
         });
   };

});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 Vanderbilt University, All rights reserved.
 *
 * Author: Tamas Kecskes
 */

define(['storage/client', 'storage/failsafe', 'storage/hashcheck', 'storage/cache', 'storage/commit', 'storage/log'], function (Client, Failsafe, Hashcheck, Cache, Commit, Log) {
  "use strict";
  function client(options) {
    //return  new Log(new Commit(new Cache(new Failsafe(new Client(options),options),options),options),options);
    return  new Commit(new Cache(new Failsafe(new Client(options), options), options), options);
  }

  return client;
});

<commit_msg>#162 src/common/storage/clientstorage.js format resolution.<commit_after>/*
 * Copyright (C) 2012 Vanderbilt University, All rights reserved.
 *
 * Author: Tamas Kecskes
 */

define(['storage/client', 'storage/failsafe', 'storage/hashcheck', 'storage/cache', 'storage/commit', 'storage/log'], function (Client,Failsafe,Hashcheck,Cache,Commit,Log) {
    "use strict";
    function client(options){
        //return  new Log(new Commit(new Cache(new Failsafe(new Client(options),options),options),options),options);
        return  new Commit(new Cache(new Failsafe(new Client(options),options),options),options);
    }

    return client;
});

<|endoftext|>


<commit_before>import React from 'react'
import { stateFromHTML } from 'draft-js';

// const sampleMarkup =
//   'Bold text, Italic text

' +
//   'Example link';

// const blocksFromHTML = convertFromHTML(sampleMarkup);
// const state = ContentState.createFromBlockArray(blocksFromHTML);

// this.state = {
//   editorState: EditorState.createWithContent(state),
// };

const IdeaCard = (props) => {
  // console.log(stateFromHTML(props.description))
  return (

{props.title}

                   {props.author}

                   Social Network

                   {props.join}

{props.description}

See Details →

  )
}

export default IdeaCard<commit_msg>no change<commit_after>import React from 'react'
// import { stateFromHTML } from 'draft-js';

// const sampleMarkup =
//   'Bold text, Italic text

' +
//   'Example link';

// const blocksFromHTML = convertFromHTML(sampleMarkup);
// const state = ContentState.createFromBlockArray(blocksFromHTML);

// this.state = {
//   editorState: EditorState.createWithContent(state),
// };

const IdeaCard = (props) => {
  // console.log(stateFromHTML(props.description))

  return (

{props.title}

                   {props.author}

                   Social Network

                   {props.join}

{props.description}

See Details →

  )
}

export default IdeaCard<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { TabNavigator } from 'react-navigation';
import { connect } from 'react-redux';
import {Platform, StatusBar} from 'react-native'
const DefaultTabBar = TabNavigator.Presets.Default.tabBarComponent

class TabBar extends Component
{
	render() {
		const { theme } = this.props
		const tintColor = theme.navigationTextTabBar ? theme.navigationTextTabBar : theme.navigationText
		return <DefaultTabBar
			{...this.props}
			activeTintColor={theme.navigationAccent}
			inactiveTintColor={tintColor}
			style={{
				backgroundColor: theme.navigation,
				paddingTop: Platform.OS === "ios" ? 0 : StatusBar.currentHeight
			}}
		/>
	}
}

const mapStateToProps = ({theme}) => ({
	theme
});

export default connect(mapStateToProps, null)(TabBar)
<commit_msg>removed styling that added padding to tabb ar<commit_after>import React, { Component } from 'react';
import { TabNavigator } from 'react-navigation';
import { connect } from 'react-redux';
import {Platform, StatusBar} from 'react-native'
const DefaultTabBar = TabNavigator.Presets.Default.tabBarComponent

class TabBar extends Component
{
	render() {
		const { theme } = this.props
		const tintColor = theme.navigationTextTabBar ? theme.navigationTextTabBar : theme.navigationText
		return <DefaultTabBar
			{...this.props}
			activeTintColor={theme.navigationAccent}
			inactiveTintColor={tintColor}
			style={{
				backgroundColor: theme.navigation,
			}}
		/>
	}
}

const mapStateToProps = ({theme}) => ({
	theme
});

export default connect(mapStateToProps, null)(TabBar)
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { Menu, Popover, Tooltip, Input, Badge } from 'antd';
import steemconnect from 'sc2-sdk';
import Avatar from '../Avatar';
import Notifications from './Notifications/Notifications';
import PopoverMenu, { PopoverMenuItem } from '../PopoverMenu/PopoverMenu';
import './Topnav.less';

const Topnav = ({
  intl,
  username,
  onNotificationClick,
  onSeeAllClick,
  onMenuItemClick,
  notifications,
}) => {
  let content;

  const notificationsCount =
    notifications && notifications.filter(notification => !notification.read).length;

  const next = window.location.pathname.length > 1 ? window.location.pathname : '';

  if (username) {
    content = (

        <Menu selectedKeys={[]} className="Topnav__menu-container__menu" mode="horizontal">
          <Menu.Item key="write">
            <Tooltip placement="bottom" title={intl.formatMessage({ id: 'write_post', defaultMessage: 'Write post' })}>
              <Link to="/write" className="Topnav__link">

              </Link>
            </Tooltip>
          </Menu.Item>
          <Menu.Item key="user" className="Topnav__item-user">
            <Link className="Topnav__user" to={`/@${username}`}>
              <Avatar username={username} size={36} />

                {username}

            </Link>
          </Menu.Item>
          <Menu.Item
            key="notifications"
            className="Topnav__item--badge"
          >
            <Tooltip placement="bottom" title={intl.formatMessage({ id: 'notifications', defaultMessage: 'Notifications' })}>
              <Popover
                placement="bottomRight"
                trigger="click"
                content={
                  <Notifications
                    notifications={notifications}
                    onClick={onNotificationClick}
                    onSeeAllClick={onSeeAllClick}
                  />
                }
                title={intl.formatMessage({ id: 'notifications', defaultMessage: 'Notifications' })}
              >

                  <Badge count={notificationsCount}>

                  </Badge>

              </Popover>
            </Tooltip>
          </Menu.Item>
          <Menu.Item key="more">
            <Popover
              placement="bottom"
              trigger="click"
              content={
                <PopoverMenu onSelect={onMenuItemClick}>
                  <PopoverMenuItem key="activity">
                    <FormattedMessage id="activity" defaultMessage="Activity" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="bookmarks">
                    <FormattedMessage id="bookmarks" defaultMessage="Bookmarks" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="drafts">
                    <FormattedMessage id="drafts" defaultMessage="Drafts" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="settings">
                    <FormattedMessage id="settings" defaultMessage="Settings" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="logout">
                    <FormattedMessage id="logout" defaultMessage="Logout" />
                  </PopoverMenuItem>
                </PopoverMenu>
              }
            >

            </Popover>
          </Menu.Item>
        </Menu>

    );
  } else {
    content = (

        <Menu className="Topnav__menu-container__menu" mode="horizontal">
          <Menu.Item key="signup">

              <FormattedMessage id="signup" defaultMessage="Sign up" />

          </Menu.Item>
          <Menu.Item key="divider" disabled>
            |
          </Menu.Item>
          <Menu.Item key="login">

              <FormattedMessage id="login" defaultMessage="Log in" />

          </Menu.Item>
        </Menu>

    );
  }

  return (

          <Link className="Topnav__brand" to="/">
            busy
          </Link>

            <Input
              onPressEnter={event => window.open(`https://www.google.com/search?q=${encodeURIComponent(`steemit:${event.target.value}`)}`)}
              placeholder={intl.formatMessage({ id: 'search_placeholder', defaultMessage: 'Search...' })}
            />

          {content}

  );
};

Topnav.propTypes = {
  intl: PropTypes.shape().isRequired,
  username: PropTypes.string,
  onNotificationClick: PropTypes.func,
  onSeeAllClick: PropTypes.func,
  onMenuItemClick: PropTypes.func,
  notifications: PropTypes.arrayOf(PropTypes.shape()),
};

Topnav.defaultProps = {
  username: undefined,
  onNotificationClick: () => {},
  onSeeAllClick: () => {},
  onMenuItemClick: () => {},
  notifications: [],
};

export default injectIntl(Topnav);
<commit_msg>Scope google search<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { Menu, Popover, Tooltip, Input, Badge } from 'antd';
import steemconnect from 'sc2-sdk';
import Avatar from '../Avatar';
import Notifications from './Notifications/Notifications';
import PopoverMenu, { PopoverMenuItem } from '../PopoverMenu/PopoverMenu';
import './Topnav.less';

const Topnav = ({
  intl,
  username,
  onNotificationClick,
  onSeeAllClick,
  onMenuItemClick,
  notifications,
}) => {
  let content;

  const notificationsCount =
    notifications && notifications.filter(notification => !notification.read).length;

  const next = window.location.pathname.length > 1 ? window.location.pathname : '';

  if (username) {
    content = (

        <Menu selectedKeys={[]} className="Topnav__menu-container__menu" mode="horizontal">
          <Menu.Item key="write">
            <Tooltip placement="bottom" title={intl.formatMessage({ id: 'write_post', defaultMessage: 'Write post' })}>
              <Link to="/write" className="Topnav__link">

              </Link>
            </Tooltip>
          </Menu.Item>
          <Menu.Item key="user" className="Topnav__item-user">
            <Link className="Topnav__user" to={`/@${username}`}>
              <Avatar username={username} size={36} />

                {username}

            </Link>
          </Menu.Item>
          <Menu.Item
            key="notifications"
            className="Topnav__item--badge"
          >
            <Tooltip placement="bottom" title={intl.formatMessage({ id: 'notifications', defaultMessage: 'Notifications' })}>
              <Popover
                placement="bottomRight"
                trigger="click"
                content={
                  <Notifications
                    notifications={notifications}
                    onClick={onNotificationClick}
                    onSeeAllClick={onSeeAllClick}
                  />
                }
                title={intl.formatMessage({ id: 'notifications', defaultMessage: 'Notifications' })}
              >

                  <Badge count={notificationsCount}>

                  </Badge>

              </Popover>
            </Tooltip>
          </Menu.Item>
          <Menu.Item key="more">
            <Popover
              placement="bottom"
              trigger="click"
              content={
                <PopoverMenu onSelect={onMenuItemClick}>
                  <PopoverMenuItem key="activity">
                    <FormattedMessage id="activity" defaultMessage="Activity" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="bookmarks">
                    <FormattedMessage id="bookmarks" defaultMessage="Bookmarks" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="drafts">
                    <FormattedMessage id="drafts" defaultMessage="Drafts" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="settings">
                    <FormattedMessage id="settings" defaultMessage="Settings" />
                  </PopoverMenuItem>
                  <PopoverMenuItem key="logout">
                    <FormattedMessage id="logout" defaultMessage="Logout" />
                  </PopoverMenuItem>
                </PopoverMenu>
              }
            >

            </Popover>
          </Menu.Item>
        </Menu>

    );
  } else {
    content = (

        <Menu className="Topnav__menu-container__menu" mode="horizontal">
          <Menu.Item key="signup">

              <FormattedMessage id="signup" defaultMessage="Sign up" />

          </Menu.Item>
          <Menu.Item key="divider" disabled>
            |
          </Menu.Item>
          <Menu.Item key="login">

              <FormattedMessage id="login" defaultMessage="Log in" />

          </Menu.Item>
        </Menu>

    );
  }

  return (

          <Link className="Topnav__brand" to="/">
            busy
          </Link>

            <Input
              onPressEnter={event => window.open(`https://www.google.com/search?q=${encodeURIComponent(`site:steemit.com: ${event.target.value}`)}`)}
              placeholder={intl.formatMessage({ id: 'search_placeholder', defaultMessage: 'Search...' })}
            />

          {content}

  );
};

Topnav.propTypes = {
  intl: PropTypes.shape().isRequired,
  username: PropTypes.string,
  onNotificationClick: PropTypes.func,
  onSeeAllClick: PropTypes.func,
  onMenuItemClick: PropTypes.func,
  notifications: PropTypes.arrayOf(PropTypes.shape()),
};

Topnav.defaultProps = {
  username: undefined,
  onNotificationClick: () => {},
  onSeeAllClick: () => {},
  onMenuItemClick: () => {},
  notifications: [],
};

export default injectIntl(Topnav);
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import List from './List';
import ListItemAudio from './ListItemAudio';
import ListItem from './ListItem';
import ListItemTag from './ListItemTag';
import Section from './Section';

const clientId = process.env.GATSBY_SOUNDCLOUD_CLIENT_ID;

const SectionSoundcloud = ({ favorites, favoritesCount }) => (
  <Section
    linkUrl="https://soundcloud.com/cadejscroggins/likes"
    title={`${favoritesCount} Favorites on Soundcloud`}
  >
    <List>
      {favorites.map(
        ({ node: { artwork_url, stream_url, title, user } }, i) => (
          <ListItem
            backgroundImg={
              artwork_url ? artwork_url.replace('large', 't500x500') : null
            }
            key={i}
            overlay
          >
            <ListItemTag>{user.username}</ListItemTag>
            {title}
            <ListItemAudio streamUrl={`${stream_url}?client_id=${clientId}`} />
          </ListItem>
        )
      )}
    </List>
  </Section>
);

SectionSoundcloud.propTypes = {
  favorites: PropTypes.array,
  favoritesCount: PropTypes.number,
};

export default SectionSoundcloud;
<commit_msg>Soundcloud -> SoundCloud<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import List from './List';
import ListItemAudio from './ListItemAudio';
import ListItem from './ListItem';
import ListItemTag from './ListItemTag';
import Section from './Section';

const clientId = process.env.GATSBY_SOUNDCLOUD_CLIENT_ID;

const SectionSoundcloud = ({ favorites, favoritesCount }) => (
  <Section
    linkUrl="https://soundcloud.com/cadejscroggins/likes"
    title={`${favoritesCount} Favorites on SoundCloud`}
  >
    <List>
      {favorites.map(
        ({ node: { artwork_url, stream_url, title, user } }, i) => (
          <ListItem
            backgroundImg={
              artwork_url ? artwork_url.replace('large', 't500x500') : null
            }
            key={i}
            overlay
          >
            <ListItemTag>{user.username}</ListItemTag>
            {title}
            <ListItemAudio streamUrl={`${stream_url}?client_id=${clientId}`} />
          </ListItem>
        )
      )}
    </List>
  </Section>
);

SectionSoundcloud.propTypes = {
  favorites: PropTypes.array,
  favoritesCount: PropTypes.number,
};

export default SectionSoundcloud;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  Carousel as ReactstrapCarousel,
  CarouselItem,
  CarouselControl,
  CarouselIndicators
} from 'reactstrap';

class Carousel extends Component {
  static propTypes = {
    items: PropTypes.array.isRequired
  };

  state = {
    activeIndex: 0,
    animating: false
  };

  onExiting() {
    this.setState({ animating: true });
  }

  onExited() {
    this.setState({ animating: false });
  }

  next(items) {
    if (this.state.animating) {
      return;
    }

    const nextIndex =
      this.state.activeIndex === items.length - 1
        ? 0
        : this.state.activeIndex + 1;
    this.setState({ activeIndex: nextIndex });
  }

  previous(items) {
    if (this.state.animating) {
      return;
    }

    const nextIndex =
      this.state.activeIndex === 0
        ? items.length - 1
        : this.state.activeIndex - 1;
    this.setState({ activeIndex: nextIndex });
  }

  goToIndex(newIndex) {
    if (this.state.animating) {
      return;
    }

    this.setState({ activeIndex: newIndex });
  }

  render() {
    const { activeIndex } = this.state;
    const { items } = this.props;

    return (
      <ReactstrapCarousel
        activeIndex={activeIndex}
        next={() => this.next(items)}
        previous={() => this.previous(items)}
        interval={false}
      >
        <CarouselIndicators
          items={items}
          activeIndex={activeIndex}
          onClickHandler={newIndex => this.goToIndex(newIndex)}
        />

        {items.map(item => (
          <CarouselItem
            onExiting={() => this.onExiting}
            onExited={() => this.onExited}
            key={item.src}
            src={item.src}
            altText={item.altText}
            cssModule={{ 'img-fluid': 'img-fluid ' + item.imageClassName }}
          >

{item.title}

{item.caption}

          </CarouselItem>
        ))}

        <CarouselControl
          direction="prev"
          directionText="Previous"
          onClickHandler={() => this.previous(items)}
        />

        <CarouselControl
          direction="next"
          directionText="Next"
          onClickHandler={() => this.next(items)}
        />
      </ReactstrapCarousel>
    );
  }
}

export default Carousel;
<commit_msg>bug fix for carousel item animation callbacks<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  Carousel as ReactstrapCarousel,
  CarouselItem,
  CarouselControl,
  CarouselIndicators
} from 'reactstrap';

class Carousel extends Component {
  static propTypes = {
    items: PropTypes.array.isRequired
  };

  state = {
    activeIndex: 0,
    animating: false
  };

  onExiting() {
    this.setState({ animating: true });
  }

  onExited() {
    this.setState({ animating: false });
  }

  next(items) {
    if (this.state.animating) {
      return;
    }

    const nextIndex =
      this.state.activeIndex === items.length - 1
        ? 0
        : this.state.activeIndex + 1;
    this.setState({ activeIndex: nextIndex });
  }

  previous(items) {
    if (this.state.animating) {
      return;
    }

    const nextIndex =
      this.state.activeIndex === 0
        ? items.length - 1
        : this.state.activeIndex - 1;
    this.setState({ activeIndex: nextIndex });
  }

  goToIndex(newIndex) {
    if (this.state.animating) {
      return;
    }

    this.setState({ activeIndex: newIndex });
  }

  render() {
    const { activeIndex } = this.state;
    const { items } = this.props;

    return (
      <ReactstrapCarousel
        activeIndex={activeIndex}
        next={() => this.next(items)}
        previous={() => this.previous(items)}
        interval={false}
      >
        <CarouselIndicators
          items={items}
          activeIndex={activeIndex}
          onClickHandler={newIndex => this.goToIndex(newIndex)}
        />

        {items.map(item => (
          <CarouselItem
            onExiting={() => this.onExiting()}
            onExited={() => this.onExited()}
            key={item.src}
            src={item.src}
            altText={item.altText}
            cssModule={{ 'img-fluid': 'img-fluid ' + item.imageClassName }}
          >

{item.title}

{item.caption}

          </CarouselItem>
        ))}

        <CarouselControl
          direction="prev"
          directionText="Previous"
          onClickHandler={() => this.previous(items)}
        />

        <CarouselControl
          direction="next"
          directionText="Next"
          onClickHandler={() => this.next(items)}
        />
      </ReactstrapCarousel>
    );
  }
}

export default Carousel;
<|endoftext|>


<commit_before><commit_msg>Refactoring<commit_after><|endoftext|>


<commit_before>import React, { Component } from 'react'
import {
  Box,
  LargeButton,
  Button,
  Flex,
  Heading,
  Icon,
  Input,
  Label,
  Text
} from '@hackclub/design-system'
import { CLIENT_URL, PUBLIC_STRIPE_KEY } from 'constants'
import { toNumber } from 'lodash'
import api from 'api'

const Secure = Flex.extend`
  position: absolute;
  top: 0;
  right: 0;
  align-items: center;
  justify-content: flex-end;
  text-align: right;
`

const amounts = [10, 20, 40, 100]

const AmountsGrid = Box.extend`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: ${({ theme }) => theme.space[3]}px;
  input[type='radio'] {
    display: none;
    &:checked + label {
      background-color: ${({ theme }) => theme.colors.blue[7]} !important;
    }
  }
  input[type='number'] {
    text-align: center;
    border-radius: ${({ theme }) => theme.pill};
    grid-column: 2 / span 2;
  }
`

const Amount = Button.withComponent('label').extend`
  border-radius: ${({ theme }) => theme.pill};
`

const Option = ({ amount, ...props }) => [
  <input
    type="radio"
    id={`amount[${amount}]`}
    name="amount"
    value={amount}
    key={`input[${amount}]`}
    {...props}
  />,
  <Amount bg="info" htmlFor={`amount[${amount}]`} key={`label[${amount}]`}>
    <Text.span>${amount}</Text.span>
  </Amount>
]

const Other = Input.extend`
  color: ${({ theme }) => theme.colors.black};
  ::placeholder {
    color: ${({ theme }) => theme.colors.muted};
  }
`

class DonateForm extends Component {
  state = {
    loading: true,
    stripeLoading: true,
    amount: 20,
    recurring: true
  }

  componentWillUnmount() {
    if (this.stripeHandler) {
      this.stripeHandler.close()
    }
  }

  render() {
    const { custom, recurring, amount } = this.state
    return (
      <Box align="center" style={{ position: 'relative' }}>
        <Box bg="primary" color="white" mx={[-3, -4]} pb={3} mb={3}>
          <Secure p={2} mr={[-3, -4]}>
            <Text f={0} color="red.1" caps bold>
              Secure
            </Text>
            <Icon size={16} name="lock" color="red.1" ml={2} />
          </Secure>
          <Heading.h2 mt={[-3, -4]} pt={3} px={3} f={5}>
            Become a patron
          </Heading.h2>
          <Text color="snow" mt={1} f={1}>
            Just $3 supports a student for a month
          </Text>
        </Box>
        <Flex align="center" justify="center" color="black">
          <Text f={3} bold>
            Select an amount
          </Text>
          <Text.span mx={2} children="—" />
          <Label>
            <input
              name="recurring"
              type="checkbox"
              style={{
                WebkitAppearance: 'checkbox',
                MozAppearance: 'checkbox',
                appearance: 'checkbox'
              }}
              checked={recurring}
              onChange={this.handleRecurringChange}
            />
            <Text.span f={2} ml={1}>
              monthly?
            </Text.span>
          </Label>
        </Flex>
        <AmountsGrid w={1} mt={3} mb={4}>
          {amounts.map(amount => (
            <Option
              amount={amount}
              onChange={e => this.handleAmountChange(amount)}
              key={amount}
            />
          ))}
          <Other
            type="number"
            placeholder="Other"
            onChange={e => this.handleAmountChange(e.target.value)}
          />
        </AmountsGrid>
        <Text color="slate" mb={3} f={1}>
          Our recommended donation is $20/month
        </Text>
        <LargeButton
          onClick={e => this.startStripe(e)}
          children={this.buttonText()}
          w={1}
        />
      </Box>
    )
  }

  loadStripe = onload => {
    if (!window.StripeCheckout) {
      const script = document.createElement('script')
      script.onload = () => {
        onload()
      }
      script.src = 'https://checkout.stripe.com/checkout.js'
      document.head.appendChild(script)
    } else {
      onload()
    }
  }

  startStripe = e => {
    this.loadStripe(() => {
      this.stripeHandler = window.StripeCheckout.configure({
        key: PUBLIC_STRIPE_KEY,
        image: `${CLIENT_URL}/twitter-avatar.png`,
        locale: 'auto',
        amount: this.amountInCents(),
        token: this.handleToken
      })

      this.setState({
        stripeLoading: false,
        // loading needs to be explicitly set false so component will render in 'loaded' state.
        loading: false
      })

      this.onStripeUpdate(e)
    })
  }

  onStripeUpdate = e => {
    this.stripeHandler.open({
      name: 'Hack Club',
      description: this.state.recurring
        ? 'Monthly contribution to Hack Club'
        : 'One-time contribution to Hack Club',
      panelLabel: 'Donate',
      allowRememberMe: false
    })

    e.preventDefault()
  }

  handleToken = token => {
    this.setState({ loading: true })

    const data = new FormData()

    data.append('stripe_email', token.email)
    data.append('stripe_token', token.id)
    data.append('recurring', this.state.recurring)
    data.append('amount', this.amountInCents())

    this.setState({ status: 'loading' })

    api
      .post('v1/donations', { data })
      .then(data => {
        if (data.donation_successful) {
          this.setState({ status: 'done' })
        } else {
          this.setState({ status: 'error' })
        }
      })
      .catch(() => {
        this.setState({ status: 'error' })
      })
  }

  handleAmountChange = a => {
    const amount = toNumber(a || 1)
    this.setState({ amount })
  }

  handleRecurringChange = v => {
    this.setState({ recurring: v.target.checked })
  }

  buttonText() {
    switch (this.state.status) {
      case 'done':
        return 'Thank you 😊'
      case 'loading':
        return 'One moment…'
      case 'error':
        return 'Something went wrong!'
      default:
        const msg = `Donate $${this.state.amount}`
        return this.state.recurring ? `${msg} a month` : msg
    }
  }
  setAmount = amount => v => {
    this.setState({ amount, custom: false })
  }
  amountInCents = () => this.state.amount * 100
}
export default DonateForm
<commit_msg>Improve padding of donation form<commit_after>import React, { Component } from 'react'
import {
  Box,
  LargeButton,
  Button,
  Flex,
  Heading,
  Icon,
  Input,
  Label,
  Text
} from '@hackclub/design-system'
import { CLIENT_URL, PUBLIC_STRIPE_KEY } from 'constants'
import { toNumber } from 'lodash'
import api from 'api'

const Secure = Flex.extend`
  position: absolute;
  top: 0;
  right: 0;
  align-items: center;
  justify-content: flex-end;
  text-align: right;
`

const amounts = [10, 20, 40, 100]

const AmountsGrid = Box.extend`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: ${({ theme }) => theme.space[3]}px;
  input[type='radio'] {
    display: none;
    &:checked + label {
      background-color: ${({ theme }) => theme.colors.blue[7]} !important;
    }
  }
  input[type='number'] {
    text-align: center;
    border-radius: ${({ theme }) => theme.pill};
    grid-column: 2 / span 2;
  }
`

const Amount = Button.withComponent('label').extend`
  border-radius: ${({ theme }) => theme.pill};
`

const Option = ({ amount, ...props }) => [
  <input
    type="radio"
    id={`amount[${amount}]`}
    name="amount"
    value={amount}
    key={`input[${amount}]`}
    {...props}
  />,
  <Amount bg="info" htmlFor={`amount[${amount}]`} key={`label[${amount}]`}>
    <Text.span>${amount}</Text.span>
  </Amount>
]

const Other = Input.extend`
  color: ${({ theme }) => theme.colors.black};
  ::placeholder {
    color: ${({ theme }) => theme.colors.muted};
  }
`

class DonateForm extends Component {
  state = {
    loading: true,
    stripeLoading: true,
    amount: 20,
    recurring: true
  }

  componentWillUnmount() {
    if (this.stripeHandler) {
      this.stripeHandler.close()
    }
  }

  render() {
    const { custom, recurring, amount } = this.state
    return (
      <Box align="center" style={{ position: 'relative' }}>
        <Box bg="primary" color="white" mx={[-3, -4]} pb={4} mb={3}>
          <Secure p={2} mr={[-3, -4]}>
            <Text f={0} color="red.1" caps bold>
              Secure
            </Text>
            <Icon size={16} name="lock" color="red.1" ml={2} />
          </Secure>
          <Heading.h2 mt={[-3, -4]} pt={4} px={3} f={5}>
            Become a patron
          </Heading.h2>
          <Text color="snow" mt={1} f={1}>
            Just $3 supports a student for a month
          </Text>
        </Box>
        <Flex align="center" justify="center" color="black">
          <Text f={3} bold>
            Select an amount
          </Text>
          <Text.span mx={2} children="—" />
          <Label>
            <input
              name="recurring"
              type="checkbox"
              style={{
                WebkitAppearance: 'checkbox',
                MozAppearance: 'checkbox',
                appearance: 'checkbox'
              }}
              checked={recurring}
              onChange={this.handleRecurringChange}
            />
            <Text.span f={2} ml={1}>
              monthly?
            </Text.span>
          </Label>
        </Flex>
        <AmountsGrid w={1} mt={3} mb={4}>
          {amounts.map(amount => (
            <Option
              amount={amount}
              onChange={e => this.handleAmountChange(amount)}
              key={amount}
            />
          ))}
          <Other
            type="number"
            placeholder="Other"
            onChange={e => this.handleAmountChange(e.target.value)}
          />
        </AmountsGrid>
        <Text color="slate" mb={3} f={1}>
          Our recommended donation is $20/month
        </Text>
        <LargeButton
          onClick={e => this.startStripe(e)}
          children={this.buttonText()}
          w={1}
        />
      </Box>
    )
  }

  loadStripe = onload => {
    if (!window.StripeCheckout) {
      const script = document.createElement('script')
      script.onload = () => {
        onload()
      }
      script.src = 'https://checkout.stripe.com/checkout.js'
      document.head.appendChild(script)
    } else {
      onload()
    }
  }

  startStripe = e => {
    this.loadStripe(() => {
      this.stripeHandler = window.StripeCheckout.configure({
        key: PUBLIC_STRIPE_KEY,
        image: `${CLIENT_URL}/twitter-avatar.png`,
        locale: 'auto',
        amount: this.amountInCents(),
        token: this.handleToken
      })

      this.setState({
        stripeLoading: false,
        // loading needs to be explicitly set false so component will render in 'loaded' state.
        loading: false
      })

      this.onStripeUpdate(e)
    })
  }

  onStripeUpdate = e => {
    this.stripeHandler.open({
      name: 'Hack Club',
      description: this.state.recurring
        ? 'Monthly contribution to Hack Club'
        : 'One-time contribution to Hack Club',
      panelLabel: 'Donate',
      allowRememberMe: false
    })

    e.preventDefault()
  }

  handleToken = token => {
    this.setState({ loading: true })

    const data = new FormData()

    data.append('stripe_email', token.email)
    data.append('stripe_token', token.id)
    data.append('recurring', this.state.recurring)
    data.append('amount', this.amountInCents())

    this.setState({ status: 'loading' })

    api
      .post('v1/donations', { data })
      .then(data => {
        if (data.donation_successful) {
          this.setState({ status: 'done' })
        } else {
          this.setState({ status: 'error' })
        }
      })
      .catch(() => {
        this.setState({ status: 'error' })
      })
  }

  handleAmountChange = a => {
    const amount = toNumber(a || 1)
    this.setState({ amount })
  }

  handleRecurringChange = v => {
    this.setState({ recurring: v.target.checked })
  }

  buttonText() {
    switch (this.state.status) {
      case 'done':
        return 'Thank you 😊'
      case 'loading':
        return 'One moment…'
      case 'error':
        return 'Something went wrong!'
      default:
        const msg = `Donate $${this.state.amount}`
        return this.state.recurring ? `${msg} a month` : msg
    }
  }
  setAmount = amount => v => {
    this.setState({ amount, custom: false })
  }
  amountInCents = () => this.state.amount * 100
}
export default DonateForm
<|endoftext|>


<commit_before><commit_msg>Fixed an issue where EditGrid restores focus on the wrong row after deleting some rows (#4590)<commit_after><|endoftext|>


<commit_before>import _ from 'lodash';
import NestedComponent from '../_classes/nested/NestedComponent';
import Component from '../_classes/component/Component';
import Components from '../Components';

export default class EditGridComponent extends NestedComponent {
  static schema(...extend) {
    return NestedComponent.schema({
      type: 'editgrid',
      label: 'Edit Grid',
      key: 'editGrid',
      clearOnHide: true,
      input: true,
      tree: true,
      defaultOpen: false,
      components: [],
      inlineEdit: false,
      templates: {
        header: this.defaultHeaderTemplate,
        row: this.defaultRowTemplate,
        footer: ''
      }
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Edit Grid',
      icon: 'tasks',
      group: 'data',
      documentation: 'http://help.form.io/userguide/#editgrid',
      weight: 40,
      schema: EditGridComponent.schema()
    };
  }

  static get defaultHeaderTemplate() {
    return  `

  {% util.eachComponent(components, function(component) { %}

{{ component.label }}

  {% }) %}

`;
  }

  static get defaultRowTemplate() {
    return `

  {% util.eachComponent(components, function(component) { %}

      {{ getView(component, row[component.key]) }}

  {% }) %}
  {% if (!instance.options.readOnly) { %}

        <button class="btn btn-default btn-sm editRow">Edit</button>
        <button class="btn btn-danger btn-sm removeRow">Delete</button>

  {% } %}

`;
  }

  get defaultSchema() {
    return EditGridComponent.schema();
  }

  get emptyValue() {
    return [];
  }

  get editgridKey() {
    return `editgrid-${this.key}`;
  }

  constructor(...args) {
    super(...args);
    this.type = 'editgrid';
  }

  init() {
    this.components = this.components || [];
    const dataValue = this.dataValue || [];
    this.editRows = dataValue.map((row) => ({
      isOpen: false,
      data: row,
    }));
    // In builder we need one row so the components will show up.
    if (this.options.attachMode === 'builder') {
      this.addRow();
    }
  }

  render() {
    const dataValue = this.dataValue || [];
    return super.render(this.renderTemplate('editgrid', {
      editgridKey: this.editgridKey,
      header: this.renderString(_.get(this.component, 'templates.header'), {
        components: this.component.components,
        value: dataValue
      }),
      footer: this.renderString(_.get(this.component, 'templates.footer'), {
        components: this.component.components,
        value: dataValue
      }),
      rows: this.editRows.map(this.renderRow.bind(this)),
      openRows: this.editRows.map(row => row.isOpen),
      errors: this.editRows.map(row => row.error)
    }));
  }

  attach(element) {
    this.loadRefs(element, {
      [`${this.editgridKey}-addRow`]: 'multiple',
      [`${this.editgridKey}-removeRow`]: 'multiple',
      [`${this.editgridKey}-saveRow`]: 'multiple',
      [`${this.editgridKey}-cancelRow`]: 'multiple',
      [this.editgridKey]: 'multiple',
    });

    this.refs[`${this.editgridKey}-addRow`].forEach((addButton) => {
      this.addEventListener(addButton, 'click', this.addRow.bind(this));
    });

    let openRowCount = 0;
    this.refs[this.editgridKey].forEach((row, rowIndex) => {
      if (this.editRows[rowIndex].isOpen) {
        this.attachComponents(row, this.editRows[rowIndex].components);
        this.addEventListener(this.refs[`${this.editgridKey}-saveRow`][openRowCount], 'click', this.saveRow.bind(this, rowIndex));
        this.addEventListener(this.refs[`${this.editgridKey}-cancelRow`][openRowCount], 'click', this.cancelRow.bind(this, rowIndex));
        openRowCount++;
      }
      else {
        // Attach edit and remove button events.
        [
          {
            class: 'removeRow',
            event: 'click',
            action: this.removeRow.bind(this, rowIndex)
          },
          {
            class: 'editRow',
            event: 'click',
            action: this.editRow.bind(this, rowIndex)
          },
        ].forEach(action => {
          const elements = row.getElementsByClassName(action.class);
          Array.prototype.forEach.call(elements, element => {
            element.addEventListener(action.event, action.action);
          });
        });
      }
    });

    // Add open class to the element if any edit grid row is open
    if (openRowCount) {
      this.addClass(this.refs.component, `formio-component-${this.component.type}-row-open`);
    }
    else {
      this.removeClass(this.refs.component, `formio-component-${this.component.type}-row-open`);
    }

    super.attach(element);
  }

  renderRow(row, rowIndex) {
    const dataValue = this.dataValue || [];
    if (row.isOpen) {
      return this.renderComponents(row.components);
    }
    else {
      return this.renderString(_.get(this.component, 'templates.row', EditGridComponent.defaultRowTemplate),
        {
          row: dataValue[rowIndex],
          rowIndex,
          components: this.component.components,
          getView: (component, data) => Components.create(component, this.options, data, true).getView(data)
        });
    }
  }

  checkData(data, flags = {}, index) {
    let valid = true;
    if (flags.noCheck) {
      return;
    }

    // Update the value.
    let changed = this.updateValue({
      noUpdateEvent: true
    });

    const editRow = this.editRows[index];

    // Iterate through all components and check conditions, and calculate values.
    editRow.components.forEach(comp => {
      changed |= comp.calculateValue(data, {
        noUpdateEvent: true
      });
      comp.checkConditions(data);
      if (!flags.noValidate) {
        valid &= comp.checkValidity(data, !editRow.isOpen);
      }
    });

    valid &= (this.validateRow(index) === true);

    // Trigger the change if the values changed.
    if (changed) {
      this.triggerChange(flags);
    }

    // Return if the value is valid.
    return valid;
  }

  addRow() {
    if (this.options.readOnly) {
      return;
    }
    const dataObj = {};
    this.editRows.push({
      components: [],
      isOpen: true,
      data: dataObj
    });
    if (this.component.inlineEdit) {
      this.dataValue.push(dataObj);
    }
    const rowIndex = this.editRows.length - 1;
    const editRow = this.editRows[rowIndex];
    this.emit('editGridAddRow', {
      component: this.component,
      row: editRow
    });
    editRow.components = this.createRowComponents(editRow.data, rowIndex);
    this.redraw();
  }

  editRow(rowIndex) {
    const dataValue = this.dataValue || [];
    const editRow = this.editRows[rowIndex];
    editRow.dirty = false;
    editRow.isOpen = true;
    editRow.editing = true;
    const dataSnapshot = dataValue[rowIndex] ? _.cloneDeep(dataValue[rowIndex]) : {};
    if (this.component.inlineEdit) {
      editRow.backup = dataSnapshot;
    }
    else {
      editRow.data = dataSnapshot;
    }
    editRow.components = this.createRowComponents(editRow.data, rowIndex);
    this.redraw();
  }

  clearErrors(rowIndex) {
    const editRow = this.editRows[rowIndex];
    if (editRow && Array.isArray(editRow.components)) {
      editRow.components.forEach(comp => {
        comp.setPristine(true);
        comp.setCustomValidity('');
      });
    }
  }

  cancelRow(rowIndex) {
    const editRow = this.editRows[rowIndex];
    if (this.options.readOnly) {
      editRow.dirty = false;
      editRow.isOpen = false;
      this.redraw();
      return;
    }
    if (editRow.editing) {
      const dataValue = this.dataValue || [];
      editRow.dirty = false;
      editRow.isOpen = false;
      if (this.component.inlineEdit) {
        this.dataValue[rowIndex] = editRow.backup;
      }
      editRow.data = dataValue[rowIndex] || {};
      this.clearErrors(rowIndex);
    }
    else {
      this.clearErrors(rowIndex);
      if (this.component.inlineEdit) {
        this.splice(rowIndex);
      }
      this.editRows.splice(rowIndex, 1);
    }

    this.checkValidity(this.data, true);
    this.redraw();
  }

  saveRow(rowIndex) {
    const editRow = this.editRows[rowIndex];
    if (this.options.readOnly) {
      editRow.dirty = false;
      editRow.isOpen = false;
      this.redraw();
      return;
    }
    editRow.dirty = true;
    if (this.validateRow(rowIndex) !== true) {
      return;
    }

    if (!this.component.inlineEdit) {
      const dataValue = this.dataValue || [];
      if (editRow.editing) {
        dataValue[rowIndex] = editRow.data;
      }
      else {
        // Insert this row into its proper place.
        const newIndex = dataValue.length;
        dataValue.push(editRow.data);
        this.editRows.splice(rowIndex, 1);
        this.editRows.splice(newIndex, 0, editRow);
        rowIndex = newIndex;
      }
    }
    editRow.dirty = false;
    editRow.isOpen = false;
    this.updateValue();
    this.triggerChange();
    this.checkValidity(this.data, true);
    this.redraw();
  }

  removeRow(rowIndex) {
    if (this.options.readOnly) {
      return;
    }
    this.splice(rowIndex);
    this.editRows.splice(rowIndex, 1);
    this.updateValue();
    this.triggerChange();
    this.checkValidity(this.data, true);
    this.redraw();
  }

  createRowComponents(row, rowIndex) {
    const components = [];
    this.component.components.map((col, colIndex) => {
      const column = _.clone(col);
      const options = _.clone(this.options);
      options.name += `[${rowIndex}]`;
      options.row = `${rowIndex}-${colIndex}`;
      const comp = this.createComponent(_.assign({}, column, {
        row: options.row
      }), options, row);
      comp.rowIndex = rowIndex;
      // Don't bubble sub changes since they won't apply until pressing save.
      comp.triggerChange = () => {
        // Should we recalculate or something here?
        // TODO: Cause refreshOn to trigger.
      };
      components.push(comp);
    });
    return components;
  }

  validateRow(rowIndex, dirty) {
    let valid = true;
    const editRow = this.editRows[rowIndex];
    if (editRow.isOpen) {
      const isDirty = dirty || !!editRow.dirty;
      editRow.components.forEach(comp => {
        comp.setPristine(!isDirty);
        valid &= comp.checkValidity(null, isDirty, editRow.data);
      });
    }

    if (this.component.validate && this.component.validate.row) {
      valid = this.evaluate(this.component.validate.row, {
        valid,
        row: editRow.data
      }, 'valid', true);
      if (valid.toString() !== 'true') {
        editRow.error = valid;
      }
      else {
        delete editRow.error;
      }
      if (valid === null) {
        valid = `Invalid row validation for ${this.key}`;
      }
    }

    return valid;
  }

  checkValidity(data, dirty) {
    if (!this.checkCondition(null, data)) {
      this.setCustomValidity('');
      return true;
    }

    let rowsValid = true;
    let rowsClosed = true;
    this.editRows.forEach((editRow, rowIndex) => {
      // Trigger all errors on the row.
      const rowValid = this.validateRow(rowIndex, dirty);

      rowsValid &= rowValid;

      // Any open rows causes validation to fail.
      rowsClosed &= !editRow.isOpen;
    });

    if (!rowsValid) {
      this.setCustomValidity('Please correct rows before proceeding.', dirty);
      return false;
    }
    else if (!rowsClosed) {
      this.setCustomValidity('Please save all rows before proceeding.', dirty);
      return false;
    }

    const message = this.invalid || this.invalidMessage(data, dirty);
    this.setCustomValidity(message, dirty);
    return true;
  }

  get defaultValue() {
    const value = super.defaultValue;
    return Array.isArray(value) ? value : [];
  }

  updateValue(flags, value) {
    // Intentionally skip over nested component updateValue method to keep recursive update from occurring with sub components.
    return Component.prototype.updateValue.call(this, flags, value);
  }

  hasChanged(before, after) {
    if (
      ((before === undefined) || (before === null)) &&
      ((after === undefined) || (after === null))
    ) {
      return false;
    }
    return !_.isEqual(before, after);
  }

  setValue(value, flags) {
    if (!value) {
      return;
    }
    if (!Array.isArray(value)) {
      if (typeof value === 'object') {
        value = [value];
      }
      else {
        return;
      }
    }

    const changed = this.hasChanged(value, this.dataValue);
    this.dataValue = value;
    // Refresh editRow data when data changes.
    this.dataValue.forEach((row, rowIndex) => {
      const editRow = this.editRows[rowIndex];
      if (editRow) {
        editRow.data = row;
        if (editRow.isOpen) {
          editRow.components.forEach(col => {
            col.data = row;
            col.setValue(row[col.key], flags);
          });
        }
      }
      else {
        this.editRows[rowIndex] = {
          components: [],
          isOpen: false,
          data: row
        };
      }
    });
    if (changed) {
      this.checkValidity(this.data);
      this.redraw();
    }
    return changed;
  }

  /**
   * Get the value of this component.
   *
   * @returns {*}
   */
  getValue() {
    return this.dataValue;
  }

  restoreComponentsContext() {
    return;
  }
}
<commit_msg>Improved validation for the case when EditGrid has inline edit.<commit_after>import _ from 'lodash';
import NestedComponent from '../_classes/nested/NestedComponent';
import Component from '../_classes/component/Component';
import Components from '../Components';

export default class EditGridComponent extends NestedComponent {
  static schema(...extend) {
    return NestedComponent.schema({
      type: 'editgrid',
      label: 'Edit Grid',
      key: 'editGrid',
      clearOnHide: true,
      input: true,
      tree: true,
      defaultOpen: false,
      components: [],
      inlineEdit: false,
      templates: {
        header: this.defaultHeaderTemplate,
        row: this.defaultRowTemplate,
        footer: ''
      }
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Edit Grid',
      icon: 'tasks',
      group: 'data',
      documentation: 'http://help.form.io/userguide/#editgrid',
      weight: 40,
      schema: EditGridComponent.schema()
    };
  }

  static get defaultHeaderTemplate() {
    return  `

  {% util.eachComponent(components, function(component) { %}

{{ component.label }}

  {% }) %}

`;
  }

  static get defaultRowTemplate() {
    return `

  {% util.eachComponent(components, function(component) { %}

      {{ getView(component, row[component.key]) }}

  {% }) %}
  {% if (!instance.options.readOnly) { %}

        <button class="btn btn-default btn-sm editRow">Edit</button>
        <button class="btn btn-danger btn-sm removeRow">Delete</button>

  {% } %}

`;
  }

  get defaultSchema() {
    return EditGridComponent.schema();
  }

  get emptyValue() {
    return [];
  }

  get editgridKey() {
    return `editgrid-${this.key}`;
  }

  constructor(...args) {
    super(...args);
    this.type = 'editgrid';
  }

  init() {
    this.components = this.components || [];
    const dataValue = this.dataValue || [];
    this.editRows = dataValue.map((row) => ({
      isOpen: false,
      data: row,
    }));
    // In builder we need one row so the components will show up.
    if (this.options.attachMode === 'builder') {
      this.addRow();
    }
  }

  render() {
    const dataValue = this.dataValue || [];
    return super.render(this.renderTemplate('editgrid', {
      editgridKey: this.editgridKey,
      header: this.renderString(_.get(this.component, 'templates.header'), {
        components: this.component.components,
        value: dataValue
      }),
      footer: this.renderString(_.get(this.component, 'templates.footer'), {
        components: this.component.components,
        value: dataValue
      }),
      rows: this.editRows.map(this.renderRow.bind(this)),
      openRows: this.editRows.map(row => row.isOpen),
      errors: this.editRows.map(row => row.error)
    }));
  }

  attach(element) {
    this.loadRefs(element, {
      [`${this.editgridKey}-addRow`]: 'multiple',
      [`${this.editgridKey}-removeRow`]: 'multiple',
      [`${this.editgridKey}-saveRow`]: 'multiple',
      [`${this.editgridKey}-cancelRow`]: 'multiple',
      [this.editgridKey]: 'multiple',
    });

    this.refs[`${this.editgridKey}-addRow`].forEach((addButton) => {
      this.addEventListener(addButton, 'click', this.addRow.bind(this));
    });

    let openRowCount = 0;
    this.refs[this.editgridKey].forEach((row, rowIndex) => {
      if (this.editRows[rowIndex].isOpen) {
        this.attachComponents(row, this.editRows[rowIndex].components);
        this.addEventListener(this.refs[`${this.editgridKey}-saveRow`][openRowCount], 'click', this.saveRow.bind(this, rowIndex));
        this.addEventListener(this.refs[`${this.editgridKey}-cancelRow`][openRowCount], 'click', this.cancelRow.bind(this, rowIndex));
        openRowCount++;
      }
      else {
        // Attach edit and remove button events.
        [
          {
            class: 'removeRow',
            event: 'click',
            action: this.removeRow.bind(this, rowIndex)
          },
          {
            class: 'editRow',
            event: 'click',
            action: this.editRow.bind(this, rowIndex)
          },
        ].forEach(action => {
          const elements = row.getElementsByClassName(action.class);
          Array.prototype.forEach.call(elements, element => {
            element.addEventListener(action.event, action.action);
          });
        });
      }
    });

    // Add open class to the element if any edit grid row is open
    if (openRowCount) {
      this.addClass(this.refs.component, `formio-component-${this.component.type}-row-open`);
    }
    else {
      this.removeClass(this.refs.component, `formio-component-${this.component.type}-row-open`);
    }

    super.attach(element);
  }

  renderRow(row, rowIndex) {
    const dataValue = this.dataValue || [];
    if (row.isOpen) {
      return this.renderComponents(row.components);
    }
    else {
      return this.renderString(_.get(this.component, 'templates.row', EditGridComponent.defaultRowTemplate),
        {
          row: dataValue[rowIndex],
          rowIndex,
          components: this.component.components,
          getView: (component, data) => Components.create(component, this.options, data, true).getView(data)
        });
    }
  }

  checkData(data, flags = {}, index) {
    let valid = true;
    if (flags.noCheck) {
      return;
    }

    // Update the value.
    let changed = this.updateValue({
      noUpdateEvent: true
    });

    const editRow = this.editRows[index];

    // Iterate through all components and check conditions, and calculate values.
    editRow.components.forEach(comp => {
      changed |= comp.calculateValue(data, {
        noUpdateEvent: true
      });
      comp.checkConditions(data);
      if (!flags.noValidate) {
        valid &= comp.checkValidity(data, !editRow.isOpen);
      }
    });

    valid &= (this.validateRow(index) === true);

    // Trigger the change if the values changed.
    if (changed) {
      this.triggerChange(flags);
    }

    // Return if the value is valid.
    return valid;
  }

  addRow() {
    if (this.options.readOnly) {
      return;
    }
    const dataObj = {};
    this.editRows.push({
      components: [],
      isOpen: true,
      data: dataObj
    });
    if (this.component.inlineEdit) {
      this.dataValue.push(dataObj);
    }
    const rowIndex = this.editRows.length - 1;
    const editRow = this.editRows[rowIndex];
    this.emit('editGridAddRow', {
      component: this.component,
      row: editRow
    });
    editRow.components = this.createRowComponents(editRow.data, rowIndex);
    this.redraw();
  }

  editRow(rowIndex) {
    const dataValue = this.dataValue || [];
    const editRow = this.editRows[rowIndex];
    editRow.dirty = false;
    editRow.isOpen = true;
    editRow.editing = true;
    const dataSnapshot = dataValue[rowIndex] ? _.cloneDeep(dataValue[rowIndex]) : {};
    if (this.component.inlineEdit) {
      editRow.backup = dataSnapshot;
    }
    else {
      editRow.data = dataSnapshot;
    }
    editRow.components = this.createRowComponents(editRow.data, rowIndex);
    this.redraw();
  }

  clearErrors(rowIndex) {
    const editRow = this.editRows[rowIndex];
    if (editRow && Array.isArray(editRow.components)) {
      editRow.components.forEach(comp => {
        comp.setPristine(true);
        comp.setCustomValidity('');
      });
    }
  }

  cancelRow(rowIndex) {
    const editRow = this.editRows[rowIndex];
    if (this.options.readOnly) {
      editRow.dirty = false;
      editRow.isOpen = false;
      this.redraw();
      return;
    }
    if (editRow.editing) {
      const dataValue = this.dataValue || [];
      editRow.dirty = false;
      editRow.isOpen = false;
      if (this.component.inlineEdit) {
        this.dataValue[rowIndex] = editRow.backup;
      }
      editRow.data = dataValue[rowIndex] || {};
      this.clearErrors(rowIndex);
    }
    else {
      this.clearErrors(rowIndex);
      if (this.component.inlineEdit) {
        this.splice(rowIndex);
      }
      this.editRows.splice(rowIndex, 1);
    }

    this.checkValidity(this.data, true);
    this.redraw();
  }

  saveRow(rowIndex) {
    const editRow = this.editRows[rowIndex];
    if (this.options.readOnly) {
      editRow.dirty = false;
      editRow.isOpen = false;
      this.redraw();
      return;
    }
    editRow.dirty = true;
    if (this.validateRow(rowIndex) !== true) {
      return;
    }

    if (!this.component.inlineEdit) {
      const dataValue = this.dataValue || [];
      if (editRow.editing) {
        dataValue[rowIndex] = editRow.data;
      }
      else {
        // Insert this row into its proper place.
        const newIndex = dataValue.length;
        dataValue.push(editRow.data);
        this.editRows.splice(rowIndex, 1);
        this.editRows.splice(newIndex, 0, editRow);
        rowIndex = newIndex;
      }
    }
    editRow.dirty = false;
    editRow.isOpen = false;
    this.updateValue();
    this.triggerChange();
    this.checkValidity(this.data, true);
    this.redraw();
  }

  removeRow(rowIndex) {
    if (this.options.readOnly) {
      return;
    }
    this.splice(rowIndex);
    this.editRows.splice(rowIndex, 1);
    this.updateValue();
    this.triggerChange();
    this.checkValidity(this.data, true);
    this.redraw();
  }

  createRowComponents(row, rowIndex) {
    const components = [];
    this.component.components.map((col, colIndex) => {
      const column = _.clone(col);
      const options = _.clone(this.options);
      options.name += `[${rowIndex}]`;
      options.row = `${rowIndex}-${colIndex}`;
      const comp = this.createComponent(_.assign({}, column, {
        row: options.row
      }), options, row);
      comp.rowIndex = rowIndex;
      // Don't bubble sub changes since they won't apply until pressing save.
      comp.triggerChange = () => {
        // Should we recalculate or something here?
        // TODO: Cause refreshOn to trigger.
      };
      components.push(comp);
    });
    return components;
  }

  validateRow(rowIndex, dirty) {
    let valid = true;
    const editRow = this.editRows[rowIndex];
    if (editRow.isOpen) {
      const isDirty = dirty || !!editRow.dirty;
      editRow.components.forEach(comp => {
        comp.setPristine(!isDirty);
        valid &= comp.checkValidity(null, isDirty, editRow.data);
      });
    }

    if (this.component.validate && this.component.validate.row) {
      valid = this.evaluate(this.component.validate.row, {
        valid,
        row: editRow.data
      }, 'valid', true);
      if (valid.toString() !== 'true') {
        editRow.error = valid;
      }
      else {
        delete editRow.error;
      }
      if (valid === null) {
        valid = `Invalid row validation for ${this.key}`;
      }
    }

    return valid;
  }

  checkValidity(data, dirty) {
    if (!this.checkCondition(null, data)) {
      this.setCustomValidity('');
      return true;
    }

    let rowsValid = true;
    let rowsClosed = true;
    this.editRows.forEach((editRow, rowIndex) => {
      // Trigger all errors on the row.
      const rowValid = this.validateRow(rowIndex, dirty);

      rowsValid &= rowValid;

      // Any open rows causes validation to fail.
      rowsClosed &= !editRow.isOpen;
    });

    if (!rowsValid) {
      this.setCustomValidity('Please correct rows before proceeding.', dirty);
      return false;
    }
    else if (!rowsClosed && !this.component.inlineEdit) {
      this.setCustomValidity('Please save all rows before proceeding.', dirty);
      return false;
    }

    const message = this.invalid || this.invalidMessage(data, dirty);
    this.setCustomValidity(message, dirty);
    return true;
  }

  get defaultValue() {
    const value = super.defaultValue;
    return Array.isArray(value) ? value : [];
  }

  updateValue(flags, value) {
    // Intentionally skip over nested component updateValue method to keep recursive update from occurring with sub components.
    return Component.prototype.updateValue.call(this, flags, value);
  }

  hasChanged(before, after) {
    if (
      ((before === undefined) || (before === null)) &&
      ((after === undefined) || (after === null))
    ) {
      return false;
    }
    return !_.isEqual(before, after);
  }

  setValue(value, flags) {
    if (!value) {
      return;
    }
    if (!Array.isArray(value)) {
      if (typeof value === 'object') {
        value = [value];
      }
      else {
        return;
      }
    }

    const changed = this.hasChanged(value, this.dataValue);
    this.dataValue = value;
    // Refresh editRow data when data changes.
    this.dataValue.forEach((row, rowIndex) => {
      const editRow = this.editRows[rowIndex];
      if (editRow) {
        editRow.data = row;
        if (editRow.isOpen) {
          editRow.components.forEach(col => {
            col.data = row;
            col.setValue(row[col.key], flags);
          });
        }
      }
      else {
        this.editRows[rowIndex] = {
          components: [],
          isOpen: false,
          data: row
        };
      }
    });
    if (changed) {
      this.checkValidity(this.data);
      this.redraw();
    }
    return changed;
  }

  /**
   * Get the value of this component.
   *
   * @returns {*}
   */
  getValue() {
    return this.dataValue;
  }

  restoreComponentsContext() {
    return;
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import Bills from './bills'
import Payments from './payments'
import FinanceNavmenu from './finance_navmenu'

class Finances extends Component {
  render() {
    return (

        <FinanceNavmenu />
        {this.props.children}

    )
  }
}

export default Finances<commit_msg>change responsiveness level for finances<commit_after>import React, { Component } from 'react'
import Bills from './bills'
import Payments from './payments'
import FinanceNavmenu from './finance_navmenu'

class Finances extends Component {
  render() {
    return (

        <FinanceNavmenu />
        {this.props.children}

    )
  }
}

export default Finances<|endoftext|>


<commit_before>// dependencies
//
import React from 'react';
import classnames from 'classnames';

// @TODO a generic button which is not wired into the form `isValid` state

/**
 * The SubmitButton component
 * @param  {String}   children   The label to display in the button
 * @param  {String}   style   The style with which to display the button
 * @param  {Function} onClick The `onClick` handler for the button
 * @param  {Boolean}  isValid A flag indicating whether the form to which this
 *                            button is attached is valid
 * @return {React.Element}    The React element describing this component
 */
const SubmitButton = ({ children, style, onClick }, { isValid }) => (
    <button
        className={classnames('btn', {
            'btn-danger':  style === 'danger' || style === 'error',
            'btn-warning': style === 'warning' || style === 'warn',
            'btn-info':    style === 'info',
            'btn-success': style === 'success' || style === 'ok',
            'btn-default': !style,
        })}
        disabled={!isValid}
        onClick={onClick}
    >
        {children || 'Submit'}
    </button>
);

// define the property types for the component
//
SubmitButton.propTypes = {
    style:    React.PropTypes.string,
    onClick:  React.PropTypes.func,
    children: React.PropTypes.oneOfType([
        React.PropTypes.arrayOf(React.PropTypes.node),
        React.PropTypes.node,
    ]),
};

// define the context types for values received from higher up the food chain
//
SubmitButton.contextTypes = {
    isValid: React.PropTypes.bool,
};

// export the component
//
export default SubmitButton;
<commit_msg>comment cleanup<commit_after>// dependencies
//
import React from 'react';
import classnames from 'classnames';

// @TODO a generic button which is not wired into the form `isValid` state

/**
 * The SubmitButton component
 * @param  {String}   children   The label to display in the button
 * @param  {String}   style   The style with which to display the button
 * @param  {Function} onClick The `onClick` handler for the button
 * @param  {Boolean}  isValid A flag (from the parent Form's context) indicating
 *                            whether the form to which this button is attached
 *                            is valid
 * @return {React.Element}    The React element describing this component
 */
const SubmitButton = ({ children, style, onClick }, { isValid }) => (
    <button
        className={classnames('btn', {
            'btn-danger':  style === 'danger' || style === 'error',
            'btn-warning': style === 'warning' || style === 'warn',
            'btn-info':    style === 'info',
            'btn-success': style === 'success' || style === 'ok',
            'btn-default': !style,
        })}
        disabled={!isValid}
        onClick={onClick}
    >
        {children || 'Submit'}
    </button>
);

// define the property types for the component
//
SubmitButton.propTypes = {
    style:    React.PropTypes.string,
    onClick:  React.PropTypes.func,
    children: React.PropTypes.oneOfType([
        React.PropTypes.arrayOf(React.PropTypes.node),
        React.PropTypes.node,
    ]),
};

// define the context types for values received from higher up the food chain
//
SubmitButton.contextTypes = {
    isValid: React.PropTypes.bool,
};

// export the component
//
export default SubmitButton;
<|endoftext|>


<commit_before>// npm dependencies
//
import React from 'react';
import classnames from 'classnames';

/**
 * The SubmitButton component
 * @param  {String}   children   The label to display in the button
 * @param  {String}   name    The name for the component
 * @param  {String}   style   The style with which to display the button
 * @param  {Function} onClick The `onClick` handler for the button
 * @param  {Boolean}  isValid A flag (from the parent Form's context) indicating
 *                            whether the form to which this button is attached
 *                            is valid
 * @return {React.Element}    The React element describing this component
 */

class SubmitButton extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            disabled: props.disabled,
        };
    }

    componentWillReceiveProps(newProps) {
        if (this.state.disabled !== !!newProps.disabled) {
            this.setState({ disabled: !!newProps.disabled });
        }
    }

    render() {
        return (
            <button
                name={this.props.name}
                className={classnames('btn', {
                    'btn-danger':  this.props.style === 'danger' || this.props.style === 'error',
                    'btn-warning': this.props.style === 'warning' || this.props.style === 'warn',
                    'btn-info':    this.props.style === 'info',
                    'btn-success': this.props.style === 'success' || this.props.style === 'ok',
                    'btn-default': !this.props.style,
                })}
                disabled={!this.context.isValid || !!this.state.disabled}
                onClick={this.props.onClick}
            >
                {this.props.children || 'Submit'}
            </button>
        );
    }
}

// define the property types for the component
//
SubmitButton.propTypes = {
    /** A flag indicated whether this button is disabled */
    disabled: React.PropTypes.boolean,
    /** The name for the component */
    name:     React.PropTypes.string,
    /** The style with which to display the button */
    style:       React.PropTypes.oneOf(
        ['danger', 'error', 'warning', 'warn', 'info', 'success', 'ok']
    ),
    /** The `onClick` handler for the button */
    onClick:  React.PropTypes.func,
    /** The child(ren) of this component */
    children: React.PropTypes.oneOfType([
        React.PropTypes.arrayOf(React.PropTypes.node),
        React.PropTypes.node,
    ]),
};

// define the context types for values received from higher up the food chain
//
SubmitButton.contextTypes = {
    isValid: React.PropTypes.bool,
};

// export the component
//
export default SubmitButton;
<commit_msg>comment cleanup<commit_after>// npm dependencies
//
import React from 'react';
import classnames from 'classnames';

/**
 * The SubmitButton component
 * @param  {String}   children   The label to display in the button
 * @param  {String}   name    The name for the component
 * @param  {String}   style   The style with which to display the button
 * @param  {Function} onClick The `onClick` handler for the button
 * @param  {Boolean}  isValid A flag (from the parent Form's context) indicating
 *                            whether the form to which this button is attached
 *                            is valid
 * @return {React.Element}    The React element describing this component
 */

class SubmitButton extends React.Component {
    /**
     * Construct the SubmitButton instance
     * @param  {Object} props The component properties
     */
    constructor(props) {
        super(props);

        // initialize the component state
        //
        this.state = {
            disabled: props.disabled,
        };
    }

    /**
     * Handle new props for the component
     * @param  {Object} newProps The new property values
     */
    componentWillReceiveProps(newProps) {
        // has the disabled flag state changed? if so, update the component
        // state
        //
        if (this.state.disabled !== !!newProps.disabled) {
            this.setState({ disabled: !!newProps.disabled });
        }
    }

    /**
     * Render the component
     * @return {React.Element} The React component
     */
    render() {
        return (
            <button
                name={this.props.name}
                className={classnames('btn', {
                    'btn-danger':  this.props.style === 'danger' || this.props.style === 'error',
                    'btn-warning': this.props.style === 'warning' || this.props.style === 'warn',
                    'btn-info':    this.props.style === 'info',
                    'btn-success': this.props.style === 'success' || this.props.style === 'ok',
                    'btn-default': !this.props.style,
                })}
                disabled={!this.context.isValid || !!this.state.disabled}
                onClick={this.props.onClick}
            >
                {this.props.children || 'Submit'}
            </button>
        );
    }
}

// define the property types for the component
//
SubmitButton.propTypes = {
    /** A flag indicated whether this button is disabled */
    disabled: React.PropTypes.boolean,
    /** The name for the component */
    name:     React.PropTypes.string,
    /** The style with which to display the button */
    style:       React.PropTypes.oneOf(
        ['danger', 'error', 'warning', 'warn', 'info', 'success', 'ok']
    ),
    /** The `onClick` handler for the button */
    onClick:  React.PropTypes.func,
    /** The child(ren) of this component */
    children: React.PropTypes.oneOfType([
        React.PropTypes.arrayOf(React.PropTypes.node),
        React.PropTypes.node,
    ]),
};

// define the context types for values received from higher up the food chain
//
SubmitButton.contextTypes = {
    isValid: React.PropTypes.bool,
};

// export the component
//
export default SubmitButton;
<|endoftext|>


<commit_before>import styled from 'styled-components';
import { slideAnimation, fadeAnimation } from './pages.config';
import StyledHeadingIcon from '../heading/heading.style';

const PagesContent = styled.div`
  overflow: hidden;
  position: relative;

  ${StyledHeadingIcon} {
    margin-top: -9px;
  }
`;

const PagesWrapperStyle = styled.div`
  ${slideAnimation};
  ${fadeAnimation};
`;

export {
  PagesWrapperStyle,
  PagesContent
};
<commit_msg>FE-1587 resolved defects reported by qa.<commit_after>import styled from 'styled-components';
import { slideAnimation, fadeAnimation } from './pages.config';
import StyledHeadingIcon from '../heading/heading.style';

const PagesContent = styled.div`
  overflow: hidden;
  position: relative;

  ${StyledHeadingIcon} {
    margin-top: -9px;
  }

  .carbon-app-wrapper {
    min-width: auto;
    max-width: 100%;
  }
`;

const PagesWrapperStyle = styled.div`
  ${slideAnimation};
  ${fadeAnimation};
`;

export {
  PagesWrapperStyle,
  PagesContent
};
<|endoftext|>


<commit_before>import React from 'react';
import { Line } from 'react-chartjs-2';

export default function Day({ days }) {
  const items = days.day || [];

  if (days.loading) {
    return (

Loading...

    );
  }

  const data = {
    labels: items.map((d) => d.date, []),
    datasets: [{
      label: 'Moisture',
      fill: true,
      data: items.map((d) => d.moisture, []),
    }],
  };

  return (

        <Line data={data} />

  );
}
<commit_msg>Switch x axis to minute<commit_after>import React from 'react';
import { Line } from 'react-chartjs-2';

export default function Day({ days }) {
  const items = days.day || [];

  if (days.loading) {
    return (

Loading...

    );
  }

  const data = {
    labels: items.map((d) => d.date, []),
    datasets: [{
      label: 'Moisture',
      fill: true,
      data: items.map((d) => d.moisture, []),
    }],
  };

  const options = {
    scales: {
      xAxes: [{
        type: 'time',
      }],
    },
  };

  return (

        <Line data={data} options={options} />

  );
}
<|endoftext|>