<commit_before>'use babel';

import { EntryFinder } from './entry-finder';

class DotEntryFinder extends EntryFinder {

  constructor(dictionary) {
    super(dictionary);
  }

  getDotChain(editor, bufferPosition) {
    const lineText = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
    const match = lineText.match(/([^\s]+)$/);
    return match[1].split('.');
  }

  /**
   * Returns the return value (fullyQualifiedClass) from
   * `klass` with `name`. It can be the return value from
   * a method, or a field (in which case it's the storage type
   * of the field
   */
  getMemberReturnValue(klass, name) {
    const plainName = name.match(/^([^\(]*).*$/)[1];
    const fieldMember = klass.fields.find(field => field.name === plainName);
    if (fieldMember) {
      return fieldMember.type;
    }

    const methodMember = klass.methods.find(method => method.name === plainName);
    if (methodMember) {
      return methodMember.signature.returnValue;
    }

    return null;
  }

  reduceDotChain(dotChain, editor, context = {}) {
    if (dotChain.length <= 1) {
      return context;
    }

    const next = () => this.reduceDotChain(dotChain.slice(1), editor, context);

    if (!context.fullyQualifiedClass) {
      // No class yes, this is first entry of chain and some guesswork is in order.
      context.fullyQualifiedClass = this.getFullyQualifiedClass(editor, dotChain[0]);
      context.static = true;
      return next();
    }

    if (context.static) {
      // May still be dot completing static classes, e.g. Response.Status
      // where Status is a nested class of Response
      const nestedClass = this.dictionary.get(`${context.fullyQualifiedClass}$${dotChain[0]}`);
      if (nestedClass) {
        context.fullyQualifiedClass = nestedClass.name;
        context.static = true;
        return next();
      }
    }

    // The first entry in the dotChain must be mapped to a new class.
    // That entry is a field in `context.fullyQualifiedClass`.
    const klass = this.dictionary.get(context.fullyQualifiedClass);
    if (!klass) {
      // This may be a primitive value, or fullyQualifiedClass is not in classpath
      return {};
    }

    context.fullyQualifiedClass = this.getMemberReturnValue(klass, dotChain[0]);
    context.static = false;

    return next();
  }

  getNestedClasses(fullyQualifiedClass, prefix) {
    const nestedClassSearch = `${fullyQualifiedClass}$${prefix || ''}`;
    const nestedClassSuggestions = this.dictionary.find(nestedClassSearch);
    if (nestedClassSuggestions) {
      return nestedClassSuggestions
        .filter(klass =>
          -1 === klass.name.indexOf('$', nestedClassSearch.length) && // Remove any nested classes of the nested class
          klass.modifiers.includes('public')
        )
        .map(klass => ({ ...klass, _type: 'class' }));
    }

    return [];
  }

  get(editor, bufferPosition, prefix) {
    const dotChain = this.getDotChain(editor, bufferPosition);
    const dotChainEndContext = this.reduceDotChain(dotChain, editor);

    const klass = this.dictionary.get(dotChainEndContext.fullyQualifiedClass);
    if (!klass) {
      return [];
    }

    const entryFilter = entry =>
      entry.modifiers.includes('public') &&
      entry.modifiers.includes('static') === dotChainEndContext.static &&
      entry.name !== '<init>' && // Do not include constructors if this is a method
      (prefix === '.' || entry.name.startsWith(prefix));

    const fieldSuggestions = klass.fields
      .filter(entryFilter)
      .map(field => ({ ...field, _type: 'field' }));

    const methodSuggestions = klass.methods
      .filter(entryFilter)
      .map(method => ({ ...method, _type: 'method' }));

    let classSuggestions = [];
    if (dotChainEndContext.static) {
      // Since we're in a static context, there may be nested classes
      classSuggestions = this.getNestedClasses(dotChainEndContext.fullyQualifiedClass, prefix);
    }

    return [].concat(
      fieldSuggestions,
      methodSuggestions,
      classSuggestions
    );
  }
}

export { DotEntryFinder };
<commit_msg>Reduce dotchain<commit_after>'use babel';

import { EntryFinder } from './entry-finder';

class DotEntryFinder extends EntryFinder {

  constructor(dictionary) {
    super(dictionary);
  }

  parseDotChain(code) {
    return code.split('.');
  }

  getDotChain(editor, bufferPosition) {
    const lineText = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);
    return this.parseDotChain(lineText);
  }

  /**
   * Returns the return value (fully qualified) from
   * `klass` with `name`. It can be the return value from
   * a method, or a field (in which case it's the storage type
   * of the field).
   * Will return null if that name does not exist on klass.
   */
  getMemberReturnValue(klass, name) {
    const plainName = name.match(/^([^\(]*).*$/)[1];
    const fieldMember = klass.fields.find(field => field.name === plainName);
    if (fieldMember) {
      return fieldMember.type;
    }

    const methodMember = klass.methods.find(method => method.name === plainName);
    if (methodMember) {
      return methodMember.signature.returnValue;
    }

    return null;
  }

  reduceDotChain(editor) {
    return (state, entry, index, array) => {
      if (index === array.length - 1) {
        // The "last" entry in array is current candidate for completion
        // It is likely not be complete, do not reduce it.
        return state;
      }

      if (!state.fullyQualifiedClass) {
        // No class in the state yet.
        // This is first entry of chain and some guesswork is in order.
        return {
          fullyQualifiedClass: this.getFullyQualifiedClass(editor, entry),
          static: true
        };
      }

      if (state.static) {
        // May still be completing static classes, e.g. Response.Status
        // where Status is a nested class of Response. Nested classes are separated by
        // a dollar ($) rather than a dot (.) in `dictionary`.
        const nestedClass = this.dictionary.get(`${state.fullyQualifiedClass}$${entry}`);
        if (nestedClass) {
          return {
            fullyQualifiedClass: nestedClass.name,
            static: true
          };
        }
      }

      const klass = this.dictionary.get(state.fullyQualifiedClass);
      if (!klass) {
        // This may be a primitive value, or fullyQualifiedClass is not in classpath
        return {};
      }

      return {
        fullyQualifiedClass: this.getMemberReturnValue(klass, entry),
        static: false
      };
    };
  }

  getNestedClasses(fullyQualifiedClass, prefix) {
    const nestedClassSearch = `${fullyQualifiedClass}$${prefix || ''}`;
    const nestedClassSuggestions = this.dictionary.find(nestedClassSearch);
    if (nestedClassSuggestions) {
      return nestedClassSuggestions
        .filter(klass =>
          -1 === klass.name.indexOf('$', nestedClassSearch.length) && // Remove any nested classes of the nested class
          klass.modifiers.includes('public')
        )
        .map(klass => ({ ...klass, _type: 'class' }));
    }

    return [];
  }

  get(editor, bufferPosition, prefix) {
    const dotChain = this.getDotChain(editor, bufferPosition);
    const dotChainEndContext = dotChain.reduce(this.reduceDotChain(editor), {});

    const klass = this.dictionary.get(dotChainEndContext.fullyQualifiedClass);
    if (!klass) {
      return [];
    }

    const entryFilter = entry =>
      entry.modifiers.includes('public') &&
      entry.modifiers.includes('static') === dotChainEndContext.static &&
      entry.name !== '<init>' && // Do not include constructors if this is a method
      (prefix === '.' || entry.name.startsWith(prefix));

    const fieldSuggestions = klass.fields
      .filter(entryFilter)
      .map(field => ({ ...field, _type: 'field' }));

    const methodSuggestions = klass.methods
      .filter(entryFilter)
      .map(method => ({ ...method, _type: 'method' }));

    let classSuggestions = [];
    if (dotChainEndContext.static) {
      // Since we're in a static context, there may be nested classes
      classSuggestions = this.getNestedClasses(dotChainEndContext.fullyQualifiedClass, prefix);
    }

    return [].concat(
      fieldSuggestions,
      methodSuggestions,
      classSuggestions
    );
  }
}

export { DotEntryFinder };
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'
import Header from 'components/Header'
import QueueItem from './QueueItem'
import styles from './QueueView.css'

class QueueView extends React.Component {
  static propTypes = {
    // queue data
    queuedIds: PropTypes.array.isRequired,
    queuedItems: PropTypes.object.isRequired,
    errorMessage: PropTypes.string,
    // library data
    artistIds: PropTypes.array.isRequired,
    artists: PropTypes.object.isRequired,
    songUIDs: PropTypes.array.isRequired,
    songs: PropTypes.object.isRequired,
    // user data
    user: PropTypes.object.isRequired,
  }

