Add attached attribute to BottomTab

:new: Add attached attribute to BottomTab

--- lib/views/bottom-tab.js
+++ lib/views/bottom-tab.js
@@ -3,10 +3,12 @@
 class BottomTab extends HTMLElement{
   prepare(Name){
     this.Name = Name
+    this.attached = false
     return this
   }
   attachedCallback() {
     this.active = false
+    this.attached = true
     this.classList.add('linter-tab')

     this.countSpan = document.createElement('span')
@@ -15,6 +17,9 @@

     this.innerHTML = this.Name + ' '
     this.appendChild(this.countSpan)
+  }
+  detachedCallback(){
+    this.attached = false
   }
   get active() {
     return this._active

<|original_code|>
'use strict';

class BottomTab extends HTMLElement{
  prepare(Name){
    this.Name = Name
    return this
  }
  attachedCallback() {
    this.active = false
    this.classList.add('linter-tab')

    this.countSpan = document.createElement('span')
    this.countSpan.classList.add('count')
    this.countSpan.textContent = '0'

    this.innerHTML = this.Name + ' '
    this.appendChild(this.countSpan)
  }
  get active() {
    return this._active
  }
  set active(value) {
    this._active = value
    if (value) {
      this.classList.add('active')
    } else {
      this.classList.remove('active')
    }
  }
  set count(value) {
    this.countSpan.textContent = value
  }
}

module.exports = BottomTab = document.registerElement('linter-bottom-tab', {
  prototype: BottomTab.prototype
})

<|edits_diff|>
--- lib/views/bottom-tab.js
+++ lib/views/bottom-tab.js
@@ -3,10 +3,12 @@
 class BottomTab extends HTMLElement{
   prepare(Name){
     this.Name = Name
+    this.attached = false
     return this
   }
   attachedCallback() {
     this.active = false
+    this.attached = true
     this.classList.add('linter-tab')

     this.countSpan = document.createElement('span')
<|current_version|>
'use strict';

class BottomTab extends HTMLElement{
  prepare(Name){
    this.Name = Name
    this.attached = false
    return this
  }
  attachedCallback() {
    this.active = false
    this.attached = true
    this.classList.add('linter-tab')

    this.countSpan = document.createElement('span')
    this.countSpan.classList.add('count')
    this.countSpan.textContent = '0'

    this.innerHTML = this.Name + ' '
    this.appendChild(this.countSpan)
  }
  get active() {
    return this._active
  }
  set active(value) {
    this._active = value
    if (value) {
      this.classList.add('active')
    } else {
      this.classList.remove('active')
    }
  }
  set count(value) {
    this.countSpan.textContent = value
  }
}

module.exports = BottomTab = document.registerElement('linter-bottom-tab', {
  prototype: BottomTab.prototype
})

<|next_version|>
'use strict';

class BottomTab extends HTMLElement{
  prepare(Name){
    this.Name = Name
    this.attached = false
    return this
  }
  attachedCallback() {
    this.active = false
    this.attached = true
    this.classList.add('linter-tab')

    this.countSpan = document.createElement('span')
    this.countSpan.classList.add('count')
    this.countSpan.textContent = '0'

    this.innerHTML = this.Name + ' '
    this.appendChild(this.countSpan)
  }
  detachedCallback(){
    this.attached = false
  }
  get active() {
    return this._active
  }
  set active(value) {
    this._active = value
    if (value) {
      this.classList.add('active')
    } else {
      this.classList.remove('active')
    }
  }
  set count(value) {
    this.countSpan.textContent = value
  }
}

module.exports = BottomTab = document.registerElement('linter-bottom-tab', {
  prototype: BottomTab.prototype
})


Add LayoutContainer to route root

Add LayoutContainer to route root

--- client/config/routes.js
+++ client/config/routes.js
@@ -3,6 +3,9 @@
  */
 import React from 'react'
 import { IndexRedirect, IndexRoute, Route } from 'react-router'
+
+// Layout
+import { LayoutContainer } from 'components/Layout'

 // Pages
 import Home from 'client/pages/Home'
@@ -15,6 +18,7 @@

 export default (
   <Route
+    component={LayoutContainer}
     path='/'
   >
     <IndexRoute

<|original_code|>
/**
 * Route definitions
 */
import React from 'react'
import { IndexRedirect, IndexRoute, Route } from 'react-router'

// Pages
import Home from 'client/pages/Home'
import Terms from 'client/pages/Terms'
import BikeshedViewer from 'client/pages/BikeshedViewer'

// Queries
import ViewerQueries from 'client/queries/ViewerQueries'
import BikeshedQueries from 'client/queries/BikeshedQueries'

export default (
  <Route
    path='/'
  >
    <IndexRoute
      component={Home}
      queries={ViewerQueries}
    />

    <Route path='/bikesheds'>
      <IndexRedirect to='/'/>

      <Route
        component={BikeshedViewer}
        path=':bikeshedId'
        queries={BikeshedQueries}
      />
    </Route>

    <Route
      component={Terms}
      path='/terms'
    />
  </Route>
)

<|edits_diff|>
--- client/config/routes.js
+++ client/config/routes.js
@@ -3,6 +3,9 @@
  */
 import React from 'react'
 import { IndexRedirect, IndexRoute, Route } from 'react-router'
+
+// Layout
+import { LayoutContainer } from 'components/Layout'

 // Pages
 import Home from 'client/pages/Home'
<|current_version|>
/**
 * Route definitions
 */
import React from 'react'
import { IndexRedirect, IndexRoute, Route } from 'react-router'

// Layout
import { LayoutContainer } from 'components/Layout'

// Pages
import Home from 'client/pages/Home'
import Terms from 'client/pages/Terms'
import BikeshedViewer from 'client/pages/BikeshedViewer'

// Queries
import ViewerQueries from 'client/queries/ViewerQueries'
import BikeshedQueries from 'client/queries/BikeshedQueries'

export default (
  <Route
    path='/'
  >
    <IndexRoute
      component={Home}
      queries={ViewerQueries}
    />

    <Route path='/bikesheds'>
      <IndexRedirect to='/'/>

      <Route
        component={BikeshedViewer}
        path=':bikeshedId'
        queries={BikeshedQueries}
      />
    </Route>

    <Route
      component={Terms}
      path='/terms'
    />
  </Route>
)

<|next_version|>
/**
 * Route definitions
 */
import React from 'react'
import { IndexRedirect, IndexRoute, Route } from 'react-router'

// Layout
import { LayoutContainer } from 'components/Layout'

// Pages
import Home from 'client/pages/Home'
import Terms from 'client/pages/Terms'
import BikeshedViewer from 'client/pages/BikeshedViewer'

// Queries
import ViewerQueries from 'client/queries/ViewerQueries'
import BikeshedQueries from 'client/queries/BikeshedQueries'

export default (
  <Route
    component={LayoutContainer}
    path='/'
  >
    <IndexRoute
      component={Home}
      queries={ViewerQueries}
    />

    <Route path='/bikesheds'>
      <IndexRedirect to='/'/>

      <Route
        component={BikeshedViewer}
        path=':bikeshedId'
        queries={BikeshedQueries}
      />
    </Route>

    <Route
      component={Terms}
      path='/terms'
    />
  </Route>
)


Set up test route to enqueue message

Set up test route to enqueue message

--- api-server/index.js
+++ api-server/index.js
@@ -10,4 +10,9 @@

 app.get('/test', (req, res) => {
   mQ.enqueue('scrape_req');
+  res.status(200).send('test hit');
 });
+
+app.listen(app.get('port'), () => {
+  console.log('api-server is listening on port', app.get('port'));
+});

<|original_code|>
'use strict';

const express = require('express');
const MsgQueueClient = require('msgqueue-client');

const mqServerConfig = require('../common/config/mqserver.js');

const app = express();
const mQ = new MsgQueueClient(mqServerConfig.url);

app.get('/test', (req, res) => {
  mQ.enqueue('scrape_req');
});

<|edits_diff|>
--- api-server/index.js
+++ api-server/index.js
@@ -10,4 +10,5 @@

 app.get('/test', (req, res) => {
   mQ.enqueue('scrape_req');
+  res.status(200).send('test hit');
 });
<|current_version|>
'use strict';

const express = require('express');
const MsgQueueClient = require('msgqueue-client');

const mqServerConfig = require('../common/config/mqserver.js');

const app = express();
const mQ = new MsgQueueClient(mqServerConfig.url);

app.get('/test', (req, res) => {
  mQ.enqueue('scrape_req');
  res.status(200).send('test hit');
});

<|next_version|>
'use strict';

const express = require('express');
const MsgQueueClient = require('msgqueue-client');

const mqServerConfig = require('../common/config/mqserver.js');

const app = express();
const mQ = new MsgQueueClient(mqServerConfig.url);

app.get('/test', (req, res) => {
  mQ.enqueue('scrape_req');
  res.status(200).send('test hit');
});

app.listen(app.get('port'), () => {
  console.log('api-server is listening on port', app.get('port'));
});


Use member cookie on comment API

Use member cookie on comment API

refs https://github.com/TryGhost/Team/issues/1664

--- core/server/web/comments/app.js
+++ core/server/web/comments/app.js
@@ -6,6 +6,7 @@
 const urlUtils = require('../../../shared/url-utils');
 const sentry = require('../../../shared/sentry');
 const routes = require('./routes');
+const membersService = require('../../../server/services/members');

 module.exports = function setupCommentsApp() {
     debug('Comments App setup start');
@@ -16,6 +17,9 @@
     commentsApp.use(cors(siteUrl.origin));

     commentsApp.use(bodyParser.json({limit: '50mb'}));
+
+    // Global handling for member session, ensures a member is logged in to the frontend
+    commentsApp.use(membersService.middleware.loadMemberSession);

     // Routing
     commentsApp.use('/api/', routes());

<|original_code|>
const debug = require('@tryghost/debug')('comments');
const errorHandler = require('@tryghost/mw-error-handler');
const cors = require('cors');
const bodyParser = require('body-parser');
const express = require('../../../shared/express');
const urlUtils = require('../../../shared/url-utils');
const sentry = require('../../../shared/sentry');
const routes = require('./routes');

module.exports = function setupCommentsApp() {
    debug('Comments App setup start');
    const commentsApp = express('comments');

    // Support CORS for requests from the frontend
    const siteUrl = new URL(urlUtils.getSiteUrl());
    commentsApp.use(cors(siteUrl.origin));

    commentsApp.use(bodyParser.json({limit: '50mb'}));

    // Routing
    commentsApp.use('/api/', routes());

    // API error handling
    commentsApp.use('/api', errorHandler.resourceNotFound);
    commentsApp.use('/api', errorHandler.handleJSONResponse(sentry));

    debug('Comments App setup end');

    return commentsApp;
};

<|edits_diff|>
--- core/server/web/comments/app.js
+++ core/server/web/comments/app.js
@@ -6,6 +6,7 @@
 const urlUtils = require('../../../shared/url-utils');
 const sentry = require('../../../shared/sentry');
 const routes = require('./routes');
+const membersService = require('../../../server/services/members');

 module.exports = function setupCommentsApp() {
     debug('Comments App setup start');
<|current_version|>
const debug = require('@tryghost/debug')('comments');
const errorHandler = require('@tryghost/mw-error-handler');
const cors = require('cors');
const bodyParser = require('body-parser');
const express = require('../../../shared/express');
const urlUtils = require('../../../shared/url-utils');
const sentry = require('../../../shared/sentry');
const routes = require('./routes');
const membersService = require('../../../server/services/members');

module.exports = function setupCommentsApp() {
    debug('Comments App setup start');
    const commentsApp = express('comments');

    // Support CORS for requests from the frontend
    const siteUrl = new URL(urlUtils.getSiteUrl());
    commentsApp.use(cors(siteUrl.origin));

    commentsApp.use(bodyParser.json({limit: '50mb'}));

    // Routing
    commentsApp.use('/api/', routes());

    // API error handling
    commentsApp.use('/api', errorHandler.resourceNotFound);
    commentsApp.use('/api', errorHandler.handleJSONResponse(sentry));

    debug('Comments App setup end');

    return commentsApp;
};

<|next_version|>
const debug = require('@tryghost/debug')('comments');
const errorHandler = require('@tryghost/mw-error-handler');
const cors = require('cors');
const bodyParser = require('body-parser');
const express = require('../../../shared/express');
const urlUtils = require('../../../shared/url-utils');
const sentry = require('../../../shared/sentry');
const routes = require('./routes');
const membersService = require('../../../server/services/members');

module.exports = function setupCommentsApp() {
    debug('Comments App setup start');
    const commentsApp = express('comments');

    // Support CORS for requests from the frontend
    const siteUrl = new URL(urlUtils.getSiteUrl());
    commentsApp.use(cors(siteUrl.origin));

    commentsApp.use(bodyParser.json({limit: '50mb'}));

    // Global handling for member session, ensures a member is logged in to the frontend
    commentsApp.use(membersService.middleware.loadMemberSession);

    // Routing
    commentsApp.use('/api/', routes());

    // API error handling
    commentsApp.use('/api', errorHandler.resourceNotFound);
    commentsApp.use('/api', errorHandler.handleJSONResponse(sentry));

    debug('Comments App setup end');

    return commentsApp;
};


Fix <EditableBasicRow> isn't exported to bundle

--- src/index.js
+++ src/index.js
@@ -2,6 +2,7 @@

 // Visual elements
 import BasicRow from './BasicRow';
+import EditableBasicRow from './EditableBasicRow';
 import Icon from './Icon';
 import StatusIcon from './StatusIcon';
 import Tag from './Tag';
@@ -32,6 +33,7 @@

 export {
     BasicRow,
+    EditableBasicRow,
     Icon,
     StatusIcon,
     Tag,

<|original_code|>
import './styles/index.scss';

// Visual elements
import BasicRow from './BasicRow';
import Icon from './Icon';
import StatusIcon from './StatusIcon';
import Tag from './Tag';
import Text from './Text';
import EditableText from './EditableText';
import Tooltip from './Tooltip';
import AnchoredTooltip from './AnchoredTooltip';
import SwitchIcon from './SwitchIcon';

// Layout helpers
import FlexCell from './FlexCell';
import IconLayout from './IconLayout';
import TextEllipsis from './TextEllipsis';

// Row components
import TextLabel from './TextLabel';
import EditableTextLabel from './EditableTextLabel';
import Button from './Button';
import IconButton from './IconButton';
import Checkbox from './Checkbox';
import IconCheckbox from './IconCheckbox';
import Switch from './Switch';
import TextInput from './TextInput';
import SearchInput from './SearchInput';

// Containers
import InfiniteScroll from './InfiniteScroll';

export {
    BasicRow,
    Icon,
    StatusIcon,
    Tag,
    Text,
    EditableText,
    Tooltip,
    AnchoredTooltip,
    SwitchIcon,

    FlexCell,
    IconLayout,
    TextEllipsis,

    TextLabel,
    EditableTextLabel,
    Button,
    IconButton,
    Checkbox,
    IconCheckbox,
    Switch,
    TextInput,
    SearchInput,

    InfiniteScroll,
};

<|edits_diff|>
--- src/index.js
+++ src/index.js
@@ -2,6 +2,7 @@

 // Visual elements
 import BasicRow from './BasicRow';
+import EditableBasicRow from './EditableBasicRow';
 import Icon from './Icon';
 import StatusIcon from './StatusIcon';
 import Tag from './Tag';
<|current_version|>
import './styles/index.scss';

// Visual elements
import BasicRow from './BasicRow';
import EditableBasicRow from './EditableBasicRow';
import Icon from './Icon';
import StatusIcon from './StatusIcon';
import Tag from './Tag';
import Text from './Text';
import EditableText from './EditableText';
import Tooltip from './Tooltip';
import AnchoredTooltip from './AnchoredTooltip';
import SwitchIcon from './SwitchIcon';

// Layout helpers
import FlexCell from './FlexCell';
import IconLayout from './IconLayout';
import TextEllipsis from './TextEllipsis';

// Row components
import TextLabel from './TextLabel';
import EditableTextLabel from './EditableTextLabel';
import Button from './Button';
import IconButton from './IconButton';
import Checkbox from './Checkbox';
import IconCheckbox from './IconCheckbox';
import Switch from './Switch';
import TextInput from './TextInput';
import SearchInput from './SearchInput';

// Containers
import InfiniteScroll from './InfiniteScroll';

export {
    BasicRow,
    Icon,
    StatusIcon,
    Tag,
    Text,
    EditableText,
    Tooltip,
    AnchoredTooltip,
    SwitchIcon,

    FlexCell,
    IconLayout,
    TextEllipsis,

    TextLabel,
    EditableTextLabel,
    Button,
    IconButton,
    Checkbox,
    IconCheckbox,
    Switch,
    TextInput,
    SearchInput,

    InfiniteScroll,
};

<|next_version|>
import './styles/index.scss';

// Visual elements
import BasicRow from './BasicRow';
import EditableBasicRow from './EditableBasicRow';
import Icon from './Icon';
import StatusIcon from './StatusIcon';
import Tag from './Tag';
import Text from './Text';
import EditableText from './EditableText';
import Tooltip from './Tooltip';
import AnchoredTooltip from './AnchoredTooltip';
import SwitchIcon from './SwitchIcon';

// Layout helpers
import FlexCell from './FlexCell';
import IconLayout from './IconLayout';
import TextEllipsis from './TextEllipsis';

// Row components
import TextLabel from './TextLabel';
import EditableTextLabel from './EditableTextLabel';
import Button from './Button';
import IconButton from './IconButton';
import Checkbox from './Checkbox';
import IconCheckbox from './IconCheckbox';
import Switch from './Switch';
import TextInput from './TextInput';
import SearchInput from './SearchInput';

// Containers
import InfiniteScroll from './InfiniteScroll';

export {
    BasicRow,
    EditableBasicRow,
    Icon,
    StatusIcon,
    Tag,
    Text,
    EditableText,
    Tooltip,
    AnchoredTooltip,
    SwitchIcon,

    FlexCell,
    IconLayout,
    TextEllipsis,

    TextLabel,
    EditableTextLabel,
    Button,
    IconButton,
    Checkbox,
    IconCheckbox,
    Switch,
    TextInput,
    SearchInput,

    InfiniteScroll,
};


Add actual setting of new json

Add actual setting of new json

--- src/localization/utilities.js
+++ src/localization/utilities.js
@@ -17,6 +17,8 @@
   syncStrings,
   tableStrings,
   LANGUAGE_CODES,
+  formInputStrings,
+  dispensingStrings,
 } from './index';

 const DATE_CONFIGS = {
@@ -37,6 +39,8 @@
   tableStrings.setLanguage(language);
   syncStrings.setLanguage(language);
   programStrings.setLanguage(language);
+  formInputStrings.setLanguage(language);
+  dispensingStrings.setLanguage(language);
 }

 export default setCurrentLanguage;

<|original_code|>
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */
import moment from 'moment';
import french from 'moment/locale/fr';
import english from 'moment/locale/en-nz';

import {
  authStrings,
  buttonStrings,
  generalStrings,
  modalStrings,
  navStrings,
  pageInfoStrings,
  programStrings,
  syncStrings,
  tableStrings,
  LANGUAGE_CODES,
} from './index';

const DATE_CONFIGS = {
  DEFAULT: english,
  [LANGUAGE_CODES.FRENCH]: french,
};

export const setDateLocale = languageCode =>
  moment.updateLocale(languageCode, DATE_CONFIGS[languageCode] || DATE_CONFIGS.DEFAULT);

export function setCurrentLanguage(language) {
  authStrings.setLanguage(language);
  buttonStrings.setLanguage(language);
  generalStrings.setLanguage(language);
  modalStrings.setLanguage(language);
  navStrings.setLanguage(language);
  pageInfoStrings.setLanguage(language);
  tableStrings.setLanguage(language);
  syncStrings.setLanguage(language);
  programStrings.setLanguage(language);
}

export default setCurrentLanguage;

<|edits_diff|>
--- src/localization/utilities.js
+++ src/localization/utilities.js
@@ -17,6 +17,8 @@
   syncStrings,
   tableStrings,
   LANGUAGE_CODES,
+  formInputStrings,
+  dispensingStrings,
 } from './index';

 const DATE_CONFIGS = {
<|current_version|>
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */
import moment from 'moment';
import french from 'moment/locale/fr';
import english from 'moment/locale/en-nz';

import {
  authStrings,
  buttonStrings,
  generalStrings,
  modalStrings,
  navStrings,
  pageInfoStrings,
  programStrings,
  syncStrings,
  tableStrings,
  LANGUAGE_CODES,
  formInputStrings,
  dispensingStrings,
} from './index';

const DATE_CONFIGS = {
  DEFAULT: english,
  [LANGUAGE_CODES.FRENCH]: french,
};

export const setDateLocale = languageCode =>
  moment.updateLocale(languageCode, DATE_CONFIGS[languageCode] || DATE_CONFIGS.DEFAULT);

export function setCurrentLanguage(language) {
  authStrings.setLanguage(language);
  buttonStrings.setLanguage(language);
  generalStrings.setLanguage(language);
  modalStrings.setLanguage(language);
  navStrings.setLanguage(language);
  pageInfoStrings.setLanguage(language);
  tableStrings.setLanguage(language);
  syncStrings.setLanguage(language);
  programStrings.setLanguage(language);
}

export default setCurrentLanguage;

<|next_version|>
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */
import moment from 'moment';
import french from 'moment/locale/fr';
import english from 'moment/locale/en-nz';

import {
  authStrings,
  buttonStrings,
  generalStrings,
  modalStrings,
  navStrings,
  pageInfoStrings,
  programStrings,
  syncStrings,
  tableStrings,
  LANGUAGE_CODES,
  formInputStrings,
  dispensingStrings,
} from './index';

const DATE_CONFIGS = {
  DEFAULT: english,
  [LANGUAGE_CODES.FRENCH]: french,
};

export const setDateLocale = languageCode =>
  moment.updateLocale(languageCode, DATE_CONFIGS[languageCode] || DATE_CONFIGS.DEFAULT);

export function setCurrentLanguage(language) {
  authStrings.setLanguage(language);
  buttonStrings.setLanguage(language);
  generalStrings.setLanguage(language);
  modalStrings.setLanguage(language);
  navStrings.setLanguage(language);
  pageInfoStrings.setLanguage(language);
  tableStrings.setLanguage(language);
  syncStrings.setLanguage(language);
  programStrings.setLanguage(language);
  formInputStrings.setLanguage(language);
  dispensingStrings.setLanguage(language);
}

export default setCurrentLanguage;


import React from 'react';

import FormTextField from '../shared/FormTextField';
import ValidationErrors from '../../lib/ValidationErrors';

class Form extends React.Component {
  static propTypes = {
    name: React.PropTypes.string,
    description: React.PropTypes.string,
    errors: React.PropTypes.array,
    onChange: React.PropTypes.func
  };

  componentDidMount() {
    this.nameTextField.focus();
  }

  render() {
    var errors = new ValidationErrors(this.props.errors);

    return (
      <div>
        <FormTextField
          label="Name"
          errors={errors.findForField("name")}
          value={this.props.name}
          onChange={this.handleTeamNameChange}
          ref={(c) => this.nameTextField = c} />

        <FormTextField
          label="Description"
          errors={errors.findForField("description")}
          value={this.props.description}
          onChange={this.handleDescriptionChange} />
      </div>
    );
  }

  handleTeamNameChange = (e) => {
    this.props.onChange('name', e.target.value);
  };

  handleDescriptionChange = (e) => {
    this.props.onChange('description', e.target.value);
  };
}

export default Form

import React from 'react';

import FormTextField from '../shared/FormTextField';
import ValidationErrors from '../../lib/ValidationErrors';

class Form extends React.Component {
  static propTypes = {
    name: React.PropTypes.string,
    description: React.PropTypes.string,
    errors: React.PropTypes.array,
    onChange: React.PropTypes.func
  };

  componentDidMount() {
    this.nameTextField.focus();
  }

  render() {
    var errors = new ValidationErrors(this.props.errors);

    return (
      <div>
        <FormTextField
          label="Name"
          help="Pick a name for your team (you can even use :emoji:)"
          errors={errors.findForField("name")}
          value={this.props.name}
          onChange={this.handleTeamNameChange}
          ref={(c) => this.nameTextField = c} />

        <FormTextField
          label="Description"
          help="Describe what this team is all about"
          errors={errors.findForField("description")}
          value={this.props.description}
          onChange={this.handleDescriptionChange} />
      </div>
    );
  }

  handleTeamNameChange = (e) => {
    this.props.onChange('name', e.target.value);
  };

  handleDescriptionChange = (e) => {
    this.props.onChange('description', e.target.value);
  };
}

export default Form

Add some friendly help back

Add some friendly help back

--- app/components/team/Form.js
+++ app/components/team/Form.js
@@ -22,6 +22,7 @@
       <div>
         <FormTextField
           label="Name"
+          help="Pick a name for your team (you can even use :emoji:)"
           errors={errors.findForField("name")}
           value={this.props.name}
           onChange={this.handleTeamNameChange}
@@ -29,6 +30,7 @@

         <FormTextField
           label="Description"
+          help="Describe what this team is all about"
           errors={errors.findForField("description")}
           value={this.props.description}
           onChange={this.handleDescriptionChange} />

import React from 'react';

import FormTextField from '../shared/FormTextField';
import ValidationErrors from '../../lib/ValidationErrors';

class Form extends React.Component {
  static propTypes = {
    name: React.PropTypes.string,
    description: React.PropTypes.string,
    errors: React.PropTypes.array,
    onChange: React.PropTypes.func
  };

  componentDidMount() {
    this.nameTextField.focus();
  }

  render() {
    var errors = new ValidationErrors(this.props.errors);

    return (
      <div>
        <FormTextField
          label="Name"
          help="Pick a name for your team (you can even use :emoji:)"
          errors={errors.findForField("name")}
          value={this.props.name}
          onChange={this.handleTeamNameChange}
          ref={(c) => this.nameTextField = c} />

        <FormTextField
          label="Description"
          errors={errors.findForField("description")}
          value={this.props.description}
          onChange={this.handleDescriptionChange} />
      </div>
    );
  }

  handleTeamNameChange = (e) => {
    this.props.onChange('name', e.target.value);
  };

  handleDescriptionChange = (e) => {
    this.props.onChange('description', e.target.value);
  };
}

export default Form

<|original_code|>
import React from 'react';

import FormTextField from '../shared/FormTextField';
import ValidationErrors from '../../lib/ValidationErrors';

class Form extends React.Component {
  static propTypes = {
    name: React.PropTypes.string,
    description: React.PropTypes.string,
    errors: React.PropTypes.array,
    onChange: React.PropTypes.func
  };

  componentDidMount() {
    this.nameTextField.focus();
  }

  render() {
    var errors = new ValidationErrors(this.props.errors);

    return (

        <FormTextField
          label="Name"
          errors={errors.findForField("name")}
          value={this.props.name}
          onChange={this.handleTeamNameChange}
          ref={(c) => this.nameTextField = c} />

        <FormTextField
          label="Description"
          errors={errors.findForField("description")}
          value={this.props.description}
          onChange={this.handleDescriptionChange} />

    );
  }

  handleTeamNameChange = (e) => {
    this.props.onChange('name', e.target.value);
  };

  handleDescriptionChange = (e) => {
    this.props.onChange('description', e.target.value);
  };
}

export default Form

<|edits_diff|>
--- app/components/team/Form.js
+++ app/components/team/Form.js
@@ -22,6 +22,7 @@

         <FormTextField
           label="Name"
+          help="Pick a name for your team (you can even use :emoji:)"
           errors={errors.findForField("name")}
           value={this.props.name}
           onChange={this.handleTeamNameChange}
<|current_version|>
import React from 'react';

import FormTextField from '../shared/FormTextField';
import ValidationErrors from '../../lib/ValidationErrors';

class Form extends React.Component {
  static propTypes = {
    name: React.PropTypes.string,
    description: React.PropTypes.string,
    errors: React.PropTypes.array,
    onChange: React.PropTypes.func
  };

  componentDidMount() {
    this.nameTextField.focus();
  }

  render() {
    var errors = new ValidationErrors(this.props.errors);

    return (

        <FormTextField
          label="Name"
          help="Pick a name for your team (you can even use :emoji:)"
          errors={errors.findForField("name")}
          value={this.props.name}
          onChange={this.handleTeamNameChange}
          ref={(c) => this.nameTextField = c} />

        <FormTextField
          label="Description"
          errors={errors.findForField("description")}
          value={this.props.description}
          onChange={this.handleDescriptionChange} />

    );
  }

  handleTeamNameChange = (e) => {
    this.props.onChange('name', e.target.value);
  };

  handleDescriptionChange = (e) => {
    this.props.onChange('description', e.target.value);
  };
}

export default Form

<|next_version|>
import React from 'react';

import FormTextField from '../shared/FormTextField';
import ValidationErrors from '../../lib/ValidationErrors';

class Form extends React.Component {
  static propTypes = {
    name: React.PropTypes.string,
    description: React.PropTypes.string,
    errors: React.PropTypes.array,
    onChange: React.PropTypes.func
  };

  componentDidMount() {
    this.nameTextField.focus();
  }

  render() {
    var errors = new ValidationErrors(this.props.errors);

    return (

        <FormTextField
          label="Name"
          help="Pick a name for your team (you can even use :emoji:)"
          errors={errors.findForField("name")}
          value={this.props.name}
          onChange={this.handleTeamNameChange}
          ref={(c) => this.nameTextField = c} />

        <FormTextField
          label="Description"
          help="Describe what this team is all about"
          errors={errors.findForField("description")}
          value={this.props.description}
          onChange={this.handleDescriptionChange} />

    );
  }

  handleTeamNameChange = (e) => {
    this.props.onChange('name', e.target.value);
  };

  handleDescriptionChange = (e) => {
    this.props.onChange('description', e.target.value);
  };
}

export default Form


[server] Use multipleStatements mode in account deletion indexes script

Summary: Without multipleStatements mode an error is returned `You have an error in your SQL syntax...`. Enabling multipleStatements fixes the issue.

Test Plan: Run the script and check if indexes were added

Reviewers: ashoat

Reviewed By: ashoat

Subscribers: KatPo, Adrian, atul

Differential Revision: https://phabricator.ashoat.com/D1319

--- server/src/scripts/add-indexes-for-account-deletion.js
+++ server/src/scripts/add-indexes-for-account-deletion.js
@@ -1,7 +1,12 @@
 // @flow

 import { dbQuery, SQL } from '../database/database';
+import { setScriptContext } from './script-context';
 import { main } from './utils';
+
+setScriptContext({
+  allowMultiStatementSQLQueries: true,
+});

 async function addIndexes() {
   await dbQuery(SQL`

<|original_code|>
// @flow

import { dbQuery, SQL } from '../database/database';
import { main } from './utils';

async function addIndexes() {
  await dbQuery(SQL`
    ALTER TABLE memberships ADD INDEX user (user);
    ALTER TABLE notifications ADD INDEX user (user);
    ALTER TABLE relationships_directed ADD UNIQUE user2_user1 (user2, user1);
  `);
}

main([addIndexes]);

<|edits_diff|>
--- server/src/scripts/add-indexes-for-account-deletion.js
+++ server/src/scripts/add-indexes-for-account-deletion.js
@@ -1,6 +1,7 @@
 // @flow

 import { dbQuery, SQL } from '../database/database';
+import { setScriptContext } from './script-context';
 import { main } from './utils';

 async function addIndexes() {
<|current_version|>
// @flow

import { dbQuery, SQL } from '../database/database';
import { setScriptContext } from './script-context';
import { main } from './utils';

async function addIndexes() {
  await dbQuery(SQL`
    ALTER TABLE memberships ADD INDEX user (user);
    ALTER TABLE notifications ADD INDEX user (user);
    ALTER TABLE relationships_directed ADD UNIQUE user2_user1 (user2, user1);
  `);
}

main([addIndexes]);

<|next_version|>
// @flow

import { dbQuery, SQL } from '../database/database';
import { setScriptContext } from './script-context';
import { main } from './utils';

setScriptContext({
  allowMultiStatementSQLQueries: true,
});

async function addIndexes() {
  await dbQuery(SQL`
    ALTER TABLE memberships ADD INDEX user (user);
    ALTER TABLE notifications ADD INDEX user (user);
    ALTER TABLE relationships_directed ADD UNIQUE user2_user1 (user2, user1);
  `);
}

main([addIndexes]);


Hide output textarea by default

JsMinify: Hide output textarea by default

--- share/goodie/js_minify/js_minify.js
+++ share/goodie/js_minify/js_minify.js
@@ -24,6 +24,9 @@

             $main.toggleClass('c-base');

+            // hide output textarea by default
+            $output.css('display', 'none');
+
             $.getScript('http://sahildua.com/js/prettydiff.min.js', function() {
                 // Add click handler for the minify button
                 $minifyButton.click(function() {
@@ -37,6 +40,9 @@
                     // Operate using the prettydiff function provided by the library
                     var output = prettydiff(args);

+                    // hide output textarea by default
+                    $output.css('display', 'inline');
+
                     // Add the output to output textarea field
                     $output.val(output);
                 });

<|original_code|>
DDH.js_minify = DDH.js_minify || {};

"use strict";

DDH.js_minify.build = function(ops) {

    var shown = false;

    return {
        onShow: function() {
            // make sure this function is run only once, the first time
            // the IA is shown otherwise things will get initialized more than once
            if (shown)
                return;

            // set the flag to true so it doesn't get run again
            shown = true;

            var $dom = $('#zci-js_minify'),
                $main = $dom.find('.zci__main'),
                $minifyButton = $dom.find('.js_minify__action'),
                $input = $dom.find('#js_minify__input'),
                $output = $dom.find('#js_minify__output');

            $main.toggleClass('c-base');

            $.getScript('http://sahildua.com/js/prettydiff.min.js', function() {
                // Add click handler for the minify button
                $minifyButton.click(function() {
                    // Set config options for minify operation
                    var args = {
                        mode: "minify",
                        lang: "javascript",
                        source: $input.val()
                    };

                    // Operate using the prettydiff function provided by the library
                    var output = prettydiff(args);

                    // Add the output to output textarea field
                    $output.val(output);
                });
            })
        }
    };
};

<|edits_diff|>
--- share/goodie/js_minify/js_minify.js
+++ share/goodie/js_minify/js_minify.js
@@ -24,6 +24,9 @@

             $main.toggleClass('c-base');

+            // hide output textarea by default
+            $output.css('display', 'none');
+
             $.getScript('http://sahildua.com/js/prettydiff.min.js', function() {
                 // Add click handler for the minify button
                 $minifyButton.click(function() {
<|current_version|>
DDH.js_minify = DDH.js_minify || {};

"use strict";

DDH.js_minify.build = function(ops) {

    var shown = false;

    return {
        onShow: function() {
            // make sure this function is run only once, the first time
            // the IA is shown otherwise things will get initialized more than once
            if (shown)
                return;

            // set the flag to true so it doesn't get run again
            shown = true;

            var $dom = $('#zci-js_minify'),
                $main = $dom.find('.zci__main'),
                $minifyButton = $dom.find('.js_minify__action'),
                $input = $dom.find('#js_minify__input'),
                $output = $dom.find('#js_minify__output');

            $main.toggleClass('c-base');

            // hide output textarea by default
            $output.css('display', 'none');

            $.getScript('http://sahildua.com/js/prettydiff.min.js', function() {
                // Add click handler for the minify button
                $minifyButton.click(function() {
                    // Set config options for minify operation
                    var args = {
                        mode: "minify",
                        lang: "javascript",
                        source: $input.val()
                    };

                    // Operate using the prettydiff function provided by the library
                    var output = prettydiff(args);

                    // Add the output to output textarea field
                    $output.val(output);
                });
            })
        }
    };
};

<|next_version|>
DDH.js_minify = DDH.js_minify || {};

"use strict";

DDH.js_minify.build = function(ops) {

    var shown = false;

    return {
        onShow: function() {
            // make sure this function is run only once, the first time
            // the IA is shown otherwise things will get initialized more than once
            if (shown)
                return;

            // set the flag to true so it doesn't get run again
            shown = true;

            var $dom = $('#zci-js_minify'),
                $main = $dom.find('.zci__main'),
                $minifyButton = $dom.find('.js_minify__action'),
                $input = $dom.find('#js_minify__input'),
                $output = $dom.find('#js_minify__output');

            $main.toggleClass('c-base');

            // hide output textarea by default
            $output.css('display', 'none');

            $.getScript('http://sahildua.com/js/prettydiff.min.js', function() {
                // Add click handler for the minify button
                $minifyButton.click(function() {
                    // Set config options for minify operation
                    var args = {
                        mode: "minify",
                        lang: "javascript",
                        source: $input.val()
                    };

                    // Operate using the prettydiff function provided by the library
                    var output = prettydiff(args);

                    // hide output textarea by default
                    $output.css('display', 'inline');

                    // Add the output to output textarea field
                    $output.val(output);
                });
            })
        }
    };
};


Fix imaginary constant gate not being drawn as an octagon

--- src/gates/Joke_ImaginaryGate.js
+++ src/gates/Joke_ImaginaryGate.js
@@ -15,11 +15,16 @@
 import {GateBuilder} from "src/circuit/Gate.js"
 import {Matrix} from "src/math/Matrix.js"
 import {Complex} from "src/math/Complex.js"
+import {GatePainting} from "src/draw/GatePainting.js"

 const ImaginaryGate = new GateBuilder().
     setSerializedIdAndSymbol("i").
     setTitle("Imaginary Gate").
     setBlurb("Phases everything by i.").
+    setDrawer(args => {
+        GatePainting.paintLocationIndependentFrame(args);
+        GatePainting.paintGateSymbol(args);
+    }).
     setKnownEffectToMatrix(Matrix.square(Complex.I, 0, 0, Complex.I)).
     gate;

<|original_code|>
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {GateBuilder} from "src/circuit/Gate.js"
import {Matrix} from "src/math/Matrix.js"
import {Complex} from "src/math/Complex.js"

const ImaginaryGate = new GateBuilder().
    setSerializedIdAndSymbol("i").
    setTitle("Imaginary Gate").
    setBlurb("Phases everything by i.").
    setKnownEffectToMatrix(Matrix.square(Complex.I, 0, 0, Complex.I)).
    gate;

export {ImaginaryGate}

<|edits_diff|>
--- src/gates/Joke_ImaginaryGate.js
+++ src/gates/Joke_ImaginaryGate.js
@@ -15,6 +15,7 @@
 import {GateBuilder} from "src/circuit/Gate.js"
 import {Matrix} from "src/math/Matrix.js"
 import {Complex} from "src/math/Complex.js"
+import {GatePainting} from "src/draw/GatePainting.js"

 const ImaginaryGate = new GateBuilder().
     setSerializedIdAndSymbol("i").
<|current_version|>
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {GateBuilder} from "src/circuit/Gate.js"
import {Matrix} from "src/math/Matrix.js"
import {Complex} from "src/math/Complex.js"
import {GatePainting} from "src/draw/GatePainting.js"

const ImaginaryGate = new GateBuilder().
    setSerializedIdAndSymbol("i").
    setTitle("Imaginary Gate").
    setBlurb("Phases everything by i.").
    setKnownEffectToMatrix(Matrix.square(Complex.I, 0, 0, Complex.I)).
    gate;

export {ImaginaryGate}

<|next_version|>
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {GateBuilder} from "src/circuit/Gate.js"
import {Matrix} from "src/math/Matrix.js"
import {Complex} from "src/math/Complex.js"
import {GatePainting} from "src/draw/GatePainting.js"

const ImaginaryGate = new GateBuilder().
    setSerializedIdAndSymbol("i").
    setTitle("Imaginary Gate").
    setBlurb("Phases everything by i.").
    setDrawer(args => {
        GatePainting.paintLocationIndependentFrame(args);
        GatePainting.paintGateSymbol(args);
    }).
    setKnownEffectToMatrix(Matrix.square(Complex.I, 0, 0, Complex.I)).
    gate;

export {ImaginaryGate}


Add debug output to console in onBeforeSendRequestHeaders callback

--- adressaway.js
+++ adressaway.js
@@ -16,8 +16,14 @@
     author.
 */

+var debug = true;
+
 chrome.webRequest.onBeforeSendHeaders.addListener(
     function (details) {
+        if (debug) {
+            console.log("Intercepting request headers, adding google referrer.")
+        }
+
         details.requestHeaders.push(
             {
                 name: "Referer",

<|original_code|>
/*
    Author: mythern

    Copyright (C) 2014, MIT License
    http://www.opensource.org/licenses/mit-license.php

    Adressaway is provided free of charge, to any person obtaining a copy
    of this software and associated documentation files, to deal in the Software
    without restriction, including without limitation the rights to use, copy,
    modify, merge, publish, distribute, sublicense, and/or sell copies of the software,
    and to permit persons to whom the software is furnished todo so, subject to the
    following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial parts of the software, including credits to the original
    author.
*/

chrome.webRequest.onBeforeSendHeaders.addListener(
    function (details) {
        details.requestHeaders.push(
            {
                name: "Referer",
                value: "https://www.google.com/"
            }
        )
        return {
            requestHeaders: details.requestHeaders
        }
    },
    {
        urls: ["*://*.adressa.no/*"]
    },
    ["blocking", "requestHeaders"]
)

<|edits_diff|>
--- adressaway.js
+++ adressaway.js
@@ -16,6 +16,8 @@
     author.
 */

+var debug = true;
+
 chrome.webRequest.onBeforeSendHeaders.addListener(
     function (details) {
         details.requestHeaders.push(
<|current_version|>
/*
    Author: mythern

    Copyright (C) 2014, MIT License
    http://www.opensource.org/licenses/mit-license.php

    Adressaway is provided free of charge, to any person obtaining a copy
    of this software and associated documentation files, to deal in the Software
    without restriction, including without limitation the rights to use, copy,
    modify, merge, publish, distribute, sublicense, and/or sell copies of the software,
    and to permit persons to whom the software is furnished todo so, subject to the
    following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial parts of the software, including credits to the original
    author.
*/

var debug = true;

chrome.webRequest.onBeforeSendHeaders.addListener(
    function (details) {
        details.requestHeaders.push(
            {
                name: "Referer",
                value: "https://www.google.com/"
            }
        )
        return {
            requestHeaders: details.requestHeaders
        }
    },
    {
        urls: ["*://*.adressa.no/*"]
    },
    ["blocking", "requestHeaders"]
)

<|next_version|>
/*
    Author: mythern

    Copyright (C) 2014, MIT License
    http://www.opensource.org/licenses/mit-license.php

    Adressaway is provided free of charge, to any person obtaining a copy
    of this software and associated documentation files, to deal in the Software
    without restriction, including without limitation the rights to use, copy,
    modify, merge, publish, distribute, sublicense, and/or sell copies of the software,
    and to permit persons to whom the software is furnished todo so, subject to the
    following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial parts of the software, including credits to the original
    author.
*/

var debug = true;

chrome.webRequest.onBeforeSendHeaders.addListener(
    function (details) {
        if (debug) {
            console.log("Intercepting request headers, adding google referrer.")
        }

        details.requestHeaders.push(
            {
                name: "Referer",
                value: "https://www.google.com/"
            }
        )
        return {
            requestHeaders: details.requestHeaders
        }
    },
    {
        urls: ["*://*.adressa.no/*"]
    },
    ["blocking", "requestHeaders"]
)


Add standard timing for test runs.

Add standard timing for test runs.

I'm trying not to optimize every single Lore program I write, but I like
to at least have some overview of the general speed of different
programs.

--- runtime/lore.js
+++ runtime/lore.js
@@ -1,3 +1,10 @@
 import { test } from '../lore-program.js';

+function toMus(before, after) {
+  return Math.round((after - before) * 1000);
+}
+
+const before = performance.now();
 test();
+const after = performance.now();
+console.log('Running test took ~' + toMus(before, after) + 'µs.');

<|original_code|>
import { test } from '../lore-program.js';

test();

<|edits_diff|>
--- runtime/lore.js
+++ runtime/lore.js
@@ -1,3 +1,8 @@
 import { test } from '../lore-program.js';

+function toMus(before, after) {
+  return Math.round((after - before) * 1000);
+}
+
+const before = performance.now();
 test();
<|current_version|>
import { test } from '../lore-program.js';

function toMus(before, after) {
  return Math.round((after - before) * 1000);
}

const before = performance.now();
test();

<|next_version|>
import { test } from '../lore-program.js';

function toMus(before, after) {
  return Math.round((after - before) * 1000);
}

const before = performance.now();
test();
const after = performance.now();
console.log('Running test took ~' + toMus(before, after) + 'µs.');


Add SearchControls to search page

Add SearchControls to search page

Refs #29.

--- app/containers/SearchPage.js
+++ app/containers/SearchPage.js
@@ -6,6 +6,7 @@
 import { fetchResources } from 'actions/resourceActions';
 import { fetchUnits } from 'actions/unitActions';
 import SearchResults from 'components/search/SearchResults';
+import SearchControls from 'containers/SearchControls';
 import { searchPageSelectors } from 'selectors/searchPageSelectors';

 export class UnconnectedSearchPage extends Component {
@@ -22,6 +23,7 @@
       <DocumentTitle title="Haku - Respa">

Haku

+          <SearchControls />

Hakutulokset

           <SearchResults
             isFetching={isFetchingSearchResults}

<|original_code|>
import React, { Component, PropTypes } from 'react';
import DocumentTitle from 'react-document-title';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { fetchResources } from 'actions/resourceActions';
import { fetchUnits } from 'actions/unitActions';
import SearchResults from 'components/search/SearchResults';
import { searchPageSelectors } from 'selectors/searchPageSelectors';

export class UnconnectedSearchPage extends Component {
  componentDidMount() {
    const { actions } = this.props;
    actions.fetchResources();
    actions.fetchUnits();
  }

  render() {
    const { isFetchingSearchResults, results, units } = this.props;

    return (
      <DocumentTitle title="Haku - Respa">

Haku

Hakutulokset

          <SearchResults
            isFetching={isFetchingSearchResults}
            results={results}
            units={units}
          />

      </DocumentTitle>
    );
  }
}

UnconnectedSearchPage.propTypes = {
  actions: PropTypes.object.isRequired,
  isFetchingSearchResults: PropTypes.bool,
  results: PropTypes.array.isRequired,
  units: PropTypes.object.isRequired,
};

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators({ fetchResources, fetchUnits }, dispatch) };
}

export default connect(searchPageSelectors, mapDispatchToProps)(UnconnectedSearchPage);

<|edits_diff|>
--- app/containers/SearchPage.js
+++ app/containers/SearchPage.js
@@ -6,6 +6,7 @@
 import { fetchResources } from 'actions/resourceActions';
 import { fetchUnits } from 'actions/unitActions';
 import SearchResults from 'components/search/SearchResults';
+import SearchControls from 'containers/SearchControls';
 import { searchPageSelectors } from 'selectors/searchPageSelectors';

 export class UnconnectedSearchPage extends Component {
<|current_version|>
import React, { Component, PropTypes } from 'react';
import DocumentTitle from 'react-document-title';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { fetchResources } from 'actions/resourceActions';
import { fetchUnits } from 'actions/unitActions';
import SearchResults from 'components/search/SearchResults';
import SearchControls from 'containers/SearchControls';
import { searchPageSelectors } from 'selectors/searchPageSelectors';

export class UnconnectedSearchPage extends Component {
  componentDidMount() {
    const { actions } = this.props;
    actions.fetchResources();
    actions.fetchUnits();
  }

  render() {
    const { isFetchingSearchResults, results, units } = this.props;

    return (
      <DocumentTitle title="Haku - Respa">

Haku

Hakutulokset

          <SearchResults
            isFetching={isFetchingSearchResults}
            results={results}
            units={units}
          />

      </DocumentTitle>
    );
  }
}

UnconnectedSearchPage.propTypes = {
  actions: PropTypes.object.isRequired,
  isFetchingSearchResults: PropTypes.bool,
  results: PropTypes.array.isRequired,
  units: PropTypes.object.isRequired,
};

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators({ fetchResources, fetchUnits }, dispatch) };
}

export default connect(searchPageSelectors, mapDispatchToProps)(UnconnectedSearchPage);

<|next_version|>
import React, { Component, PropTypes } from 'react';
import DocumentTitle from 'react-document-title';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { fetchResources } from 'actions/resourceActions';
import { fetchUnits } from 'actions/unitActions';
import SearchResults from 'components/search/SearchResults';
import SearchControls from 'containers/SearchControls';
import { searchPageSelectors } from 'selectors/searchPageSelectors';

export class UnconnectedSearchPage extends Component {
  componentDidMount() {
    const { actions } = this.props;
    actions.fetchResources();
    actions.fetchUnits();
  }

  render() {
    const { isFetchingSearchResults, results, units } = this.props;

    return (
      <DocumentTitle title="Haku - Respa">

Haku

          <SearchControls />

Hakutulokset

          <SearchResults
            isFetching={isFetchingSearchResults}
            results={results}
            units={units}
          />

      </DocumentTitle>
    );
  }
}

UnconnectedSearchPage.propTypes = {
  actions: PropTypes.object.isRequired,
  isFetchingSearchResults: PropTypes.bool,
  results: PropTypes.array.isRequired,
  units: PropTypes.object.isRequired,
};

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators({ fetchResources, fetchUnits }, dispatch) };
}

export default connect(searchPageSelectors, mapDispatchToProps)(UnconnectedSearchPage);


Add "push" and "replace" handy methods

Add "push" and "replace" handy methods

There are also two handy methods of history API that allow not to specify state object during transitions:

* `push(path[, state])`
* `replace(path[, state])`

--- src/actionCreators.js
+++ src/actionCreators.js
@@ -42,7 +42,9 @@
 }

 export const pushState = historyAPI('pushState');
+export const push = historyAPI('push');
 export const replaceState = historyAPI('replaceState');
+export const replace = historyAPI('replace');
 export const setState = historyAPI('setState');
 export const go = historyAPI('go');
 export const goBack = historyAPI('goBack');

<|original_code|>
import { ROUTER_DID_CHANGE, REPLACE_ROUTES, HISTORY_API } from './constants';

/**
 * Action creator for signaling that the router has changed.
 * @private
 * @param  {RouterState} state - New router state
 * @return {Action} Action object
 */
export function routerDidChange(state) {
  return {
    type: ROUTER_DID_CHANGE,
    payload: state
  };
}

/**
 * Action creator that replaces the current route config
 * @private
 * @param {Array<Route>|ReactElement} routes - New routes
 */
export function replaceRoutes(routes) {
  return {
    type: REPLACE_ROUTES,
    payload: routes
  };
}

/**
 * Creates an action creator for calling a history API method.
 * @param {string} method - Name of method
 * @returns {ActionCreator} Action creator with same parameters as corresponding
 * history method
 */
export function historyAPI(method) {
  return (...args) => ({
    type: HISTORY_API,
    payload: {
      method,
      args
    }
  });
}

export const pushState = historyAPI('pushState');
export const replaceState = historyAPI('replaceState');
export const setState = historyAPI('setState');
export const go = historyAPI('go');
export const goBack = historyAPI('goBack');
export const goForward = historyAPI('goForward');

<|edits_diff|>
--- src/actionCreators.js
+++ src/actionCreators.js
@@ -42,6 +42,7 @@
 }

 export const pushState = historyAPI('pushState');
+export const push = historyAPI('push');
 export const replaceState = historyAPI('replaceState');
 export const setState = historyAPI('setState');
 export const go = historyAPI('go');
<|current_version|>
import { ROUTER_DID_CHANGE, REPLACE_ROUTES, HISTORY_API } from './constants';

/**
 * Action creator for signaling that the router has changed.
 * @private
 * @param  {RouterState} state - New router state
 * @return {Action} Action object
 */
export function routerDidChange(state) {
  return {
    type: ROUTER_DID_CHANGE,
    payload: state
  };
}

/**
 * Action creator that replaces the current route config
 * @private
 * @param {Array<Route>|ReactElement} routes - New routes
 */
export function replaceRoutes(routes) {
  return {
    type: REPLACE_ROUTES,
    payload: routes
  };
}

/**
 * Creates an action creator for calling a history API method.
 * @param {string} method - Name of method
 * @returns {ActionCreator} Action creator with same parameters as corresponding
 * history method
 */
export function historyAPI(method) {
  return (...args) => ({
    type: HISTORY_API,
    payload: {
      method,
      args
    }
  });
}

export const pushState = historyAPI('pushState');
export const push = historyAPI('push');
export const replaceState = historyAPI('replaceState');
export const setState = historyAPI('setState');
export const go = historyAPI('go');
export const goBack = historyAPI('goBack');
export const goForward = historyAPI('goForward');

<|next_version|>
import { ROUTER_DID_CHANGE, REPLACE_ROUTES, HISTORY_API } from './constants';

/**
 * Action creator for signaling that the router has changed.
 * @private
 * @param  {RouterState} state - New router state
 * @return {Action} Action object
 */
export function routerDidChange(state) {
  return {
    type: ROUTER_DID_CHANGE,
    payload: state
  };
}

/**
 * Action creator that replaces the current route config
 * @private
 * @param {Array<Route>|ReactElement} routes - New routes
 */
export function replaceRoutes(routes) {
  return {
    type: REPLACE_ROUTES,
    payload: routes
  };
}

/**
 * Creates an action creator for calling a history API method.
 * @param {string} method - Name of method
 * @returns {ActionCreator} Action creator with same parameters as corresponding
 * history method
 */
export function historyAPI(method) {
  return (...args) => ({
    type: HISTORY_API,
    payload: {
      method,
      args
    }
  });
}

export const pushState = historyAPI('pushState');
export const push = historyAPI('push');
export const replaceState = historyAPI('replaceState');
export const replace = historyAPI('replace');
export const setState = historyAPI('setState');
export const go = historyAPI('go');
export const goBack = historyAPI('goBack');
export const goForward = historyAPI('goForward');


Allow libraries like moment.js to attach to window instead of global.

Closes #5

--- blueprints/node-webkit/files/vendor/node-webkit/shim.js
+++ blueprints/node-webkit/files/vendor/node-webkit/shim.js
@@ -1,11 +1,19 @@
 (function(window) {
   if (!window.nwDispatcher) return;
+
+  // Move `global.window` out of the way as it causes some third-party
+  // libraries to expose themselves via `global` instead of `window`.
+  var globalWindow = global.window;
+  delete global.window;

   // Rename node's `require` to avoid conflicts with AMD's `require`.
   var requireNode = window.requireNode = window.require;
   delete window.require;

   window.addEventListener('load', function() {
+    // Restore `global`.
+    global.window = globalWindow;
+
     // Once the document is loaded, make node's `require` function
     // available again. This is required for certain node modules that
     // can't work with the alias, and for nw.js's GUI to function properly

<|original_code|>
(function(window) {
  if (!window.nwDispatcher) return;

  // Rename node's `require` to avoid conflicts with AMD's `require`.
  var requireNode = window.requireNode = window.require;
  delete window.require;

  window.addEventListener('load', function() {
    // Once the document is loaded, make node's `require` function
    // available again. This is required for certain node modules that
    // can't work with the alias, and for nw.js's GUI to function properly
    // (e.g. opening the dev tools from the window toolbar).
    var requireAMD = window.require;

    if (requireAMD) {
      window.require = function() {
        try {
          return requireAMD.apply(null, arguments);
        } catch (e) {
          return requireNode.apply(null, arguments);
        }
      };
    } else {
      window.require = requireNode;
    }
  });
})(this);

<|edits_diff|>
--- blueprints/node-webkit/files/vendor/node-webkit/shim.js
+++ blueprints/node-webkit/files/vendor/node-webkit/shim.js
@@ -1,5 +1,10 @@
 (function(window) {
   if (!window.nwDispatcher) return;
+
+  // Move `global.window` out of the way as it causes some third-party
+  // libraries to expose themselves via `global` instead of `window`.
+  var globalWindow = global.window;
+  delete global.window;

   // Rename node's `require` to avoid conflicts with AMD's `require`.
   var requireNode = window.requireNode = window.require;
<|current_version|>
(function(window) {
  if (!window.nwDispatcher) return;

  // Move `global.window` out of the way as it causes some third-party
  // libraries to expose themselves via `global` instead of `window`.
  var globalWindow = global.window;
  delete global.window;

  // Rename node's `require` to avoid conflicts with AMD's `require`.
  var requireNode = window.requireNode = window.require;
  delete window.require;

  window.addEventListener('load', function() {
    // Once the document is loaded, make node's `require` function
    // available again. This is required for certain node modules that
    // can't work with the alias, and for nw.js's GUI to function properly
    // (e.g. opening the dev tools from the window toolbar).
    var requireAMD = window.require;

    if (requireAMD) {
      window.require = function() {
        try {
          return requireAMD.apply(null, arguments);
        } catch (e) {
          return requireNode.apply(null, arguments);
        }
      };
    } else {
      window.require = requireNode;
    }
  });
})(this);

<|next_version|>
(function(window) {
  if (!window.nwDispatcher) return;

  // Move `global.window` out of the way as it causes some third-party
  // libraries to expose themselves via `global` instead of `window`.
  var globalWindow = global.window;
  delete global.window;

  // Rename node's `require` to avoid conflicts with AMD's `require`.
  var requireNode = window.requireNode = window.require;
  delete window.require;

  window.addEventListener('load', function() {
    // Restore `global`.
    global.window = globalWindow;

    // Once the document is loaded, make node's `require` function
    // available again. This is required for certain node modules that
    // can't work with the alias, and for nw.js's GUI to function properly
    // (e.g. opening the dev tools from the window toolbar).
    var requireAMD = window.require;

    if (requireAMD) {
      window.require = function() {
        try {
          return requireAMD.apply(null, arguments);
        } catch (e) {
          return requireNode.apply(null, arguments);
        }
      };
    } else {
      window.require = requireNode;
    }
  });
})(this);


Add debug logging to stats transform.

Add debug logging to stats transform.

--- src/main/transforms/StatsTransform.js
+++ src/main/transforms/StatsTransform.js
@@ -23,9 +23,11 @@

       switch (type) {
         case 'increment':
+          this._logger.debug('Sending stats increment.', {name, value});
           this._stats.increment(name, value);
           break;
         case 'gauge':
+          this._logger.debug('Sending stats guage.', {name, value});
           this._stats.gauge(name, value);
           break;
       }

<|original_code|>
import Transform from './Transform';
import Stats from '../server/Stats';

export default class StatsTransform extends Transform {
  constructor(opts, config, logger) {
    super(opts, config, logger);

    this._stats = new Stats(config, logger);
    this._type = this.getOption('type', 'increment');
    this._name = this.getOption('name');
    this._value = this.getOption('value', '1');
  }

  close() {
    this._stats.close();
  }

  process(event) {
    try {
      const value = parseInt(this._value.render(event), 10);
      const type = this._type.render(event);
      const name = this._name.render(event);

      switch (type) {
        case 'increment':
          this._stats.increment(name, value);
          break;
        case 'gauge':
          this._stats.gauge(name, value);
          break;
      }

      return this.emit(null);
    } catch (e) {
      return this.fail(e);
    }
  }
}

<|edits_diff|>
--- src/main/transforms/StatsTransform.js
+++ src/main/transforms/StatsTransform.js
@@ -23,6 +23,7 @@

       switch (type) {
         case 'increment':
+          this._logger.debug('Sending stats increment.', {name, value});
           this._stats.increment(name, value);
           break;
         case 'gauge':
<|current_version|>
import Transform from './Transform';
import Stats from '../server/Stats';

export default class StatsTransform extends Transform {
  constructor(opts, config, logger) {
    super(opts, config, logger);

    this._stats = new Stats(config, logger);
    this._type = this.getOption('type', 'increment');
    this._name = this.getOption('name');
    this._value = this.getOption('value', '1');
  }

  close() {
    this._stats.close();
  }

  process(event) {
    try {
      const value = parseInt(this._value.render(event), 10);
      const type = this._type.render(event);
      const name = this._name.render(event);

      switch (type) {
        case 'increment':
          this._logger.debug('Sending stats increment.', {name, value});
          this._stats.increment(name, value);
          break;
        case 'gauge':
          this._stats.gauge(name, value);
          break;
      }

      return this.emit(null);
    } catch (e) {
      return this.fail(e);
    }
  }
}

<|next_version|>
import Transform from './Transform';
import Stats from '../server/Stats';

export default class StatsTransform extends Transform {
  constructor(opts, config, logger) {
    super(opts, config, logger);

    this._stats = new Stats(config, logger);
    this._type = this.getOption('type', 'increment');
    this._name = this.getOption('name');
    this._value = this.getOption('value', '1');
  }

  close() {
    this._stats.close();
  }

  process(event) {
    try {
      const value = parseInt(this._value.render(event), 10);
      const type = this._type.render(event);
      const name = this._name.render(event);

      switch (type) {
        case 'increment':
          this._logger.debug('Sending stats increment.', {name, value});
          this._stats.increment(name, value);
          break;
        case 'gauge':
          this._logger.debug('Sending stats guage.', {name, value});
          this._stats.gauge(name, value);
          break;
      }

      return this.emit(null);
    } catch (e) {
      return this.fail(e);
    }
  }
}


Add performance monitoring for Best Of

Add performance monitoring for Best Of

--- app/controllers/api/v2/TimelinesController.js
+++ app/controllers/api/v2/TimelinesController.js
@@ -1,3 +1,4 @@
+import monitor from 'monitor-dog'
 import { dbAdapter } from '../../../models'
 import { reportError } from '../../../support/exceptions'
 import { serializePostsCollection } from '../../../serializers/v2/post';
@@ -10,6 +11,8 @@
   }

   bestOf = async (req, res) => {
+    const timer = monitor.timer('timelines.bestof-time')
+
     try {
       const DEFAULT_LIMIT = 30;

@@ -22,8 +25,11 @@
       const postsCollectionJson = await serializePostsCollection(postsObjects);

       res.jsonp(postsCollectionJson);
+      monitor.increment('timelines.bestof-requests')
     } catch (e) {
       reportError(res)(e);
+    } finally {
+      timer.stop()
     }
   };
 }

<|original_code|>
import { dbAdapter } from '../../../models'
import { reportError } from '../../../support/exceptions'
import { serializePostsCollection } from '../../../serializers/v2/post';

export default class TimelinesController {
  app = null;

  constructor(app) {
    this.app = app;
  }

  bestOf = async (req, res) => {
    try {
      const DEFAULT_LIMIT = 30;

      const currentUserId = req.user ? req.user.id : null;
      const offset = parseInt(req.query.offset, 10) || 0;
      const limit =  parseInt(req.query.limit, 10) || DEFAULT_LIMIT;

      const foundPosts = await dbAdapter.bestPosts(req.user, offset, limit);
      const postsObjects = dbAdapter.initRawPosts(foundPosts, { currentUser: currentUserId });
      const postsCollectionJson = await serializePostsCollection(postsObjects);

      res.jsonp(postsCollectionJson);
    } catch (e) {
      reportError(res)(e);
    }
  };
}

<|edits_diff|>
--- app/controllers/api/v2/TimelinesController.js
+++ app/controllers/api/v2/TimelinesController.js
@@ -1,3 +1,4 @@
+import monitor from 'monitor-dog'
 import { dbAdapter } from '../../../models'
 import { reportError } from '../../../support/exceptions'
 import { serializePostsCollection } from '../../../serializers/v2/post';
@@ -10,6 +11,8 @@
   }

   bestOf = async (req, res) => {
+    const timer = monitor.timer('timelines.bestof-time')
+
     try {
       const DEFAULT_LIMIT = 30;

@@ -22,6 +25,7 @@
       const postsCollectionJson = await serializePostsCollection(postsObjects);

       res.jsonp(postsCollectionJson);
+      monitor.increment('timelines.bestof-requests')
     } catch (e) {
       reportError(res)(e);
     }
<|current_version|>
import monitor from 'monitor-dog'
import { dbAdapter } from '../../../models'
import { reportError } from '../../../support/exceptions'
import { serializePostsCollection } from '../../../serializers/v2/post';

export default class TimelinesController {
  app = null;

  constructor(app) {
    this.app = app;
  }

  bestOf = async (req, res) => {
    const timer = monitor.timer('timelines.bestof-time')

    try {
      const DEFAULT_LIMIT = 30;

      const currentUserId = req.user ? req.user.id : null;
      const offset = parseInt(req.query.offset, 10) || 0;
      const limit =  parseInt(req.query.limit, 10) || DEFAULT_LIMIT;

      const foundPosts = await dbAdapter.bestPosts(req.user, offset, limit);
      const postsObjects = dbAdapter.initRawPosts(foundPosts, { currentUser: currentUserId });
      const postsCollectionJson = await serializePostsCollection(postsObjects);

      res.jsonp(postsCollectionJson);
      monitor.increment('timelines.bestof-requests')
    } catch (e) {
      reportError(res)(e);
    }
  };
}

<|next_version|>
import monitor from 'monitor-dog'
import { dbAdapter } from '../../../models'
import { reportError } from '../../../support/exceptions'
import { serializePostsCollection } from '../../../serializers/v2/post';

export default class TimelinesController {
  app = null;

  constructor(app) {
    this.app = app;
  }

  bestOf = async (req, res) => {
    const timer = monitor.timer('timelines.bestof-time')

    try {
      const DEFAULT_LIMIT = 30;

      const currentUserId = req.user ? req.user.id : null;
      const offset = parseInt(req.query.offset, 10) || 0;
      const limit =  parseInt(req.query.limit, 10) || DEFAULT_LIMIT;

      const foundPosts = await dbAdapter.bestPosts(req.user, offset, limit);
      const postsObjects = dbAdapter.initRawPosts(foundPosts, { currentUser: currentUserId });
      const postsCollectionJson = await serializePostsCollection(postsObjects);

      res.jsonp(postsCollectionJson);
      monitor.increment('timelines.bestof-requests')
    } catch (e) {
      reportError(res)(e);
    } finally {
      timer.stop()
    }
  };
}


Improve color display, remove gray from the texts

--- netease_music_helper.user.js
+++ netease_music_helper.user.js
@@ -7,8 +7,14 @@
 // @author       Bob
 // @match        http://music.163.com/*
 // @grant        none
+// @require http://code.jquery.com/jquery-latest.js
 // ==/UserScript==
 window.GAbroad=false;
+
+/**Then remove the gray color**/
+$( window ).load(function() {
+   $(".js-dis").removeClass('js-dis');
+});
 /**
 如果是在浏览器控制台，你也可以把这句代码：window.GAboard=window.contentFrame.GAbroad = false; 执行，目前来说，可以达到相同的效果
 **/

<|original_code|>
// ==UserScript==
// @name       网易云音乐海外助手
// @namespace    http://www.sudodev.cn/music-163-access-abroad/
// @updateURL http://git.oschina.net/bobshi/NetEaseMusicHelper/raw/master/netease_music_helper.user.js
// @version      0.2
// @description  让海外的用户能正常听网易云音乐
// @author       Bob
// @match        http://music.163.com/*
// @grant        none
// ==/UserScript==
window.GAbroad=false;
/**
如果是在浏览器控制台，你也可以把这句代码：window.GAboard=window.contentFrame.GAbroad = false; 执行，目前来说，可以达到相同的效果
**/

<|edits_diff|>
--- netease_music_helper.user.js
+++ netease_music_helper.user.js
@@ -7,6 +7,7 @@
 // @author       Bob
 // @match        http://music.163.com/*
 // @grant        none
+// @require http://code.jquery.com/jquery-latest.js
 // ==/UserScript==
 window.GAbroad=false;
 /**
<|current_version|>
// ==UserScript==
// @name       网易云音乐海外助手
// @namespace    http://www.sudodev.cn/music-163-access-abroad/
// @updateURL http://git.oschina.net/bobshi/NetEaseMusicHelper/raw/master/netease_music_helper.user.js
// @version      0.2
// @description  让海外的用户能正常听网易云音乐
// @author       Bob
// @match        http://music.163.com/*
// @grant        none
// @require http://code.jquery.com/jquery-latest.js
// ==/UserScript==
window.GAbroad=false;
/**
如果是在浏览器控制台，你也可以把这句代码：window.GAboard=window.contentFrame.GAbroad = false; 执行，目前来说，可以达到相同的效果
**/

<|next_version|>
// ==UserScript==
// @name       网易云音乐海外助手
// @namespace    http://www.sudodev.cn/music-163-access-abroad/
// @updateURL http://git.oschina.net/bobshi/NetEaseMusicHelper/raw/master/netease_music_helper.user.js
// @version      0.2
// @description  让海外的用户能正常听网易云音乐
// @author       Bob
// @match        http://music.163.com/*
// @grant        none
// @require http://code.jquery.com/jquery-latest.js
// ==/UserScript==
window.GAbroad=false;

/**Then remove the gray color**/
$( window ).load(function() {
   $(".js-dis").removeClass('js-dis');
});
/**
如果是在浏览器控制台，你也可以把这句代码：window.GAboard=window.contentFrame.GAbroad = false; 执行，目前来说，可以达到相同的效果
**/


Add print statements for logging lambda func

--- functions/tweetcount.js
+++ functions/tweetcount.js
@@ -20,14 +20,19 @@
         }
     };

+    console.log('Initializing https request...');
+
     https.get(options, function(res) {
+        console.log('https request sent! Waiting for response...');
         res.on('data', function(data) {
+            console.log('Data received');
             callback(null, {
                 statusCode: 200,
                 body: data
             })
         });
     }).on('error', function(err) {
+        console.log('Error in the https request');
         callback(err);
     });
 }

<|original_code|>
var https = require('https');

exports.handler = function(event, context, callback) {

    // This token should live as an environmental variable set in the
    // build config for Netlify.
    var bearerToken = process.env.TWITTER_BEARER_TOKEN;
    if (bearerToken == null) {
        callback('Could not find required TWITTER_BEARER_TOKEN environmental variable.');
    }

    var authToken = 'Bearer ' + bearerToken;
    var queryString = '?q=chano4mayor%20OR%20chance4mayor%20OR%20chanoformayor%20OR%20chanceformayor';

    var options = {
        hostname: 'api.twitter.com',
        path: '/1.1/search/tweets.json' + queryString,
        headers: {
            'Authorization': authToken
        }
    };

    https.get(options, function(res) {
        res.on('data', function(data) {
            callback(null, {
                statusCode: 200,
                body: data
            })
        });
    }).on('error', function(err) {
        callback(err);
    });
}

<|edits_diff|>
--- functions/tweetcount.js
+++ functions/tweetcount.js
@@ -20,8 +20,12 @@
         }
     };

+    console.log('Initializing https request...');
+
     https.get(options, function(res) {
+        console.log('https request sent! Waiting for response...');
         res.on('data', function(data) {
+            console.log('Data received');
             callback(null, {
                 statusCode: 200,
                 body: data
<|current_version|>
var https = require('https');

exports.handler = function(event, context, callback) {

    // This token should live as an environmental variable set in the
    // build config for Netlify.
    var bearerToken = process.env.TWITTER_BEARER_TOKEN;
    if (bearerToken == null) {
        callback('Could not find required TWITTER_BEARER_TOKEN environmental variable.');
    }

    var authToken = 'Bearer ' + bearerToken;
    var queryString = '?q=chano4mayor%20OR%20chance4mayor%20OR%20chanoformayor%20OR%20chanceformayor';

    var options = {
        hostname: 'api.twitter.com',
        path: '/1.1/search/tweets.json' + queryString,
        headers: {
            'Authorization': authToken
        }
    };

    console.log('Initializing https request...');

    https.get(options, function(res) {
        console.log('https request sent! Waiting for response...');
        res.on('data', function(data) {
            console.log('Data received');
            callback(null, {
                statusCode: 200,
                body: data
            })
        });
    }).on('error', function(err) {
        callback(err);
    });
}

<|next_version|>
var https = require('https');

exports.handler = function(event, context, callback) {

    // This token should live as an environmental variable set in the
    // build config for Netlify.
    var bearerToken = process.env.TWITTER_BEARER_TOKEN;
    if (bearerToken == null) {
        callback('Could not find required TWITTER_BEARER_TOKEN environmental variable.');
    }

    var authToken = 'Bearer ' + bearerToken;
    var queryString = '?q=chano4mayor%20OR%20chance4mayor%20OR%20chanoformayor%20OR%20chanceformayor';

    var options = {
        hostname: 'api.twitter.com',
        path: '/1.1/search/tweets.json' + queryString,
        headers: {
            'Authorization': authToken
        }
    };

    console.log('Initializing https request...');

    https.get(options, function(res) {
        console.log('https request sent! Waiting for response...');
        res.on('data', function(data) {
            console.log('Data received');
            callback(null, {
                statusCode: 200,
                body: data
            })
        });
    }).on('error', function(err) {
        console.log('Error in the https request');
        callback(err);
    });
}


Fix a redux-form onBlur related issue

Fix a redux-form onBlur related issue

issue: onBlur causes redux-form to fetch raw value (for example
12/08/2017) from DatePicker.

--- src/mui/input/DateInput.js
+++ src/mui/input/DateInput.js
@@ -18,6 +18,7 @@

 class DateInput extends Component {
     onChange = (_, date) => this.props.input.onChange(date);
+    onBlur = () => this.props.input.onBlur();

     render() {
         const { input, isRequired, label, meta: { touched, error }, options, source, elStyle, resource } = this.props;
@@ -31,6 +32,7 @@
             autoOk
             value={datify(input.value)}
             onChange={this.onChange}
+            onBlur={this.onBlur}
             style={elStyle}
             {...options}
         />);

<|original_code|>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import DatePicker from 'material-ui/DatePicker';
import FieldTitle from '../../util/FieldTitle';

export const datify = input => {
    if (!input) {
        return null;
    }

    const date = input instanceof Date ? input : new Date(input);
    if (isNaN(date)) {
        throw new Error(`Invalid date: ${input}`);
    }

    return date;
};

class DateInput extends Component {
    onChange = (_, date) => this.props.input.onChange(date);

    render() {
        const { input, isRequired, label, meta: { touched, error }, options, source, elStyle, resource } = this.props;

        return (<DatePicker
            {...input}
            errorText={touched && error}
            floatingLabelText={<FieldTitle label={label} source={source} resource={resource} isRequired={isRequired} />}
            DateTimeFormat={Intl.DateTimeFormat}
            container="inline"
            autoOk
            value={datify(input.value)}
            onChange={this.onChange}
            style={elStyle}
            {...options}
        />);
    }
}

DateInput.propTypes = {
    addField: PropTypes.bool.isRequired,
    elStyle: PropTypes.object,
    input: PropTypes.object,
    isRequired: PropTypes.bool,
    label: PropTypes.string,
    meta: PropTypes.object,
    options: PropTypes.object,
    resource: PropTypes.string,
    source: PropTypes.string,
};

DateInput.defaultProps = {
    addField: true,
    options: {},
};

export default DateInput;

<|edits_diff|>
--- src/mui/input/DateInput.js
+++ src/mui/input/DateInput.js
@@ -18,6 +18,7 @@

 class DateInput extends Component {
     onChange = (_, date) => this.props.input.onChange(date);
+    onBlur = () => this.props.input.onBlur();

     render() {
         const { input, isRequired, label, meta: { touched, error }, options, source, elStyle, resource } = this.props;
<|current_version|>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import DatePicker from 'material-ui/DatePicker';
import FieldTitle from '../../util/FieldTitle';

export const datify = input => {
    if (!input) {
        return null;
    }

    const date = input instanceof Date ? input : new Date(input);
    if (isNaN(date)) {
        throw new Error(`Invalid date: ${input}`);
    }

    return date;
};

class DateInput extends Component {
    onChange = (_, date) => this.props.input.onChange(date);
    onBlur = () => this.props.input.onBlur();

    render() {
        const { input, isRequired, label, meta: { touched, error }, options, source, elStyle, resource } = this.props;

        return (<DatePicker
            {...input}
            errorText={touched && error}
            floatingLabelText={<FieldTitle label={label} source={source} resource={resource} isRequired={isRequired} />}
            DateTimeFormat={Intl.DateTimeFormat}
            container="inline"
            autoOk
            value={datify(input.value)}
            onChange={this.onChange}
            style={elStyle}
            {...options}
        />);
    }
}

DateInput.propTypes = {
    addField: PropTypes.bool.isRequired,
    elStyle: PropTypes.object,
    input: PropTypes.object,
    isRequired: PropTypes.bool,
    label: PropTypes.string,
    meta: PropTypes.object,
    options: PropTypes.object,
    resource: PropTypes.string,
    source: PropTypes.string,
};

DateInput.defaultProps = {
    addField: true,
    options: {},
};

export default DateInput;

<|next_version|>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import DatePicker from 'material-ui/DatePicker';
import FieldTitle from '../../util/FieldTitle';

export const datify = input => {
    if (!input) {
        return null;
    }

    const date = input instanceof Date ? input : new Date(input);
    if (isNaN(date)) {
        throw new Error(`Invalid date: ${input}`);
    }

    return date;
};

class DateInput extends Component {
    onChange = (_, date) => this.props.input.onChange(date);
    onBlur = () => this.props.input.onBlur();

    render() {
        const { input, isRequired, label, meta: { touched, error }, options, source, elStyle, resource } = this.props;

        return (<DatePicker
            {...input}
            errorText={touched && error}
            floatingLabelText={<FieldTitle label={label} source={source} resource={resource} isRequired={isRequired} />}
            DateTimeFormat={Intl.DateTimeFormat}
            container="inline"
            autoOk
            value={datify(input.value)}
            onChange={this.onChange}
            onBlur={this.onBlur}
            style={elStyle}
            {...options}
        />);
    }
}

DateInput.propTypes = {
    addField: PropTypes.bool.isRequired,
    elStyle: PropTypes.object,
    input: PropTypes.object,
    isRequired: PropTypes.bool,
    label: PropTypes.string,
    meta: PropTypes.object,
    options: PropTypes.object,
    resource: PropTypes.string,
    source: PropTypes.string,
};

DateInput.defaultProps = {
    addField: true,
    options: {},
};

export default DateInput;


Store canVote in state when user connects

--- client/src/features/auth/reducer.js
+++ client/src/features/auth/reducer.js
@@ -9,6 +9,7 @@
   loggedIn: false,
   registered: false,
   authenticated: false,
+  canVote: false,
 };

 const auth = (state = defaultState, action) => {
@@ -16,6 +17,7 @@
     case AUTH_SIGNED_IN: {
       return {
         ...state,
+        canVote: action.data.canVote,
         username: action.data.username,
         fullName: action.data.full_name,
         loggedIn: true,

<|original_code|>
import { ADMIN_SIGNED_IN, AUTH_SIGNED_IN, AUTH_AUTHENTICATED } from 'common/actionTypes/auth';

const defaultState = {
  id: '',
  username: '',
  fullName: '',
  permissions: 0,
  reloadPage: false,
  loggedIn: false,
  registered: false,
  authenticated: false,
};

const auth = (state = defaultState, action) => {
  switch (action.type) {
    case AUTH_SIGNED_IN: {
      return {
        ...state,
        username: action.data.username,
        fullName: action.data.full_name,
        loggedIn: true,
        registered: action.data.completedRegistration,
        id: action.data.id,
        permissions: action.data.permissions,
      };
    }

    case AUTH_AUTHENTICATED: {
      return {
        ...state,
        authenticated: action.data.authenticated,
      };
    }

    case ADMIN_SIGNED_IN: {
      return {
        ...state,
        reloadPage: true,
      };
    }

    default:
      return state;
  }
};

export default auth;

<|edits_diff|>
--- client/src/features/auth/reducer.js
+++ client/src/features/auth/reducer.js
@@ -9,6 +9,7 @@
   loggedIn: false,
   registered: false,
   authenticated: false,
+  canVote: false,
 };

 const auth = (state = defaultState, action) => {
<|current_version|>
import { ADMIN_SIGNED_IN, AUTH_SIGNED_IN, AUTH_AUTHENTICATED } from 'common/actionTypes/auth';

const defaultState = {
  id: '',
  username: '',
  fullName: '',
  permissions: 0,
  reloadPage: false,
  loggedIn: false,
  registered: false,
  authenticated: false,
  canVote: false,
};

const auth = (state = defaultState, action) => {
  switch (action.type) {
    case AUTH_SIGNED_IN: {
      return {
        ...state,
        username: action.data.username,
        fullName: action.data.full_name,
        loggedIn: true,
        registered: action.data.completedRegistration,
        id: action.data.id,
        permissions: action.data.permissions,
      };
    }

    case AUTH_AUTHENTICATED: {
      return {
        ...state,
        authenticated: action.data.authenticated,
      };
    }

    case ADMIN_SIGNED_IN: {
      return {
        ...state,
        reloadPage: true,
      };
    }

    default:
      return state;
  }
};

export default auth;

<|next_version|>
import { ADMIN_SIGNED_IN, AUTH_SIGNED_IN, AUTH_AUTHENTICATED } from 'common/actionTypes/auth';

const defaultState = {
  id: '',
  username: '',
  fullName: '',
  permissions: 0,
  reloadPage: false,
  loggedIn: false,
  registered: false,
  authenticated: false,
  canVote: false,
};

const auth = (state = defaultState, action) => {
  switch (action.type) {
    case AUTH_SIGNED_IN: {
      return {
        ...state,
        canVote: action.data.canVote,
        username: action.data.username,
        fullName: action.data.full_name,
        loggedIn: true,
        registered: action.data.completedRegistration,
        id: action.data.id,
        permissions: action.data.permissions,
      };
    }

    case AUTH_AUTHENTICATED: {
      return {
        ...state,
        authenticated: action.data.authenticated,
      };
    }

    case ADMIN_SIGNED_IN: {
      return {
        ...state,
        reloadPage: true,
      };
    }

    default:
      return state;
  }
};

export default auth;


Add a link for a new post

Add a link for a new post

--- src/components/posts_index.js
+++ src/components/posts_index.js
@@ -1,6 +1,7 @@
 import _ from 'lodash';
 import React, { Component } from 'react';
 import { connect } from 'react-redux';
+import { Link } from 'react-router-dom';
 import { fetchPosts } from  '../actions';

 class PostsIndex extends Component {
@@ -21,6 +22,11 @@
   render() {
     return (

+

+          <Link className="btn btn-primary" to="/posts/new">
+            Add a Post
+          </Link>
+

Posts

           {this.renderPosts()}

<|original_code|>
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPosts } from  '../actions';

class PostsIndex extends Component {
  componentDidMount() {
    this.props.fetchPosts();
  }

  renderPosts() {
    return _.map(this.props.posts, post => {
      return (

          {post.title}

      );
    })
  }

  render() {
    return (

Posts

          {this.renderPosts()}

    );
  }
}

function mapStateToProps(state) {
  return { posts: state.posts }
}

export default connect(mapStateToProps, { fetchPosts })(PostsIndex);

<|edits_diff|>
--- src/components/posts_index.js
+++ src/components/posts_index.js
@@ -1,6 +1,7 @@
 import _ from 'lodash';
 import React, { Component } from 'react';
 import { connect } from 'react-redux';
+import { Link } from 'react-router-dom';
 import { fetchPosts } from  '../actions';

 class PostsIndex extends Component {
<|current_version|>
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchPosts } from  '../actions';

class PostsIndex extends Component {
  componentDidMount() {
    this.props.fetchPosts();
  }

  renderPosts() {
    return _.map(this.props.posts, post => {
      return (

          {post.title}

      );
    })
  }

  render() {
    return (

Posts

          {this.renderPosts()}

    );
  }
}

function mapStateToProps(state) {
  return { posts: state.posts }
}

export default connect(mapStateToProps, { fetchPosts })(PostsIndex);

<|next_version|>
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchPosts } from  '../actions';

class PostsIndex extends Component {
  componentDidMount() {
    this.props.fetchPosts();
  }

  renderPosts() {
    return _.map(this.props.posts, post => {
      return (

          {post.title}

      );
    })
  }

  render() {
    return (

          <Link className="btn btn-primary" to="/posts/new">
            Add a Post
          </Link>

Posts

          {this.renderPosts()}

    );
  }
}

function mapStateToProps(state) {
  return { posts: state.posts }
}

export default connect(mapStateToProps, { fetchPosts })(PostsIndex);


Add a test for the sector generator in builder mode

--- src/utils/__tests__/sector-generator.spec.js
+++ src/utils/__tests__/sector-generator.spec.js
@@ -8,6 +8,7 @@
       columns: 8,
       rows: 10,
       seed: 'asdfghjkl',
+      isBuilder: false,
     };
   });

@@ -31,4 +32,9 @@
     const { systems } = sectorGenerator(config);
     expect(systems).toMatchObject(testSystems);
   });
+
+  it('should generate a sector with no systems if it is initialized in builder mode', () => {
+    const { systems } = sectorGenerator({ ...config, isBuilder: true });
+    expect(systems).toEqual({});
+  });
 });

<|original_code|>
import sectorGenerator from '../sector-generator';
import { testSystems } from '../generator-data';

describe('SectorGenerator', () => {
  let config;
  beforeEach(() => {
    config = {
      columns: 8,
      rows: 10,
      seed: 'asdfghjkl',
    };
  });

  it('should have a randomly generated name', () => {
    const { name } = sectorGenerator(config);
    expect(name).toEqual('Edena Omega');
  });

  it('should pass the seed, rows, and columns through to the result', () => {
    const testSeed = 'lkjhgfdsa';
    const { seed, rows, columns } = sectorGenerator({
      ...config,
      seed: testSeed,
    });
    expect(seed).toEqual(testSeed);
    expect(rows).toEqual(10);
    expect(columns).toEqual(8);
  });

  it('should generate a sector full of systems from a seed', () => {
    const { systems } = sectorGenerator(config);
    expect(systems).toMatchObject(testSystems);
  });
});

<|edits_diff|>
--- src/utils/__tests__/sector-generator.spec.js
+++ src/utils/__tests__/sector-generator.spec.js
@@ -8,6 +8,7 @@
       columns: 8,
       rows: 10,
       seed: 'asdfghjkl',
+      isBuilder: false,
     };
   });

<|current_version|>
import sectorGenerator from '../sector-generator';
import { testSystems } from '../generator-data';

describe('SectorGenerator', () => {
  let config;
  beforeEach(() => {
    config = {
      columns: 8,
      rows: 10,
      seed: 'asdfghjkl',
      isBuilder: false,
    };
  });

  it('should have a randomly generated name', () => {
    const { name } = sectorGenerator(config);
    expect(name).toEqual('Edena Omega');
  });

  it('should pass the seed, rows, and columns through to the result', () => {
    const testSeed = 'lkjhgfdsa';
    const { seed, rows, columns } = sectorGenerator({
      ...config,
      seed: testSeed,
    });
    expect(seed).toEqual(testSeed);
    expect(rows).toEqual(10);
    expect(columns).toEqual(8);
  });

  it('should generate a sector full of systems from a seed', () => {
    const { systems } = sectorGenerator(config);
    expect(systems).toMatchObject(testSystems);
  });
});

<|next_version|>
import sectorGenerator from '../sector-generator';
import { testSystems } from '../generator-data';

describe('SectorGenerator', () => {
  let config;
  beforeEach(() => {
    config = {
      columns: 8,
      rows: 10,
      seed: 'asdfghjkl',
      isBuilder: false,
    };
  });

  it('should have a randomly generated name', () => {
    const { name } = sectorGenerator(config);
    expect(name).toEqual('Edena Omega');
  });

  it('should pass the seed, rows, and columns through to the result', () => {
    const testSeed = 'lkjhgfdsa';
    const { seed, rows, columns } = sectorGenerator({
      ...config,
      seed: testSeed,
    });
    expect(seed).toEqual(testSeed);
    expect(rows).toEqual(10);
    expect(columns).toEqual(8);
  });

  it('should generate a sector full of systems from a seed', () => {
    const { systems } = sectorGenerator(config);
    expect(systems).toMatchObject(testSystems);
  });

  it('should generate a sector with no systems if it is initialized in builder mode', () => {
    const { systems } = sectorGenerator({ ...config, isBuilder: true });
    expect(systems).toEqual({});
  });
});