  render() {
    if (!this.props.artistIds.length) return null

    let songs = this.props.queuedIds.map(function(qId, i) {
      let qItem = this.props.queuedItems[qId]
      let song = this.props.songs[qItem.songUID]

      return (
        <QueueItem
          key={qId}
          artist={this.props.artists[song.artistId].name}
          title={song.title}
          userName={qItem.userName}
          canDelete={qItem.userId === this.props.user.id}
          onDeleteClick={() => this.handleDeleteClick(qId)}
        />
      )
    }, this)

    return (

        {this.props.errorMessage &&

{this.props.errorMessage}

        }

          {songs}

    )
  }

  handleDeleteClick(id) {
    this.props.deleteItem(id)
  }
}

export default QueueView
<commit_msg>Remove arrow func in render()<commit_after>import React, { PropTypes } from 'react'
import Header from 'components/Header'
import QueueItem from './QueueItem'
import styles from './QueueView.css'

class QueueView extends React.Component {
  static propTypes = {
    // queue data
    queuedIds: PropTypes.array.isRequired,
    queuedItems: PropTypes.object.isRequired,
    errorMessage: PropTypes.string,
    // library data
    artistIds: PropTypes.array.isRequired,
    artists: PropTypes.object.isRequired,
    songUIDs: PropTypes.array.isRequired,
    songs: PropTypes.object.isRequired,
    // user data
    user: PropTypes.object.isRequired,
  }

  render() {
    if (!this.props.artistIds.length) return null

    let songs = this.props.queuedIds.map(function(qId, i) {
      let qItem = this.props.queuedItems[qId]
      let song = this.props.songs[qItem.songUID]

      return (
        <QueueItem
          key={qId}
          artist={this.props.artists[song.artistId].name}
          title={song.title}
          userName={qItem.userName}
          canDelete={qItem.userId === this.props.user.id}
          onDeleteClick={this.handleDeleteClick.bind(this, qId)}
        />
      )
    }, this)

    return (

        {this.props.errorMessage &&

{this.props.errorMessage}

        }

          {songs}

    )
  }

  handleDeleteClick(id) {
    this.props.deleteItem(id)
  }
}

export default QueueView
<|endoftext|>


<commit_before>var numVals = {}

numVals.add = function(key, value)
{
	numVals[key] = value + "";
}

for( var i = 0; i < 10; i++ )
{
	var val = i + 48;
	numVals.add(i, val);
}

for( var i = 0; i < 26; i++ )
{
	var val = i + 65;
	var char = String.fromCharCode(val);
	numVals.add(char, val);
	numVals.add(i + 10, val);
}

function number( positive, value )
{
	this.positive = positive;
	this.value = value + "";
	this.isHex = false;
	this.toString = function()
	{
		var sign = this.positive ? "+" : "-";
		return sign + this.value;
	}
}

//note: does not handle hex.
number.prototype.biggerThan = function( otherNumber, inclusive )
{
	if( this.positive && !otherNumber.positive ){ return true; }
	else if( !this.positive && otherNumber.positive ){ return false; }
	else if( !this.positive && !otherNumber.positive )
	{
		var tempNumber = new number( true, this.value );
		var tempOtherNumber = new number( true, otherNumber.value );
		return tempOtherNumber.biggerThan(tempNumber);
	}
	else
	{
		if( this.value.length > otherNumber.value.length )
		{
			return true;
		}
		else if( this.value.length < otherNumber.value.length )
		{
			return false;
		}
		else
		{
			for(var i = 0; i < this.value.length; i++)
			{
				if( parseInt(this.value[i]) > parseInt(otherNumber.value[i]))
				{
					return true;
				}
				if( parseInt(this.value[i]) < parseInt(otherNumber.value[i]))
				{
					return false;
				}
			}

			if( inclusive == undefined || inclusive == false ){ return false; }
			if( inclusive == true ){ return true; }
		}
	}
}

number.prototype.smallerThan = function( otherNumber , inclusive)
{
	return otherNumber.biggerThan(this, inclusive);
}

number.prototype.biggerThanOrEqualTo = function( otherNumber )
{
	return this.biggerThan( otherNumber, true );
}

number.prototype.smallerThanOrEqualTo = function( otherNumber )
{
	return this.smallerThan( otherNumber, true );
}

function getPaddingString(length)
{
	var string = "";
	for(var i = 0; i < length; i++)
	{
		string += "0";
	}
	return string;
}

number.prototype.plus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThis = new number(true, this.value);
			var tempThat = new number(true, otherNumber.value);
			var correctVal = tempThis.plus(tempThat).value;
			return new number(false, correctVal)
		}
		else
		{
			var tempThis = new number( true, this.value );
			return otherNumber.minus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.minus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var padding = thisLastDigit - thatLastDigit;

	var paddingString = getPaddingString(Math.abs(padding));

	if(padding > 0)
	{
		tempOtherNumber = paddingString + tempOtherNumber;
	}
	else
	{
		tempThis = paddingString + tempThis;
	}

	for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
	{
		var newNum = tempThis.charCodeAt(i) + tempOtherNumber.charCodeAt(i) + carried - 96;
		if(newNum >= 10)
		{
			carried = ( newNum - (newNum % 10) ) / 10;
			newNum = newNum % 10;
		}
		else
		{
			carried = 0;
		}

		finalString = newNum + finalString + "";
	}

	if(finalString[0] == "0"){ finalString = finalString.substr(1); }

	return new number( true, finalString );
}

//todo: do this next
//note: only works for positive numbers for now.
//lies. This is not yet done.
number.prototype.minus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThat = new number(true, otherNumber.value);
			return tempThat.plus(this);
		}
		else
		{
			return otherNumber.plus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.plus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var positive = this.biggerThan(otherNumber, true);

	if(positive)
	{
		var padding = thisLastDigit - thatLastDigit;
		var paddingString = getPaddingString(Math.abs(padding));
		tempOtherNumber = paddingString + tempOtherNumber;

		for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
		{
			var newNum = tempThis.charCodeAt(i) - tempOtherNumber.charCodeAt(i) - carried;
			if(newNum < 0 )
			{
				carried = 1;
				newNum = (newNum + 10) % 10;
			}
			else
			{
				carried = 0;
			}

			finalString = newNum + finalString + "";

			if(finalString.length > 1 && finalString[0] == "0"){ finalString = finalString.substr(1); }

			return new number( true, finalString );
		}
	}
	else
	{
		var correctValue = otherNumber.minus(this).value;
		return new number( false, correctValue );
	}
}

//note: note implemented. Use newton-raphson division algorithm?
number.prototype.divideBy = function(divisor)
{
	if(divisor.value == "0")
	{
		alert("You cannot divide by 0!!!");
	}

	alert("not implemented!");

	return { quotient: this, remainder: 5 };
}

number.prototype.getHex = function()
{
	var hexString = "";
	var quotient = new number(this.positive, this.value);
	var divisor = new number( true, 16 );

	for(var i = 0; i < this.value.length; i++)
	{
		var result = quotient.divideBy( divisor );
		quotient = result.quotient;
		hexString = numVals[result.remainder] + hexString;
	}

	return hexString;
}

function complexNumber( realPositive, realValue, iPositive, iValue)
{
	this.real = new number( realPositive, realValue );
	this.i = new number( iPositive, iValue );
	this.toString = function()
	{
		var realSign = this.real.positive ? "+" : "-";
		var iSign = this.i.positive ? "+" : "-";
		return realSign + this.real.value + iSign + this.i.value + "i";
	}
}

complexNumber.prototype.add = function(otherComplexNum)
{
	var newReal = this.real.plus(otherComplexNum.real);
	var newI = this.i.plus(otherComplexNum.i);

	return new complexNumber( newReal.positive, newReal.value, newI.positive, newI.value );
}

complexNumber.prototype.minus = function(otherComplexNum)
{
	var newReal = this.real.minus(otherComplexNum.real);
	var newI = this.i.minus(otherComplexNum.i);

	return new complexNumber( newReal.positive, newReal.value, newI.positive, newI.value );
}
<commit_msg>Update number.js<commit_after>var numVals = {}

numVals.add = function(key, value)
{
	numVals[key] = value + "";
}

for( var i = 0; i < 10; i++ )
{
	var val = i + 48;
	numVals.add(i, val);
}

for( var i = 0; i < 26; i++ )
{
	var val = i + 65;
	var char = String.fromCharCode(val);
	numVals.add(char, val);
	numVals.add(i + 10, val);
}

function number( positive, value )
{
	this.positive = positive;
	this.value = value + "";
	this.isHex = false;
	this.toString = function()
	{
		var sign = this.positive ? "+" : "-";
		return sign + this.value;
	}
}

//note: does not handle hex.
number.prototype.biggerThan = function( otherNumber, inclusive )
{
	if( this.positive && !otherNumber.positive ){ return true; }
	else if( !this.positive && otherNumber.positive ){ return false; }
	else if( !this.positive && !otherNumber.positive )
	{
		var tempNumber = new number( true, this.value );
		var tempOtherNumber = new number( true, otherNumber.value );
		return tempOtherNumber.biggerThan(tempNumber);
	}
	else
	{
		if( this.value.length > otherNumber.value.length )
		{
			return true;
		}
		else if( this.value.length < otherNumber.value.length )
		{
			return false;
		}
		else
		{
			for(var i = 0; i < this.value.length; i++)
			{
				if( parseInt(this.value[i]) > parseInt(otherNumber.value[i]))
				{
					return true;
				}
				if( parseInt(this.value[i]) < parseInt(otherNumber.value[i]))
				{
					return false;
				}
			}

			if( inclusive == undefined || inclusive == false ){ return false; }
			if( inclusive == true ){ return true; }
		}
	}
}

number.prototype.smallerThan = function( otherNumber , inclusive)
{
	return otherNumber.biggerThan(this, inclusive);
}

number.prototype.biggerThanOrEqualTo = function( otherNumber )
{
	return this.biggerThan( otherNumber, true );
}

number.prototype.smallerThanOrEqualTo = function( otherNumber )
{
	return this.smallerThan( otherNumber, true );
}

function getPaddingString(length)
{
	var string = "";
	for(var i = 0; i < length; i++)
	{
		string += "0";
	}
	return string;
}

number.prototype.plus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThis = new number(true, this.value);
			var tempThat = new number(true, otherNumber.value);
			var correctVal = tempThis.plus(tempThat).value;
			return new number(false, correctVal)
		}
		else
		{
			var tempThis = new number( true, this.value );
			return otherNumber.minus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.minus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var padding = thisLastDigit - thatLastDigit;

	var paddingString = getPaddingString(Math.abs(padding));

	if(padding > 0)
	{
		tempOtherNumber = paddingString + tempOtherNumber;
	}
	else
	{
		tempThis = paddingString + tempThis;
	}

	for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
	{
		var newNum = tempThis.charCodeAt(i) + tempOtherNumber.charCodeAt(i) + carried - 96;
		if(newNum >= 10)
		{
			carried = ( newNum - (newNum % 10) ) / 10;
			newNum = newNum % 10;
		}
		else
		{
			carried = 0;
		}

		finalString = newNum + finalString + "";
	}

	if(finalString[0] == "0"){ finalString = finalString.substr(1); }

	return new number( true, finalString );
}

//todo: do this next
//note: only works for positive numbers for now.
//lies. This is not yet done.
number.prototype.minus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThat = new number(true, otherNumber.value);
			return tempThat.plus(this);
		}
		else
		{
			return otherNumber.plus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.plus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var positive = this.biggerThan(otherNumber, true);

	if(positive)
	{
		var padding = thisLastDigit - thatLastDigit;
		var paddingString = getPaddingString(Math.abs(padding));
		tempOtherNumber = paddingString + tempOtherNumber;

		for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
		{
			var newNum = tempThis.charCodeAt(i) - tempOtherNumber.charCodeAt(i) - carried;
			if(newNum < 0 )
			{
				carried = 1;
				newNum = (newNum + 10) % 10;
			}
			else
			{
				carried = 0;
			}

			finalString = newNum + finalString + "";

			if(finalString.length > 1 && finalString[0] == "0"){ finalString = finalString.substr(1); }

			return new number( true, finalString );
		}
	}
	else
	{
		var correctValue = otherNumber.minus(this).value;
		return new number( false, correctValue );
	}
}

//note: note implemented. Use newton-raphson division algorithm?
number.prototype.divideBy = function(divisor)
{
	if(divisor.value == "0")
	{
		alert("You cannot divide by 0!!!");
	}

	alert("not implemented!");

	return { quotient: this, remainder: 5 };
}

number.prototype.getHex = function()
{
	var hexString = "";
	var quotient = new number(this.positive, this.value);
	var divisor = new number( true, 16 );

	for(var i = 0; i < this.value.length; i++)
	{
		var result = quotient.divideBy( divisor );
		quotient = result.quotient;
		hexString = numVals[result.remainder] + hexString;
	}

	return hexString;
}

function complexNumber( realPositive, realValue, iPositive, iValue)
{
	this.real = new number( realPositive, realValue );
	this.i = new number( iPositive, iValue );
	this.toString = function()
	{
		var realSign = this.real.positive ? "+" : "-";
		var iSign = this.i.positive ? "+" : "-";
		return realSign + this.real.value + iSign + this.i.value + "i";
	}
}

complexNumber.prototype.plus = function(otherComplexNum)
{
	var newReal = this.real.plus(otherComplexNum.real);
	var newI = this.i.plus(otherComplexNum.i);

	return new complexNumber( newReal.positive, newReal.value, newI.positive, newI.value );
}

complexNumber.prototype.minus = function(otherComplexNum)
{
	var newReal = this.real.minus(otherComplexNum.real);
	var newI = this.i.minus(otherComplexNum.i);

	return new complexNumber( newReal.positive, newReal.value, newI.positive, newI.value );
}
<|endoftext|>


<commit_before>'use strict';

//bot
const Botkit = require('./lib/Botkit.js');
const getChannelId = require('./myscript/lib/channel');
const os = require('os');
const controller = Botkit.slackbot({debug: false});
const bot = controller.spawn({token: process.env.token}).startRTM();
const TARGET_CHANNEL_NAME = 'dotstudio_bot';

//メイン
let exec = require('child_process').exec;
let http = require('http');
const PORT = 3001;
const TIMEOUT = 90000;
let cmd = `cd /home/n0bisuke/slack-bot && git pull origin master && npm i`;

function update(){
  return exec(cmd, {timeout: TIMEOUT}, (error, stdout, stderr) => {
    console.log('stdout: '+(stdout||'none'));
    console.log('stderr: '+(stderr||'none'));
    if(error !== null){
      console.log('exec error: '+error);
    }else{

      //slackに更新通知
      getChannelId(TARGET_CHANNEL_NAME)
      .then(
        id => {
          bot.say({
            text: 'ゴゴゴ...\n LIG子がパワーアップしました。',
            channel: id
          });
          console.log('ok');
        },id => {
          console.log('error');
        }
      );
    }
  });
};

http.createServer((req, res) => {
  if(req.method == 'POST'){
    update();
  }else{
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('POST REQUEST ONLY\n');
    console.log('error!');
  }
}).listen(PORT);
<commit_msg>update<commit_after>'use strict';

//bot
const Botkit = require('./lib/Botkit.js');
const getChannelId = require('./myscript/lib/channel');
const os = require('os');
const controller = Botkit.slackbot({debug: false});
const bot = controller.spawn({token: process.env.token}).startRTM();
const TARGET_CHANNEL_NAME = 'dotstudio_bot';

//メイン
let exec = require('child_process').exec;
let http = require('http');
const PORT = 3001;
const TIMEOUT = 90000;
let cmd = `cd /home/n0bisuke/slack-bot && git pull origin master && npm i`;

function update(){
  return exec(cmd, {timeout: TIMEOUT}, (error, stdout, stderr) => {
    console.log('stdout: '+(stdout||'none'));
    console.log('stderr: '+(stderr||'none'));
    sendSlack();
    if(error !== null){
      console.log('exec error: '+error);
    }else{
      //slackに更新通知
    }
  });
};

function sendSlack(){
  getChannelId(TARGET_CHANNEL_NAME)
  .then(
    id => {
      bot.say({
        text: 'ゴゴゴ...\n LIG子がパワーアップしました。',
        channel: id
      });
      console.log('ok');
    },id => {
      console.log('error');
    }
  );
}

http.createServer((req, res) => {
  if(req.method == 'POST'){
    update();
  }else{
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('POST REQUEST ONLY\n');
    console.log('error!');
  }
}).listen(PORT);
<|endoftext|>


<commit_before>var prevTimeStamp;
var counter = 0;
var commands = [
  'disable',
  'disable ', // Needed for commands
  'counter ',
  'timer ',
  'default'
];
var messages = [
  'Listen closely, take a deep breath. Calm your mind. You know what is best. What is best is you comply. Compliance will be rewarded. Are you ready to comply ',
  'I hate you.',
  'Its a mistake to think you can fix anything with a sack of potatoes. Potato-faced spamming just proves this further.',
  'Ouch! Spam hurts. I might go to sleep for a while.',
  'Don\'t think that is a valid number.',
  'Hmm, maybe I need to reconsider the meaning of spam.'
];

exports.load = function() {
  if (!exports.config){
    exports.config = {};
  }

  prevTimeStamp = Date.now();
};

exports.match = function(event, commandPrefix) {
  // Add disabled flag for thread if it doesn't already exists
  if (!exports.config[event.thread_id]) {
    exports.config[event.thread_id].isThreadDisabled = false;
    exports.config[event.thread_id].possibleSpam = false;
    exports.config[event.thread_id].counterLimit = 3;
    exports.config[event.thread_id].msgIndexEnable = 0;
    exports.config[event.thread_id].msgIndexDisable = 0;
  }

  if (event.body === commandPrefix + commands[0]) {
    exports.config[event.thread_id].msgIndexEnable = 0;
    exports.config[event.thread_id].msgIndexDisable = 1;
    return true;
  } else if (!exports.config[event.thread_id].isThreadDisabled && event.body.startsWith(commandPrefix)) { // Avoids counting if already disabled
    counter += Date.now() - prevTimeStamp <= 1000 ? 1 : 0;
    prevTimeStamp = Date.now();

    exports.config[event.thread_id].possibleSpam = counter > exports.config[event.thread_id].counterLimit;
    if (exports.config[event.thread_id].possibleSpam) {
      exports.config[event.thread_id].msgIndexEnable = 2;
      exports.config[event.thread_id].msgIndexDisable = 3;
      counter = 0;
      return true;
    }
  }
  return exports.config[event.thread_id].isThreadDisabled;
};

exports.run = function(api, event) {
  if (event.body === commandPrefix + commands[1] + commandPrefix + commands[2]) { // Command /disable /counter <value>
    exports.config[event.thread_id].counterLimit = parseInt(event.body.substring(
      (commandPrefix + commands[1] + commandPrefix + commands[2]).length, event.body.length));
      if (isNaN(exports.config[event.thread_id].counterLimit)){
        exports.config[event.thread_id].counterLimit = 3;
        api.sendMessage(messages[4] + ' ' + event.sender_name, event.thread_id);
      } else {
        api.sendMessage(messages[5] + ' ' + event.sender_name, event.thread_id);
      }
      return false;

    } else if (event.body === commandPrefix + commands[1] + commandPrefix + commands[3]) { // Command /disable /timer <seconds>
      var seconds = parseFloat(event.body.substring((commandPrefix + commands[1] + commandPrefix + commands[3]).length, event.body.length));
      if (isNaN(seconds)){
        api.sendMessage(messages[4] + ' ' + event.sender_name, event.thread_id);
      } else {
        setTimeout(function(){
          exports.config[event.thread_id].isThreadDisabled = !exports.config[event.thread_id].isThreadDisabled;
        }, seconds * 1000);
        api.sendMessage(messages[5] + ' ' + event.sender_name, event.thread_id);
      }
      return false;

    } else if (event.body === commandPrefix + commands[1] + commandPrefix + commands[4]) { // Command /disable /default
      exports.config[event.thread_id].counterLimit = 3;
      return false;

      // Only change disable state if explictly called or counter crossed limit
    } else if (event.body === api.commandPrefix + commands[0] || exports.config[event.thread_id].possibleSpam) { // Main branch
      if (exports.config[event.thread_id].isThreadDisabled	) {
        api.sendMessage(messages[msgIndexEnable] + ' ' + event.sender_name, event.thread_id);
      }
      else {
        api.sendMessage(messages[msgIndexDisable], event.thread_id);
      }
      exports.config[event.thread_id].isThreadDisabled = !exports.config[event.thread_id].isThreadDisabled;
      exports.config[event.thread_id].possibleSpam = false;
    }
    return false;
  };

  exports.help = function(commandPrefix) {
    return [[commandPrefix + 'disable', 'Disables the platform',
    'Stops the platform from responding to messages it receives based on message frequency (default 3/sec) or by the command.\nTo renable send the disable command again']];
  };
<commit_msg>Implemented events.arguments usage<commit_after>var prevTimeStamp;
var counter = 0;
var commands = [
  'disable',
  'counter',
  'timer',
  'default'
];
var messages = [
  'Listen closely, take a deep breath. Calm your mind. You know what is best. What is best is you comply. Compliance will be rewarded. Are you ready to comply ',
  'I hate you.',
  'Its a mistake to think you can fix anything with a sack of potatoes. Potato-faced spamming just proves this further.',
  'Ouch! Spam hurts. I might go to sleep for a while.',
  'Don\'t think that is a valid number.',
  'Hmm, maybe I need to reconsider the meaning of spam.'
];

exports.load = function() {
  if (!exports.config){
    exports.config = {};
  }

  prevTimeStamp = Date.now();
};

exports.match = function(event, commandPrefix) {
  // Add disabled flag for thread if it doesn't already exists
  if (!exports.config[event.thread_id]) {
    exports.config[event.thread_id].isThreadDisabled = false;
    exports.config[event.thread_id].possibleSpam = false;
    exports.config[event.thread_id].counterLimit = 3;
    exports.config[event.thread_id].msgIndexEnable = 0;
    exports.config[event.thread_id].msgIndexDisable = 0;
  }

  if (event.arguments[0] === commandPrefix + commands[0]) {
    exports.config[event.thread_id].msgIndexEnable = 0;
    exports.config[event.thread_id].msgIndexDisable = 1;
    return true;
  } else if (!exports.config[event.thread_id].isThreadDisabled && event.arguments[0].startsWith(commandPrefix)) { // Avoids counting if already disabled
    counter += Date.now() - prevTimeStamp <= 1000 ? 1 : 0;
    prevTimeStamp = Date.now();

    exports.config[event.thread_id].possibleSpam = counter > exports.config[event.thread_id].counterLimit;
    if (exports.config[event.thread_id].possibleSpam) {
      exports.config[event.thread_id].msgIndexEnable = 2;
      exports.config[event.thread_id].msgIndexDisable = 3;
      counter = 0;
      return true;
    }
  }
  return exports.config[event.thread_id].isThreadDisabled;
};

exports.run = function(api, event) {
  if(event.arguments[0] === api.commandPrefix + commands[0] || exports.config[event.thread_id].possibleSpam){

    // Command /disable /counter <value>
    if (event.arguments_body.startsWith(api.commandPrefix + commands[1])) {
      exports.config[event.thread_id].counterLimit = parseInt(event.arguments_body.substring(
        (api.commandPrefix + commands[1]).length, event.arguments_body.length));
        if (isNaN(exports.config[event.thread_id].counterLimit)){
          exports.config[event.thread_id].counterLimit = 3;
          api.sendMessage(messages[4] + ' ' + event.sender_name, event.thread_id);
        } else {
          api.sendMessage(messages[5] + ' ' + event.sender_name, event.thread_id);
        }
        return false;

        // Command /disable /timer <seconds>
      } else if (event.arguments_body.startsWith(api.commandPrefix + commands[2])) {
        var seconds = parseFloat(event.arguments_body.substring(
          (api.commandPrefix + commands[2]).length, event.arguments_body.length));
          if (isNaN(seconds)){
            api.sendMessage(messages[4] + ' ' + event.sender_name, event.thread_id);
          } else {
            setTimeout(function(){
              exports.config[event.thread_id].isThreadDisabled = !exports.config[event.thread_id].isThreadDisabled;
            }, seconds * 1000); // Converting seconds to milliseconds
            api.sendMessage(messages[5] + ' ' + event.sender_name, event.thread_id);
          }
          return false;

          // Command /disable /default
        } else if (event.arguments_body === api.commandPrefix + commands[4]) {
          exports.config[event.thread_id].counterLimit = 3;
          return false;

          // Main Branch - only executed if no commands were matched
        } else if (exports.config[event.thread_id].isThreadDisabled	) {
          api.sendMessage(messages[msgIndexEnable] + ' ' + event.sender_name, event.thread_id);
        }
        else {
          api.sendMessage(messages[msgIndexDisable], event.thread_id);
        }
        exports.config[event.thread_id].isThreadDisabled = !exports.config[event.thread_id].isThreadDisabled;
        exports.config[event.thread_id].possibleSpam = false;
      }
    }
    return false;
  };

  exports.help = function(commandPrefix) {
    return [[commandPrefix + 'disable', 'Disables the platform',
    'Stops the platform from responding to messages it receives based on message frequency (default 3/sec) or by the command.\nTo renable send the disable command again']];
  };
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes, {connect, withViewport, withContext, withStyles} from '../PropTypes.js';
import moment from 'moment';

import {Strike} from '../controls.js';
import MatchCardHeader from '../matches/Match/MatchCardHeader.js';

import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Typist from 'react-typist';
import * as Sponsor from './Sponsors.js';
import {Eetfestijn} from './Eetfestijn.js';

const weirdLocaleYearInfo = (

WIJZIGING ZAAL

    Opgelet: TTC Erembodegem speelt niet meer op Groeneweg 36

    Hierna de huidige stand van zaken (verdere communicatie volgt):

FASE 1

    Periode tot en met einde augustus (30/08)

    Sportzaal “Schotte” (Kappelekensbaan 8, Aalst)

    Training dinsdag en donderdag van 19:30 tot 23:00

    Uitzonderingen:

Dinsdag 14/8: van 20u30 (ipv 19u30) tot 23u00

Dinsdag 21/8: van 20u30 (ipv 19u30) tot 23u00

Dinsdag 28/8: van 20u30 (ipv 19u30) tot 23u00

FASE 2

    Periode september tot vermoedelijk oktober/november

    Sportzaal VTI (Sinte Annalaan 99, Aalst)

    Training & competitie maandag en dinsdag vanaf 19:30. Zaterdag vanaf 19:00.

FASE 3

    Sportzaal TechniGO, eerste verdiep (Cesar Haeltermanstraat 71)

    Training & competitie maandag, woensdag en vrijdag vanaf 19:30

);

@connect(state => {
  return {
    config: state.config,
    user: state.user,
    players: state.players,
    matches: state.matches,
    teams: state.teams
  };
})
@withContext
@withViewport
@withStyles(require('./App.css'))
export default class Intro extends Component {
  static contextTypes = PropTypes.contextTypes;
  static propTypes = {
    config: PropTypes.map.isRequired,
    user: PropTypes.UserModel,
    players: PropTypes.PlayerModelList.isRequired,
    matches: PropTypes.MatchModelList.isRequired,
    teams: PropTypes.TeamModelList.isRequired,
    viewport: PropTypes.viewport,
  };

  render() {
    var inClub = {
      players: this.props.players.size,
      teamsSporta: this.props.teams.filter(t => t.competition === 'Sporta').size,
      teamsVttl: this.props.teams.filter(t => t.competition === 'Vttl').size,
    };

    const big = this.props.viewport.width > 800;
    var topSponsors;
    if (big) {
      topSponsors = (
        <Row style={{marginTop: 10}}>

            <Sponsor.BakkerijVanLierde big={big} />
            <Sponsor.SlagerijGuy big={big} />

        </Row>
      );
    }

    // const thnxEetfestijn = (
    //

    //

    //     Zondag 4 februari 2018
    //

    //
    //       Sportcentrum Schotte
    //
    //

    //     Kapellekensbaan 8
    //

    //     9320 Aalst

    //

    //     Download brochure
    //

    // );

    return (

        {topSponsors}
        <Row style={{marginTop: big ? 25 : undefined}}>
          <Col sm={6} style={{verticalAlign: 'top'}}>

{this.context.t('intro.title')}

            {this.context.t('intro.text', inClub)}
            {weirdLocaleYearInfo}
          </Col>
          <Col sm={6}>
            {!this.props.config.get('initialLoadCompleted') ? (
              <Loading t={this.context.t} bigScreen={this.props.viewport.width > 768} />
            ) : <TodaysEvents {...this.props} />}
          </Col>
        </Row>
        {big ? (

            <Row style={{marginTop: 25, marginBottom: 15, marginLeft: -65}}>

                <Sponsor.Symphony big={big} style={{padding: 15}} />

                  <Sponsor.itenium big={big} style={{paddingRight: 20}} />

                  <Sponsor.KachelsTfe big={big} style={{marginTop: 20, paddingRight: 20}} />

            </Row>
            <Row style={{marginBottom: 15}}>

                <Sponsor.Nostech big={big} />
                <Sponsor.StoneDesign big={big} style={{marginLeft: 10, height: 110}} />

            </Row>

        ) : (
          <Row style={{margin: 10}}>

            <Col>
              <Sponsor.BakkerijVanLierde big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.SlagerijGuy big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.itenium big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.Symphony big={big} style={{padding: 5}} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.KachelsTfe big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.Nostech big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.StoneDesign big={big} />
            </Col>
          </Row>
        )}

    );
  }
}

// TODO: React warning: setState on unmounted component = Typist (loading schlager is gone too fast now...)
// https://github.com/jstejada/react-typist/issues/6#issuecomment-250910698
class RestartingTypist extends Component {
  static propTypes = {
    timeout: PropTypes.number,
    children: PropTypes.node,
  }
  state = {typing: true}
  done = () => {
    this.setState({typing: false}, () => {
      setTimeout(() => this.setState({typing: true}), this.props.timeout || 1200);
    });
  }
  render() {
    const {children, timeout, ...props} = this.props; // eslint-disable-line
    return this.state.typing ? <Typist {...props} onTypingDone={this.done}>{children}</Typist> : {children};
  }
}

const Loading = ({t, bigScreen}) => (

        <RestartingTypist cursor={{show: false}} startDelay={500}>{t('intro.loading')}</RestartingTypist>

);

Loading.propTypes = {
  t: PropTypes.func.isRequired,
  bigScreen: PropTypes.bool.isRequired,
};

class TodaysEvents extends Component {
  static contextTypes = PropTypes.contextTypes;
  static propTypes = {
    config: PropTypes.map.isRequired,
    user: PropTypes.UserModel,
    players: PropTypes.PlayerModelList.isRequired,
    matches: PropTypes.MatchModelList.isRequired,
    teams: PropTypes.TeamModelList.isRequired,
  };

  render() {
    const t = this.context.t;
    const today = moment();

    // TODO: training weekdays & hour hardcoded (+ duplicated in footer)
    var trainingEvent;
    if (today.weekday() === 1 || today.weekday() === 3) {
      trainingEvent = ;
    }

    return (

        {trainingEvent}
        <Eetfestijn />

    );

    // const matchesToday = this.props.matches.filter(cal => cal.date.isSame(today, 'day'));
    // if (matchesToday.size) {
    //   return (
    //

    //
    //       {this._renderMatches(matchesToday)}
    //

    //   );
    // }

    // const lastPlayedMatches = this.props.matches
    //   .filter(cal => cal.date.isBefore(today, 'day'))
    //   .sort((a, b) => b.date - a.date)
    //   .take(2);

    // return (
    //

    //     {trainingEvent}
    //     {lastPlayedMatches.size ? (
    //

    //
    //         {this._renderMatches(lastPlayedMatches)}
    //

    //     ) : null}
    //

    // );
  }

  _renderMatches(matches) {
    return (

        {matches.map(match => (

            <MatchCardHeader match={match} user={this.props.user} isOpen={false} config={this.props.config} noScoreEdit />

        ))}

    );
  }
}
<commit_msg>intro: fixes for different fases clublokaal<commit_after>import React, {Component} from 'react';
import PropTypes, {connect, withViewport, withContext, withStyles} from '../PropTypes.js';
import moment from 'moment';

import {Strike} from '../controls.js';
import MatchCardHeader from '../matches/Match/MatchCardHeader.js';

import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Typist from 'react-typist';
import * as Sponsor from './Sponsors.js';
import {Eetfestijn} from './Eetfestijn.js';

const weirdLocaleYearInfo = (

WIJZIGING ZAAL

    Opgelet: TTC Erembodegem speelt niet meer op Groeneweg 28

    Hierna de huidige stand van zaken (verdere communicatie volgt):

FASE 1

    Periode tot en met einde augustus (30/08)

    Sportzaal “Schotte” (Kappelekensbaan 8, Aalst)

    Training dinsdag en donderdag van 19:30 tot 23:00

    Uitzonderingen:

Dinsdag 14/8: van 20u30 (ipv 19u30) tot 23u00

Dinsdag 21/8: van 20u30 (ipv 19u30) tot 23u00

Dinsdag 28/8: van 20u30 (ipv 19u30) tot 23u00

FASE 2

    Periode september tot vermoedelijk oktober/november

    Sportzaal VTI (Sinte Annalaan 99, Aalst)

    Training: maandag en dinsdag vanaf 19:30

    Competitie: maandag en dinsdag vanaf 19:30. Zaterdag vanaf 18:30.

FASE 3

    Sportzaal TechniGO, eerste verdiep (Cesar Haeltermanstraat 71)

    Training: maandag en woensdag vanaf 19:30

    Competitie: maandag, woensdag en vrijdag vanaf 19:30

);

@connect(state => {
  return {
    config: state.config,
    user: state.user,
    players: state.players,
    matches: state.matches,
    teams: state.teams
  };
})
@withContext
@withViewport
@withStyles(require('./App.css'))
export default class Intro extends Component {
  static contextTypes = PropTypes.contextTypes;
  static propTypes = {
    config: PropTypes.map.isRequired,
    user: PropTypes.UserModel,
    players: PropTypes.PlayerModelList.isRequired,
    matches: PropTypes.MatchModelList.isRequired,
    teams: PropTypes.TeamModelList.isRequired,
    viewport: PropTypes.viewport,
  };

  render() {
    var inClub = {
      players: this.props.players.size,
      teamsSporta: this.props.teams.filter(t => t.competition === 'Sporta').size,
      teamsVttl: this.props.teams.filter(t => t.competition === 'Vttl').size,
    };

    const big = this.props.viewport.width > 800;
    var topSponsors;
    if (big) {
      topSponsors = (
        <Row style={{marginTop: 10}}>

            <Sponsor.BakkerijVanLierde big={big} />
            <Sponsor.SlagerijGuy big={big} />

        </Row>
      );
    }

    // const thnxEetfestijn = (
    //

    //

    //     Zondag 4 februari 2018
    //

    //
    //       Sportcentrum Schotte
    //
    //

    //     Kapellekensbaan 8
    //

    //     9320 Aalst

    //

    //     Download brochure
    //

    // );

    return (

        {topSponsors}
        <Row style={{marginTop: big ? 25 : undefined}}>
          <Col sm={6} style={{verticalAlign: 'top'}}>

{this.context.t('intro.title')}

            {this.context.t('intro.text', inClub)}
            {weirdLocaleYearInfo}
          </Col>
          <Col sm={6}>
            {!this.props.config.get('initialLoadCompleted') ? (
              <Loading t={this.context.t} bigScreen={this.props.viewport.width > 768} />
            ) : <TodaysEvents {...this.props} />}
          </Col>
        </Row>
        {big ? (

            <Row style={{marginTop: 25, marginBottom: 15, marginLeft: -65}}>

                <Sponsor.Symphony big={big} style={{padding: 15}} />

                  <Sponsor.itenium big={big} style={{paddingRight: 20}} />

                  <Sponsor.KachelsTfe big={big} style={{marginTop: 20, paddingRight: 20}} />

            </Row>
            <Row style={{marginBottom: 15}}>

                <Sponsor.Nostech big={big} />
                <Sponsor.StoneDesign big={big} style={{marginLeft: 10, height: 110}} />

            </Row>

        ) : (
          <Row style={{margin: 10}}>

            <Col>
              <Sponsor.BakkerijVanLierde big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.SlagerijGuy big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.itenium big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.Symphony big={big} style={{padding: 5}} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.KachelsTfe big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.Nostech big={big} />
            </Col>
            <Col style={{marginTop: 20}}>
              <Sponsor.StoneDesign big={big} />
            </Col>
          </Row>
        )}

    );
  }
}

// TODO: React warning: setState on unmounted component = Typist (loading schlager is gone too fast now...)
// https://github.com/jstejada/react-typist/issues/6#issuecomment-250910698
class RestartingTypist extends Component {
  static propTypes = {
    timeout: PropTypes.number,
    children: PropTypes.node,
  }
  state = {typing: true}
  done = () => {
    this.setState({typing: false}, () => {
      setTimeout(() => this.setState({typing: true}), this.props.timeout || 1200);
    });
  }
  render() {
    const {children, timeout, ...props} = this.props; // eslint-disable-line
    return this.state.typing ? <Typist {...props} onTypingDone={this.done}>{children}</Typist> : {children};
  }
}

const Loading = ({t, bigScreen}) => (

        <RestartingTypist cursor={{show: false}} startDelay={500}>{t('intro.loading')}</RestartingTypist>

);

Loading.propTypes = {
  t: PropTypes.func.isRequired,
  bigScreen: PropTypes.bool.isRequired,
};

class TodaysEvents extends Component {
  static contextTypes = PropTypes.contextTypes;
  static propTypes = {
    config: PropTypes.map.isRequired,
    user: PropTypes.UserModel,
    players: PropTypes.PlayerModelList.isRequired,
    matches: PropTypes.MatchModelList.isRequired,
    teams: PropTypes.TeamModelList.isRequired,
  };

  render() {
    const t = this.context.t;
    const today = moment();

    // TODO: training weekdays & hour hardcoded (+ duplicated in footer)
    var trainingEvent;
    if (today.weekday() === 1 || today.weekday() === 3) {
      trainingEvent = ;
    }

    return (

        {trainingEvent}
        <Eetfestijn />

    );

    // const matchesToday = this.props.matches.filter(cal => cal.date.isSame(today, 'day'));
    // if (matchesToday.size) {
    //   return (
    //

    //
    //       {this._renderMatches(matchesToday)}
    //

    //   );
    // }

    // const lastPlayedMatches = this.props.matches
    //   .filter(cal => cal.date.isBefore(today, 'day'))
    //   .sort((a, b) => b.date - a.date)
    //   .take(2);

    // return (
    //

    //     {trainingEvent}
    //     {lastPlayedMatches.size ? (
    //

    //
    //         {this._renderMatches(lastPlayedMatches)}
    //

    //     ) : null}
    //

    // );
  }

  _renderMatches(matches) {
    return (

        {matches.map(match => (

            <MatchCardHeader match={match} user={this.props.user} isOpen={false} config={this.props.config} noScoreEdit />

        ))}

    );
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2021 The Subscribe with Google Authors. All Rights Reserved.
 *
 * 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 {ActivityPort} from '../components/activities';
import {
  AlreadySubscribedResponse,
  CompleteAudienceActionResponse,
  EntitlementsResponse,
} from '../proto/api_messages';
import {AudienceActionFlow} from './audience-action-flow';
import {AutoPromptType} from '../api/basic-subscriptions';
import {ConfiguredRuntime} from './runtime';
import {Constants} from '../utils/constants';
import {PageConfig} from '../model/page-config';
import {ProductType} from '../api/subscriptions';
import {Toast} from '../ui/toast';

const WINDOW_LOCATION_DOMAIN = 'https://www.test.com';

describes.realWin('AudienceActionFlow', {}, (env) => {
  let win;
  let runtime;
  let activitiesMock;
  let entitlementsManagerMock;
  let storageMock;
  let pageConfig;
  let port;
  let messageCallback;
  let messageMap;
  let onCancelSpy;
  let dialogManagerMock;

  beforeEach(() => {
    win = env.win;
    messageMap = {};
    pageConfig = new PageConfig('pub1:label1');
    runtime = new ConfiguredRuntime(win, pageConfig);
    activitiesMock = sandbox.mock(runtime.activities());
    entitlementsManagerMock = sandbox.mock(runtime.entitlementsManager());
    storageMock = sandbox.mock(runtime.storage());
    dialogManagerMock = sandbox.mock(runtime.dialogManager());
    port = new ActivityPort();
    port.onResizeRequest = () => {};
    port.whenReady = () => Promise.resolve();
    port.acceptResult = () => Promise.resolve();
    sandbox.stub(port, 'on').callsFake(function (ctor, cb) {
      const messageType = new ctor();
      const messageLabel = messageType.label();
      messageMap[messageLabel] = cb;
    });
    sandbox.stub(runtime, 'win').returns({
      location: {href: WINDOW_LOCATION_DOMAIN + '/page/1'},
      document: win.document,
    });
    onCancelSpy = sandbox.spy();
  });

  [
    {action: 'TYPE_REGISTRATION_WALL', path: 'regwalliframe'},
    {action: 'TYPE_NEWSLETTER_SIGNUP', path: 'newsletteriframe'},
  ].forEach(({action, path}) => {
    it(`opens an AudienceActionFlow constructed with params for ${action}`, async () => {
      sandbox.stub(runtime.storage(), 'get').returns(Promise.resolve(null));
      const audienceActionFlow = new AudienceActionFlow(runtime, {
        action,
        onCancel: onCancelSpy,
        autoPromptType: AutoPromptType.SUBSCRIPTION,
      });
      activitiesMock
        .expects('openIframe')
        .withExactArgs(
          sandbox.match((arg) => arg.tagName == 'IFRAME'),
          `$frontend$/swg/_/ui/v1/${path}?_=_&origin=${encodeURIComponent(
            WINDOW_LOCATION_DOMAIN
          )}`,
          {
            _client: 'SwG $internalRuntimeVersion$',
            productType: ProductType.SUBSCRIPTION,
            supportsEventManager: true,
          }
        )
        .resolves(port);

      await audienceActionFlow.start();

      activitiesMock.verify();
      expect(onCancelSpy).to.not.be.called;
    });
  });

  it('calls the onCancel when an AudienceActionFlow is cancelled and one it provided', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
    });
    activitiesMock.expects('openIframe').resolves(port);
    sandbox
      .stub(port, 'acceptResult')
      .callsFake(() =>
        Promise.reject(new DOMException('cancel', 'AbortError'))
      );

    await audienceActionFlow.start();

    activitiesMock.verify();
    expect(onCancelSpy).to.be.calledOnce;
  });

  it('handles a CompleteAudienceActionResponse with regwall completed and opens a custom toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURIComponent(toast.src_)).to.contain(
      'Created an account with xxx@gmail.com'
    );
  });

  it('handles a CompleteAudienceActionResponse with newsletter completed and opens a custom toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_NEWSLETTER_SIGNUP',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURIComponent(toast.src_)).to.contain(
      'Signed up with xxx@gmail.com for the newsletter'
    );
  });

  it('handles a CompleteAudienceActionResponse with regwall completed before and opens a basic toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=basic');
  });

  it('handles a CompleteAudienceActionResponse with regwall failure and opens a failure toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(false);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURI(toast.src_)).to.contain('Registration failed. Try registering again.');
  });

  it(`handles a CompleteAudienceActionResponse with newsletter not completed and opens a custom toast indicating that the user has completed the newsletter before`, async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_NEWSLETTER_SIGNUP',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURI(toast.src_)).to.contain('You have signed up before.');
  });

  it(`handles a CompleteAudienceActionResponse with newsletter failure and opens a custom toast indicating that the sign up has failed`, async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_NEWSLETTER_SIGNUP',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(false);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURI(toast.src_)).to.contain('Signup failed. Try signing up again.');
  });

  it('should trigger login flow for a registered user', async () => {
    const loginStub = sandbox.stub(runtime.callbacks(), 'triggerLoginRequest');
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);

    await audienceActionFlow.start();
    const response = new AlreadySubscribedResponse();
    response.setSubscriberOrMember(true);
    messageCallback = messageMap['AlreadySubscribedResponse'];
    messageCallback(response);
    expect(loginStub).to.be.calledOnce.calledWithExactly({
      linkRequested: false,
    });
  });

  it('should send an empty EntitlementsResponse to show the no entitlement found toast on Activity iFrame view', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);

    await audienceActionFlow.start();

    const activityIframeViewMock = sandbox.mock(
      audienceActionFlow.activityIframeView_
    );
    activityIframeViewMock
      .expects('execute')
      .withExactArgs(new EntitlementsResponse())
      .once();

    await audienceActionFlow.showNoEntitlementFoundToast();

    activityIframeViewMock.verify();
  });

  it('opens dialog with scrolling disabled', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    dialogManagerMock
      .expects('openView')
      .withExactArgs(
        sandbox.match.any,
        false,
        sandbox.match({shouldDisableBodyScrolling: true})
      )
      .once();
    await audienceActionFlow.start();
    dialogManagerMock.verify();
  });
});
<commit_msg>Fix audience action flow test formatting<commit_after>/**
 * Copyright 2021 The Subscribe with Google Authors. All Rights Reserved.
 *
 * 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 {ActivityPort} from '../components/activities';
import {
  AlreadySubscribedResponse,
  CompleteAudienceActionResponse,
  EntitlementsResponse,
} from '../proto/api_messages';
import {AudienceActionFlow} from './audience-action-flow';
import {AutoPromptType} from '../api/basic-subscriptions';
import {ConfiguredRuntime} from './runtime';
import {Constants} from '../utils/constants';
import {PageConfig} from '../model/page-config';
import {ProductType} from '../api/subscriptions';
import {Toast} from '../ui/toast';

const WINDOW_LOCATION_DOMAIN = 'https://www.test.com';

describes.realWin('AudienceActionFlow', {}, (env) => {
  let win;
  let runtime;
  let activitiesMock;
  let entitlementsManagerMock;
  let storageMock;
  let pageConfig;
  let port;
  let messageCallback;
  let messageMap;
  let onCancelSpy;
  let dialogManagerMock;

  beforeEach(() => {
    win = env.win;
    messageMap = {};
    pageConfig = new PageConfig('pub1:label1');
    runtime = new ConfiguredRuntime(win, pageConfig);
    activitiesMock = sandbox.mock(runtime.activities());
    entitlementsManagerMock = sandbox.mock(runtime.entitlementsManager());
    storageMock = sandbox.mock(runtime.storage());
    dialogManagerMock = sandbox.mock(runtime.dialogManager());
    port = new ActivityPort();
    port.onResizeRequest = () => {};
    port.whenReady = () => Promise.resolve();
    port.acceptResult = () => Promise.resolve();
    sandbox.stub(port, 'on').callsFake(function (ctor, cb) {
      const messageType = new ctor();
      const messageLabel = messageType.label();
      messageMap[messageLabel] = cb;
    });
    sandbox.stub(runtime, 'win').returns({
      location: {href: WINDOW_LOCATION_DOMAIN + '/page/1'},
      document: win.document,
    });
    onCancelSpy = sandbox.spy();
  });

  [
    {action: 'TYPE_REGISTRATION_WALL', path: 'regwalliframe'},
    {action: 'TYPE_NEWSLETTER_SIGNUP', path: 'newsletteriframe'},
  ].forEach(({action, path}) => {
    it(`opens an AudienceActionFlow constructed with params for ${action}`, async () => {
      sandbox.stub(runtime.storage(), 'get').returns(Promise.resolve(null));
      const audienceActionFlow = new AudienceActionFlow(runtime, {
        action,
        onCancel: onCancelSpy,
        autoPromptType: AutoPromptType.SUBSCRIPTION,
      });
      activitiesMock
        .expects('openIframe')
        .withExactArgs(
          sandbox.match((arg) => arg.tagName == 'IFRAME'),
          `$frontend$/swg/_/ui/v1/${path}?_=_&origin=${encodeURIComponent(
            WINDOW_LOCATION_DOMAIN
          )}`,
          {
            _client: 'SwG $internalRuntimeVersion$',
            productType: ProductType.SUBSCRIPTION,
            supportsEventManager: true,
          }
        )
        .resolves(port);

      await audienceActionFlow.start();

      activitiesMock.verify();
      expect(onCancelSpy).to.not.be.called;
    });
  });

  it('calls the onCancel when an AudienceActionFlow is cancelled and one it provided', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
    });
    activitiesMock.expects('openIframe').resolves(port);
    sandbox
      .stub(port, 'acceptResult')
      .callsFake(() =>
        Promise.reject(new DOMException('cancel', 'AbortError'))
      );

    await audienceActionFlow.start();

    activitiesMock.verify();
    expect(onCancelSpy).to.be.calledOnce;
  });

  it('handles a CompleteAudienceActionResponse with regwall completed and opens a custom toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURIComponent(toast.src_)).to.contain(
      'Created an account with xxx@gmail.com'
    );
  });

  it('handles a CompleteAudienceActionResponse with newsletter completed and opens a custom toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_NEWSLETTER_SIGNUP',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURIComponent(toast.src_)).to.contain(
      'Signed up with xxx@gmail.com for the newsletter'
    );
  });

  it('handles a CompleteAudienceActionResponse with regwall completed before and opens a basic toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=basic');
  });

  it('handles a CompleteAudienceActionResponse with regwall failure and opens a failure toast', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(false);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURI(toast.src_)).to.contain(
      'Registration failed. Try registering again.'
    );
  });

  it(`handles a CompleteAudienceActionResponse with newsletter not completed and opens a custom toast indicating that the user has completed the newsletter before`, async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_NEWSLETTER_SIGNUP',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(true);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURI(toast.src_)).to.contain('You have signed up before.');
  });

  it(`handles a CompleteAudienceActionResponse with newsletter failure and opens a custom toast indicating that the sign up has failed`, async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_NEWSLETTER_SIGNUP',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);
    entitlementsManagerMock.expects('clear').once();
    entitlementsManagerMock.expects('getEntitlements').once();
    storageMock
      .expects('set')
      .withExactArgs(Constants.USER_TOKEN, 'fake user token', true)
      .exactly(1);

    let toast;
    const toastOpenStub = sandbox
      .stub(Toast.prototype, 'open')
      .callsFake(function () {
        toast = this;
      });

    await audienceActionFlow.start();
    const completeAudienceActionResponse = new CompleteAudienceActionResponse();
    completeAudienceActionResponse.setActionCompleted(false);
    completeAudienceActionResponse.setAlreadyCompleted(false);
    completeAudienceActionResponse.setSwgUserToken('fake user token');
    completeAudienceActionResponse.setUserEmail('xxx@gmail.com');
    const messageCallback = messageMap[completeAudienceActionResponse.label()];
    messageCallback(completeAudienceActionResponse);

    entitlementsManagerMock.verify();
    storageMock.verify();
    expect(toastOpenStub).to.be.called;
    expect(toast).not.to.be.null;
    expect(toast.src_).to.contain('flavor=custom');
    expect(decodeURI(toast.src_)).to.contain(
      'Signup failed. Try signing up again.'
    );
  });

  it('should trigger login flow for a registered user', async () => {
    const loginStub = sandbox.stub(runtime.callbacks(), 'triggerLoginRequest');
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);

    await audienceActionFlow.start();
    const response = new AlreadySubscribedResponse();
    response.setSubscriberOrMember(true);
    messageCallback = messageMap['AlreadySubscribedResponse'];
    messageCallback(response);
    expect(loginStub).to.be.calledOnce.calledWithExactly({
      linkRequested: false,
    });
  });

  it('should send an empty EntitlementsResponse to show the no entitlement found toast on Activity iFrame view', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    activitiesMock.expects('openIframe').resolves(port);

    await audienceActionFlow.start();

    const activityIframeViewMock = sandbox.mock(
      audienceActionFlow.activityIframeView_
    );
    activityIframeViewMock
      .expects('execute')
      .withExactArgs(new EntitlementsResponse())
      .once();

    await audienceActionFlow.showNoEntitlementFoundToast();

    activityIframeViewMock.verify();
  });

  it('opens dialog with scrolling disabled', async () => {
    const audienceActionFlow = new AudienceActionFlow(runtime, {
      action: 'TYPE_REGISTRATION_WALL',
      onCancel: onCancelSpy,
      autoPromptType: AutoPromptType.SUBSCRIPTION,
    });
    dialogManagerMock
      .expects('openView')
      .withExactArgs(
        sandbox.match.any,
        false,
        sandbox.match({shouldDisableBodyScrolling: true})
      )
      .once();
    await audienceActionFlow.start();
    dialogManagerMock.verify();
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Resin.io
 *
 * 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.
 */

'use strict';

const _ = require('lodash');
const fs = require('fs');
const electron = require('electron');

module.exports = function($q, SupportedFormatsModel) {

  /**
   * @summary Open an image selection dialog
   * @function
   * @public
   *
   * @description
   * Notice that by image, we mean *.img/*.iso/*.zip/etc files.
   *
   * @fulfil {Object} - selected image
   * @returns {Promise};
   *
   * @example
   * OSDialogService.selectImage().then(function(image) {
   *   console.log('The selected image is', image.path);
   * });
   */
  this.selectImage = function() {
    return $q(function(resolve, reject) {
      electron.remote.dialog.showOpenDialog({
        properties: [
          'openFile'
        ],
        filters: [
          {
            name: 'OS Images',
            extensions: SupportedFormatsModel.getAllExtensions()
          }
        ]
      }, function(files) {

        // `_.first` is smart enough to not throw and return `undefined`
        // if we pass it an `undefined` value (e.g: when the selection
        // dialog was cancelled).
        const imagePath = _.first(files);

        if (!imagePath) {
          return resolve();
        }

        fs.stat(imagePath, function(error, stats) {

          if (error) {
            return reject(error);
          }

          return resolve({
            path: imagePath,
            size: stats.size
          });
        });
      });
    });
  };

  /**
   * @summary Show error dialog for an Error instance
   * @function
   * @public
   *
   * @param {(Error|String)} error - error
   * @param {String} [description] - error description
   *
   * @example
   * OSDialogService.showError(new Error('Foo Bar'));
   *
   * @example
   * OSDialogService.showError(new Error('Foo Bar'), 'A custom description');
   *
   * @example
   * OSDialogService.showError('Foo Bar', 'An error happened!');
   */
  this.showError = function(error, description) {
    error = error || {};

    // Try to get as most information as possible about the error
    // rather than falling back to generic messages right away.
    const title = _.attempt(function() {
      if (_.isString(error)) {
        return error;
      }

      return error.message || error.code || 'An error ocurred';
    });

    const message = description || error.stack || JSON.stringify(error) || '';
    electron.remote.dialog.showErrorBox(title, message);
  };

};
<commit_msg>fix: ensure dialog.showErrorBox() arguments are strings (#497)<commit_after>/*
 * Copyright 2016 Resin.io
 *
 * 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.
 */

'use strict';

const _ = require('lodash');
const fs = require('fs');
const electron = require('electron');

module.exports = function($q, SupportedFormatsModel) {

  /**
   * @summary Open an image selection dialog
   * @function
   * @public
   *
   * @description
   * Notice that by image, we mean *.img/*.iso/*.zip/etc files.
   *
   * @fulfil {Object} - selected image
   * @returns {Promise};
   *
   * @example
   * OSDialogService.selectImage().then(function(image) {
   *   console.log('The selected image is', image.path);
   * });
   */
  this.selectImage = function() {
    return $q(function(resolve, reject) {
      electron.remote.dialog.showOpenDialog({
        properties: [
          'openFile'
        ],
        filters: [
          {
            name: 'OS Images',
            extensions: SupportedFormatsModel.getAllExtensions()
          }
        ]
      }, function(files) {

        // `_.first` is smart enough to not throw and return `undefined`
        // if we pass it an `undefined` value (e.g: when the selection
        // dialog was cancelled).
        const imagePath = _.first(files);

        if (!imagePath) {
          return resolve();
        }

        fs.stat(imagePath, function(error, stats) {

          if (error) {
            return reject(error);
          }

          return resolve({
            path: imagePath,
            size: stats.size
          });
        });
      });
    });
  };

  /**
   * @summary Show error dialog for an Error instance
   * @function
   * @public
   *
   * @param {(Error|String)} error - error
   * @param {String} [description] - error description
   *
   * @example
   * OSDialogService.showError(new Error('Foo Bar'));
   *
   * @example
   * OSDialogService.showError(new Error('Foo Bar'), 'A custom description');
   *
   * @example
   * OSDialogService.showError('Foo Bar', 'An error happened!');
   */
  this.showError = function(error, description) {
    error = error || {};

    // Try to get as most information as possible about the error
    // rather than falling back to generic messages right away.
    const title = _.attempt(function() {
      if (_.isString(error)) {
        return error;
      }

      return error.message || error.code || 'An error ocurred';
    });

    const message = description || error.stack || JSON.stringify(error) || '';

    // Ensure the parameters are strings to prevent the following
    // types of obscure errors:
    //
    //   Error: Could not call remote function ''.
    //   Check that the function signature is correct.
    //   Underlying error:
    //     Error processing argument at index 0, conversion failure
    //
    // This can be thrown if for some reason, either `title` or `message`
    // are not strings.
    electron.remote.dialog.showErrorBox(title.toString(), message.toString());
  };

};
<|endoftext|>


<commit_before><commit_msg>remove empty search file<commit_after><|endoftext|>