<commit_before>/* globals Meteor, RocketChat, Tracker, UserPresence */

const { ipcRenderer, shell } = require('electron');
const path = require('path');
const url = require('url');
const Notification = require('./lib/Notification');
const SpellCheck = require('./lib/SpellCheck');
const i18n = require('../i18n/index');

window.Notification = Notification;
window.i18n = i18n;

window.open = ((defaultWindowOpen) => (href, frameName, features) => {
	if (url.parse(href).host === RocketChat.settings.get('Jitsi_Domain')) {
		features = [
			features,
			'nodeIntegration=true',
			`preload=${ path.join(__dirname, 'jitsi-preload.js') }`,
		].filter((x) => Boolean(x)).join(',');
	}

	return defaultWindowOpen(href, frameName, features);
})(window.open);

window.reloadServer = () => ipcRenderer.sendToHost('reload-server');

for (const eventName of ['unread-changed', 'get-sourceId', 'user-status-manually-set']) {
	window.addEventListener(eventName, (event) => ipcRenderer.sendToHost(eventName, event.detail));
}

const changeSidebarColor = () => {
	const sidebar = document.querySelector('.sidebar');
	if (sidebar) {
		const { color, background } = window.getComputedStyle(sidebar);
		const sidebarItem = sidebar.querySelector('.sidebar-item');
		const itemColor = sidebarItem && window.getComputedStyle(sidebarItem).color;
		ipcRenderer.sendToHost('sidebar-background', { color: itemColor || color, background });
		return;
	}

	const fullpage = document.querySelector('.full-page');
	if (fullpage) {
		const { color, background } = window.getComputedStyle(fullpage);
		ipcRenderer.sendToHost('sidebar-background', { color, background });
		return;
	}

	window.requestAnimationFrame(changeSidebarColor);
};

ipcRenderer.on('request-sidebar-color', changeSidebarColor);

window.addEventListener('load', () => {
	if (!Meteor) {
		return;
	}

	Meteor.startup(() => {
		Tracker.autorun(() => {
			const siteName = RocketChat.settings.get('Site_Name');
			if (siteName) {
				ipcRenderer.sendToHost('title-changed', siteName);
			}
		});
	});

	const INTERVAL = 10000; // 10s
	setInterval(() => {
		try {
			const idleTime = ipcRenderer.sendSync('getSystemIdleTime');
			if (idleTime < INTERVAL) {
				UserPresence.setOnline();
			}
		} catch (e) {
			console.error(`Error getting system idle time: ${ e }`);
		}
	}, INTERVAL);

	document.addEventListener('click', (event) => {
		const anchorElement = event.target.closest('a');

		if (!anchorElement) {
			return;
		}

		const { href } = anchorElement;

		// Check href matching current domain
		if (RegExp(`^${ location.protocol }\/\/${ location.host }`).test(href)) {
			return;
		}

		// Check if is file upload link
		if (/^\/file-upload\//.test(href) && !anchorElement.hasAttribute('download')) {
			const tempElement = document.createElement('a');
			tempElement.href = href;
			tempElement.download = 'download';
			tempElement.click();
			return;
		}

		// Check href matching relative URL
		if (!/^([a-z]+:)?\/\//.test(href)) {
			return;
		}

		if (/^file:\/\/.+/.test(href)) {
			const item = href.slice(6);
			shell.showItemInFolder(item);
			event.preventDefault();
			return;
		}

		shell.openExternal(href);
		event.preventDefault();
	}, true);
});

// Prevent redirect to url when dragging in
document.addEventListener('dragover', (event) => event.preventDefault());
document.addEventListener('drop', (event) => event.preventDefault());

const spellChecker = new SpellCheck();
spellChecker.enable();
<commit_msg>Fixed the behavior of clicking links when running RocketChat with subdir.<commit_after>/* globals Meteor, RocketChat, Tracker, UserPresence */

const { ipcRenderer, shell } = require('electron');
const path = require('path');
const url = require('url');
const Notification = require('./lib/Notification');
const SpellCheck = require('./lib/SpellCheck');
const i18n = require('../i18n/index');

window.Notification = Notification;
window.i18n = i18n;

window.open = ((defaultWindowOpen) => (href, frameName, features) => {
	if (url.parse(href).host === RocketChat.settings.get('Jitsi_Domain')) {
		features = [
			features,
			'nodeIntegration=true',
			`preload=${ path.join(__dirname, 'jitsi-preload.js') }`,
		].filter((x) => Boolean(x)).join(',');
	}

	return defaultWindowOpen(href, frameName, features);
})(window.open);

window.reloadServer = () => ipcRenderer.sendToHost('reload-server');

for (const eventName of ['unread-changed', 'get-sourceId', 'user-status-manually-set']) {
	window.addEventListener(eventName, (event) => ipcRenderer.sendToHost(eventName, event.detail));
}

const changeSidebarColor = () => {
	const sidebar = document.querySelector('.sidebar');
	if (sidebar) {
		const { color, background } = window.getComputedStyle(sidebar);
		const sidebarItem = sidebar.querySelector('.sidebar-item');
		const itemColor = sidebarItem && window.getComputedStyle(sidebarItem).color;
		ipcRenderer.sendToHost('sidebar-background', { color: itemColor || color, background });
		return;
	}

	const fullpage = document.querySelector('.full-page');
	if (fullpage) {
		const { color, background } = window.getComputedStyle(fullpage);
		ipcRenderer.sendToHost('sidebar-background', { color, background });
		return;
	}

	window.requestAnimationFrame(changeSidebarColor);
};

ipcRenderer.on('request-sidebar-color', changeSidebarColor);

window.addEventListener('load', () => {
	if (!Meteor) {
		return;
	}

	Meteor.startup(() => {
		Tracker.autorun(() => {
			const siteName = RocketChat.settings.get('Site_Name');
			if (siteName) {
				ipcRenderer.sendToHost('title-changed', siteName);
			}
		});
	});

	const INTERVAL = 10000; // 10s
	setInterval(() => {
		try {
			const idleTime = ipcRenderer.sendSync('getSystemIdleTime');
			if (idleTime < INTERVAL) {
				UserPresence.setOnline();
			}
		} catch (e) {
			console.error(`Error getting system idle time: ${ e }`);
		}
	}, INTERVAL);

	document.addEventListener('click', (event) => {
		const anchorElement = event.target.closest('a');

		if (!anchorElement) {
			return;
		}

		const { href } = anchorElement;

		// Check href matching Rochet.Chat URL
		if (RegExp(`^${ Meteor.absoluteUrl() }`).test(href)) {
			return;
		}

		// Check if is file upload link
		if (/^\/file-upload\//.test(href) && !anchorElement.hasAttribute('download')) {
			const tempElement = document.createElement('a');
			tempElement.href = href;
			tempElement.download = 'download';
			tempElement.click();
			return;
		}

		// Check href matching relative URL
		if (!/^([a-z]+:)?\/\//.test(href)) {
			return;
		}

		if (/^file:\/\/.+/.test(href)) {
			const item = href.slice(6);
			shell.showItemInFolder(item);
			event.preventDefault();
			return;
		}

		shell.openExternal(href);
		event.preventDefault();
	}, true);
});

// Prevent redirect to url when dragging in
document.addEventListener('dragover', (event) => event.preventDefault());
document.addEventListener('drop', (event) => event.preventDefault());

const spellChecker = new SpellCheck();
spellChecker.enable();
<|endoftext|>


<commit_before>const wait = require('./wait')
const reflow = require('./reflow')
const Prebody = require('./prebody')

/**
 * Body has width, height, position and information about how it put at the postion.
 * @abstract
 */
class Body extends Prebody {
  constructor () {
    super()

    /**
     * @deprecated
     */
    this.transitionDuration = this.defaultTransitionDuration()

    /**
     * @property {Number} x sprite's x coordinate value
     */
    this.x = 0

    /**
     * @property {Number} y sprite's y coordinate value
     */
    this.y = 0

    this.getPosture()
  }

  /**
   * Default parameters
   */
  width () { return 100 }
  height () { return 100 }
  ratioX () { return 0 }
  ratioY () { return 0 }
  marginX () { return 0 }
  marginY () { return 0 }
  defaultTransitionDuration () { return 500 }

  /**
   * Updates the actual elem dom according to the current posture.
   * Returns a promise which resolves with the transitionDuration milliseconds.
   * @deprecated
   * @param {Number} [dur] The
   * @return {Promise}
   */
  updateElem (dur) {
    if (dur) {
      this.setTransitionDuration(dur)
    }

    this.updateRect()
    this.updateOffset()

    return wait(this.transitionDuration)
  }

  /**
   * Moves the elem to the given y position.
   * @deprecated
   * @param {Number} to The y position
   */
  moveToY (to) {
    this.y = to

    return this.updateElem()
  }

  /**
   * Moves the elem to the given x position.
   * @deprecated
   * @param {Number} to The x position
   */
  moveToX (to) {
    this.x = to

    return this.updateElem()
  }

  /**
   * Sets the transition duration.
   * @deprecated
   * @param {number} dur The transition duration
   */
  setTransitionDuration (dur) {
    this.transitionDuration = dur

    this.elem.css('transition-duration', dur + 'ms')

    reflow(this.el)
  }
}

module.exports = Body
<commit_msg>fix(body): add placeholder willShow method for backward compat<commit_after>const wait = require('./wait')
const reflow = require('./reflow')
const Prebody = require('./prebody')

/**
 * Body has width, height, position and information about how it put at the postion.
 * @abstract
 */
class Body extends Prebody {
  constructor () {
    super()

    /**
     * @deprecated
     */
    this.transitionDuration = this.defaultTransitionDuration()

    /**
     * @property {Number} x sprite's x coordinate value
     */
    this.x = 0

    /**
     * @property {Number} y sprite's y coordinate value
     */
    this.y = 0

    this.getPosture()
  }

  /**
   * Default parameters
   */
  width () { return 100 }
  height () { return 100 }
  ratioX () { return 0 }
  ratioY () { return 0 }
  marginX () { return 0 }
  marginY () { return 0 }
  defaultTransitionDuration () { return 500 }

  /**
   * Placeholder willShow method.
   */
  willShow() {
  }

  /**
   * Updates the actual elem dom according to the current posture.
   * Returns a promise which resolves with the transitionDuration milliseconds.
   * @deprecated
   * @param {Number} [dur] The
   * @return {Promise}
   */
  updateElem (dur) {
    if (dur) {
      this.setTransitionDuration(dur)
    }

    this.updateRect()
    this.updateOffset()

    return wait(this.transitionDuration)
  }

  /**
   * Moves the elem to the given y position.
   * @deprecated
   * @param {Number} to The y position
   */
  moveToY (to) {
    this.y = to

    return this.updateElem()
  }

  /**
   * Moves the elem to the given x position.
   * @deprecated
   * @param {Number} to The x position
   */
  moveToX (to) {
    this.x = to

    return this.updateElem()
  }

  /**
   * Sets the transition duration.
   * @deprecated
   * @param {number} dur The transition duration
   */
  setTransitionDuration (dur) {
    this.transitionDuration = dur

    this.elem.css('transition-duration', dur + 'ms')

    reflow(this.el)
  }
}

module.exports = Body
<|endoftext|>


<commit_before>var Lettris = {};
Lettris.Boot = function (game) {};

Lettris.Boot.prototype = {
    preload: function () {
	// preload the loading indicator first before anything else
	this.load.image('preloaderBar', 'assets/loading-bar.png');
    },
    create: function () {
	// set scale options
	this.input.maxPointers = 1;
	this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	this.scale.pageAlignHorizontally = true;
	this.scale.pageAlignVertically = true;

	this.game.version = "0.11 Open Beta"

	// start the Preloader state
	this.state.start('Preloader');
    }
};
<commit_msg>0.12 Open Beta!<commit_after>var Lettris = {};
Lettris.Boot = function (game) {};

Lettris.Boot.prototype = {
    preload: function () {
	// preload the loading indicator first before anything else
	this.load.image('preloaderBar', 'assets/loading-bar.png');
    },
    create: function () {
	// set scale options
	this.input.maxPointers = 1;
	this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	this.scale.pageAlignHorizontally = true;
	this.scale.pageAlignVertically = true;

	this.game.version = "0.12 Open Beta"

	// start the Preloader state
	this.state.start('Preloader');
    }
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var midi = require('midi');
var port = 80;

// Midi Stuff
// Set up a new input.
var input = new midi.input();
var output = new midi.output();

// Get the name of a specified input port.
console.log('Opening midi port:', input.getPortName(1));
input.openPort(1);
output.openPort(1);

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

// Routing
app.use(express.static(__dirname + '/public'));

io.on('connection', function (socket) {

  // when the client emits 'new message', this listens and executes
  socket.on('midiout', function (data) {
    console.log('Midi Output:', data);
    output.sendMessage(data);
  });

  // Configure a callback.
  input.on('message', function(deltaTime, message) {
    socket.emit('midiin', message);
  });

});
<commit_msg>Refactor server and add simple debug mode<commit_after>var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var midi = require('midi');
var debug = false;
var httpPort = 80;
var midiPort = 1;

// Midi Stuff
// Set up a new input.
var input = new midi.input();
var output = new midi.output();

// Get the name of a specified input port.
console.log('Opening midi port:', input.getPortName(midiPort));
input.openPort(midiPort);
output.openPort(midiPort);

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

// Routing (for index.html)
app.use(express.static(__dirname + '/public'));

io.on('connection', function (socket) {

  // When we recieve a midiout event, push that to the midi port
  socket.on('midiout', function (data) {
  	debugMessage('OUTPUT', data);
    output.sendMessage(data);
  });

  // Emit a socket for input from the piano to clients
  input.on('message', function(deltaTime, message) {
  	debugMessage('INPUT ', message);
    socket.emit('midiin', message);
  });

});

// Helper func for message debug
function debugMessage() {
  if (debug) console.debug(arguments);
}<|endoftext|>


<commit_before>import {setStyles} from "./css";

const SPECIAL_ATTRIBUTE_SETTERS = /^(html|text|css)$/;

/**
 * Creates an element with the given attributes
 *
 * @param {string} type
 * @param {{html: string, text: string, css: {}}|Object} attributes
 *
 * @returns {HTMLElement}
 */
export function createElement (type, attributes = {})
{
    const element = document.createElement(type);
    setAttrs(element, attributes);

    if (typeof attributes.css !== "undefined")
    {
        setStyles(element, attributes.css);
    }

    if (typeof attributes.text !== "undefined")
    {
        element.textContent = attributes.text;
    }
    else if (typeof attributes.html !== "undefined")
    {
        element.innerHTML = attributes.html;
    }

    return element;
}

/**
 * Sets all attributes on the given element
 *
 * @param {HTMLElement} element
 * @param {Object.<string, string>} attributes
 */
export function setAttrs (element, attributes)
{
    for (const key in attributes)
    {
        if (!attributes.hasOwnProperty(key))
        {
            continue;
        }

        const value = attributes[key];

        if (SPECIAL_ATTRIBUTE_SETTERS.test(key))
        {
            return;
        }

        if (value === null || value === false)
        {
            element.removeAttribute(key);
        }
        else
        {
            element.setAttribute(key, "" + attributes[key]);
        }
    }
}

/**
 * Returns the attribute value for the given html node
 *
 * @param {HTMLElement} element
 * @param {string} attribute
 * @return {?string}
 */
export function getAttr (element, attribute)
{
    return element.getAttribute(attribute);
}

/**
 * Removes the given element(s)
 *
 * @param {HTMLElement|HTMLElement[]} element
 */
export function remove (element)
{
    const list = Array.isArray(element) ? element : [element];

    for (let i = 0; i < list.length; i++)
    {
        list[i].parentNode.removeChild(list[i]);
    }
}

/**
 * Empties the given element(s)
 * @param {HTMLElement|HTMLElement[]} element
 */
export function empty (element)
{
    const list = Array.isArray(element) ? element : [element];

    for (let i = 0; i < list.length; i++)
    {
        list[i].innerHTML = "";
    }
}

/**
 * Replaces the given element with the replacement element
 *
 * @param {HTMLElement} element
 * @param {HTMLElement} replacement
 */
export function replace (element, replacement)
{
    element.parentNode.replaceChild(replacement, element);
}

/**
 * Inserts the given element(s)/HTML string at the given position.
 *
 * @private
 * @param {HTMLElement} reference                       The reference element from which the position is calculated.
 * @param {string|HTMLElement|HTMLElement[]} insert     The elements to insert. Can be an HTML string.
 * @param {string} adjacentPosition                     The argument for the .insertAdjacentHTML() call.
 * @param {Node} insertInto                      The parent element the item is inserted to.
 * @param {?Node} insertReference                The reference element for the .insertBefore() call.
 */
function insertElement (reference, insert, adjacentPosition, insertInto, insertReference)
{
    if (typeof insert === "string")
    {
        reference.insertAdjacentHTML(adjacentPosition, insert);
    }

    const list = Array.isArray(insert) ? insert : [insert];

    for (let i = 0; i < list.length; i++)
    {
        insertInto.insertBefore(list[i], insertReference);
    }
}

/**
 * Inserts the given element/HTML string at the end of the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function append (reference, insert)
{
    insertElement(
        reference,
        insert,
        "beforeend",
        reference,
        null
    );
}

/**
 * Inserts the given element/HTML string at the beginning of the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function prepend (reference, insert)
{
    insertElement(
        reference,
        insert,
        "afterbegin",
        reference,
        reference.firstChild
    );
}

/**
 * Inserts the given element/HTML string just before the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function before (reference, insert)
{
    insertElement(
        reference,
        insert,
        "afterbegin",
        reference.parentNode,
        reference
    );
}

/**
 * Inserts the given element/HTML string just after the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function after (reference, insert)
{
    insertElement(
        reference,
        insert,
        "afterend",
        reference.parentNode,
        reference.nextSibling
    );
}
<commit_msg>Add support for HTML in createElement<commit_after>import {setStyles} from "./css";

const SPECIAL_ATTRIBUTE_SETTERS = /^(html|text|css)$/;

/**
 * Parses the HTML to an HTMLElement
 *
 * @param {string} html
 * @return {HTMLElement}
 */
function parseHtml (html)
{
    const doc = document.implementation.createHTMLDocument();
    doc.body.innerHTML = html;
    const children = doc.body.children;

    if (children.length > 1)
    {
        throw new Error("Can't parse HTML with more than one root elements.");
    }

    return children[0];
}

/**
 * Creates an element with the given attributes
 *
 * @param {string} type
 * @param {{html: string, text: string, css: {}}|Object} attributes
 *
 * @returns {HTMLElement}
 */
export function createElement (type, attributes = {})
{
    let element = (-1 !== type.indexOf("<"))
        ? parseHtml(type)
        : document.createElement(type);

    setAttrs(element, attributes);

    if (typeof attributes.css !== "undefined")
    {
        setStyles(element, attributes.css);
    }

    if (typeof attributes.text !== "undefined")
    {
        element.textContent = attributes.text;
    }
    else if (typeof attributes.html !== "undefined")
    {
        element.innerHTML = attributes.html;
    }

    return element;
}

/**
 * Sets all attributes on the given element
 *
 * @param {HTMLElement} element
 * @param {Object.<string, string>} attributes
 */
export function setAttrs (element, attributes)
{
    for (const key in attributes)
    {
        if (!attributes.hasOwnProperty(key))
        {
            continue;
        }

        const value = attributes[key];

        if (SPECIAL_ATTRIBUTE_SETTERS.test(key))
        {
            return;
        }

        if (value === null || value === false)
        {
            element.removeAttribute(key);
        }
        else
        {
            element.setAttribute(key, "" + attributes[key]);
        }
    }
}

/**
 * Returns the attribute value for the given html node
 *
 * @param {HTMLElement} element
 * @param {string} attribute
 * @return {?string}
 */
export function getAttr (element, attribute)
{
    return element.getAttribute(attribute);
}

/**
 * Removes the given element(s)
 *
 * @param {HTMLElement|HTMLElement[]} element
 */
export function remove (element)
{
    const list = Array.isArray(element) ? element : [element];

    for (let i = 0; i < list.length; i++)
    {
        list[i].parentNode.removeChild(list[i]);
    }
}

/**
 * Empties the given element(s)
 * @param {HTMLElement|HTMLElement[]} element
 */
export function empty (element)
{
    const list = Array.isArray(element) ? element : [element];

    for (let i = 0; i < list.length; i++)
    {
        list[i].innerHTML = "";
    }
}

/**
 * Replaces the given element with the replacement element
 *
 * @param {HTMLElement} element
 * @param {HTMLElement} replacement
 */
export function replace (element, replacement)
{
    element.parentNode.replaceChild(replacement, element);
}

/**
 * Inserts the given element(s)/HTML string at the given position.
 *
 * @private
 * @param {HTMLElement} reference                       The reference element from which the position is calculated.
 * @param {string|HTMLElement|HTMLElement[]} insert     The elements to insert. Can be an HTML string.
 * @param {string} adjacentPosition                     The argument for the .insertAdjacentHTML() call.
 * @param {Node} insertInto                      The parent element the item is inserted to.
 * @param {?Node} insertReference                The reference element for the .insertBefore() call.
 */
function insertElement (reference, insert, adjacentPosition, insertInto, insertReference)
{
    if (typeof insert === "string")
    {
        reference.insertAdjacentHTML(adjacentPosition, insert);
    }

    const list = Array.isArray(insert) ? insert : [insert];

    for (let i = 0; i < list.length; i++)
    {
        insertInto.insertBefore(list[i], insertReference);
    }
}

/**
 * Inserts the given element/HTML string at the end of the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function append (reference, insert)
{
    insertElement(
        reference,
        insert,
        "beforeend",
        reference,
        null
    );
}

/**
 * Inserts the given element/HTML string at the beginning of the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function prepend (reference, insert)
{
    insertElement(
        reference,
        insert,
        "afterbegin",
        reference,
        reference.firstChild
    );
}

/**
 * Inserts the given element/HTML string just before the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function before (reference, insert)
{
    insertElement(
        reference,
        insert,
        "afterbegin",
        reference.parentNode,
        reference
    );
}

/**
 * Inserts the given element/HTML string just after the reference element.
 *
 * @param {HTMLElement} reference
 * @param {string|HTMLElement|HTMLElement[]} insert
 */
export function after (reference, insert)
{
    insertElement(
        reference,
        insert,
        "afterend",
        reference.parentNode,
        reference.nextSibling
    );
}
<|endoftext|>


<commit_before>import { combineReducers } from 'redux'
import get from 'lodash/get'

import { reducer } from 'redux-cozy-client'
import apps from 'ducks/apps'
import * as fromAccounts from 'ducks/accounts'
import * as fromKonnectors from 'ducks/konnectors'
import * as fromTriggers from 'ducks/triggers'
import connections, * as fromConnections from 'ducks/connections'

export default () =>
  combineReducers({
    apps,
    connections,
    cozy: reducer
  })

// selectors
export const getInstalledKonnectors = state =>
  fromKonnectors.getInstalledKonnectors(state.cozy)

export const getConnectionsByKonnector = (state, konnectorSlug) =>
  fromConnections.getConnectionsByKonnector(
    state.connections,
    konnectorSlug,
    fromAccounts.getIds(state.cozy),
    fromKonnectors.getSlugs(state.cozy)
  )

export const getConnectionsQueue = state =>
  fromConnections.getQueue(
    state.connections,
    fromKonnectors.getIndexedKonnectors(state.cozy)
  )

export const getCreatedConnectionAccount = state =>
  fromAccounts.getAccount(
    state.cozy,
    fromConnections.getCreatedAccount(state.connections)
  )

export const getKonnectorTriggersCount = (state, konnector) =>
  fromTriggers.getKonnectorTriggers(
    state.cozy,
    konnector,
    fromAccounts.getIds(state.cozy)
  ).length

export const getKonnectorsInMaintenance = () =>
  fromKonnectors.fetchKonnectorsInMaintenance()

export const getTriggerByKonnectorAndAccount = (state, konnector, account) => {
  const triggerId = fromConnections.getTriggerIdByKonnectorAndAccount(
    state.connections,
    konnector,
    account,
    fromAccounts.getIds(state.cozy)
  )
  return fromTriggers.getTrigger(state.cozy, triggerId)
}

export const getTriggersByKonnector = (state, konnectorSlug) => {
  const triggers = Object.keys(state.cozy.documents['io.cozy.triggers']).reduce(
    (acc, key) => {
      const document = state.cozy.documents['io.cozy.triggers'][key]
      if (
        document.worker === 'konnector' &&
        get(document, 'message.konnector') === konnectorSlug
      ) {
        acc.push(document)
      }
      return acc
    },
    []
  )
  return triggers
}
<commit_msg>fix(Konnector): Don't crash if there is no trigger<commit_after>import { combineReducers } from 'redux'
import get from 'lodash/get'

import { reducer } from 'redux-cozy-client'
import apps from 'ducks/apps'
import * as fromAccounts from 'ducks/accounts'
import * as fromKonnectors from 'ducks/konnectors'
import * as fromTriggers from 'ducks/triggers'
import connections, * as fromConnections from 'ducks/connections'

export default () =>
  combineReducers({
    apps,
    connections,
    cozy: reducer
  })

// selectors
export const getInstalledKonnectors = state =>
  fromKonnectors.getInstalledKonnectors(state.cozy)

export const getConnectionsByKonnector = (state, konnectorSlug) =>
  fromConnections.getConnectionsByKonnector(
    state.connections,
    konnectorSlug,
    fromAccounts.getIds(state.cozy),
    fromKonnectors.getSlugs(state.cozy)
  )

export const getConnectionsQueue = state =>
  fromConnections.getQueue(
    state.connections,
    fromKonnectors.getIndexedKonnectors(state.cozy)
  )

export const getCreatedConnectionAccount = state =>
  fromAccounts.getAccount(
    state.cozy,
    fromConnections.getCreatedAccount(state.connections)
  )

export const getKonnectorTriggersCount = (state, konnector) =>
  fromTriggers.getKonnectorTriggers(
    state.cozy,
    konnector,
    fromAccounts.getIds(state.cozy)
  ).length

export const getKonnectorsInMaintenance = () =>
  fromKonnectors.fetchKonnectorsInMaintenance()

export const getTriggerByKonnectorAndAccount = (state, konnector, account) => {
  const triggerId = fromConnections.getTriggerIdByKonnectorAndAccount(
    state.connections,
    konnector,
    account,
    fromAccounts.getIds(state.cozy)
  )
  return fromTriggers.getTrigger(state.cozy, triggerId)
}

export const getTriggersByKonnector = (state, konnectorSlug) => {
  const triggersInState = state.cozy.documents['io.cozy.triggers'] || {}
  const triggers = Object.keys(triggersInState).reduce((acc, key) => {
    const document = state.cozy.documents['io.cozy.triggers'][key]
    if (
      document.worker === 'konnector' &&
      get(document, 'message.konnector') === konnectorSlug
    ) {
      acc.push(document)
    }
    return acc
  }, [])
  return triggers
}
<|endoftext|>


<commit_before>import { combineReducers } from 'redux'
import { handleActions } from 'redux-actions'
import { createSelector } from 'reselect'

import findKey from 'lodash/findKey'
import get from 'lodash/get'
import isNil from 'lodash/isNil'
import pick from 'lodash/pick'
import pickBy from 'lodash/pickBy'
import range from 'lodash/range'
import reduce from 'lodash/reduce'
import set from 'lodash/set' // <- careful, this mutates the object

import { containerDims, toWellName, getMaxVolumes, wellKeyToXYList } from '../constants.js'
import { uuid } from '../utils.js'

const sortedSlotnames = [].concat.apply( // flatten
  [],
  [1, 2, 3].map(num => ['A', 'B', 'C', 'D', 'E'].map(letter => letter + num))
)

// UTILS

const nextEmptySlot = loadedContainersSubstate => {
  // Next empty slot in the sorted slotnames order. Or null if no more slots.
  const nextEmptySlotIdx = sortedSlotnames.findIndex(slot => !(slot in loadedContainersSubstate))
  const result = nextEmptySlotIdx >= sortedSlotnames.length ? null : sortedSlotnames[nextEmptySlotIdx]
  return result
}

// REDUCERS

// modeLabwareSelection: boolean. If true, we're selecting labware to add to a slot
// (this state just toggles a modal)
const modeLabwareSelection = handleActions({
  OPEN_LABWARE_SELECTOR: (state, action) => action.payload.slotName,
  CLOSE_LABWARE_SELECTOR: (state, action) => false,
  CREATE_CONTAINER: (state, action) => false // close window when labware is selected
}, false)

const selectedContainer = handleActions({
  OPEN_INGREDIENT_SELECTOR: (state, action) => action.payload,
  CLOSE_INGREDIENT_SELECTOR: (state, action) => null
}, null)

const selectedIngredientGroup = handleActions({
  EDIT_MODE_INGREDIENT_GROUP: (state, action) => action.payload, // selected ingredient group to edit
  OPEN_INGREDIENT_SELECTOR: (state, action) => null,
  EDIT_INGREDIENT: (state, action) => null, // unselect ingredient group when edited.
  DELETE_INGREDIENT: (state, action) => null, // unselect ingredient group when deleted.
  CLOSE_INGREDIENT_SELECTOR: (state, action) => null
}, null)

const containers = handleActions({
  CREATE_CONTAINER: (state, action) => ({
    ...state,
    [uuid() + ':' + action.payload.containerType]: {
      slotName: action.payload.slotName || nextEmptySlot(_loadedContainersBySlot(state)),
      type: action.payload.containerType,
      name: null // create with null name, so we force explicit naming.
    }
  }),
  DELETE_CONTAINER: (state, action) => {
    // For leaving open slots functionality, do this one-liner instead
    return pickBy(state, (value, key) => key !== action.payload.containerId)

    // TODO: make the slots slide backward again

    // const deletedSlot = action.payload
    // const deletedIdx = sortedSlotnames.findIndex(slot => slot === deletedSlot)
    // // Summary:
    // //  {A1: 'alex', B1: 'brock', C1: 'charlie'} ==(delete slot B1)==> {A1: 'alex', B1: 'charlie'}
    // const nextState = sortedSlotnames.reduce((acc, slotName, i) => slotName === deletedSlot || !(slotName in state)
    //   ? acc
    //   : ({...acc, [sortedSlotnames[i < deletedIdx ? i : i - 1]]: state[slotName]}),
    //   {})
    //
    // return nextState
  },
  MODIFY_CONTAINER: (state, action) => {
    const { containerId, modify } = action.payload
    return {...state, [containerId]: {...state[containerId], ...modify}}
  }
}, {
  'default-trash': {
    type: 'trash-box',
    name: 'Trash',
    slotName: 'E3'
  }
})

const selectedWellsInitialState = {preselected: {}, selected: {}}
const selectedWells = handleActions({
  PRESELECT_WELLS: (state, action) => action.payload.append
    ? {...state, preselected: action.payload.wells}
    : {selected: {}, preselected: action.payload.wells},
  SELECT_WELLS: (state, action) => ({
    preselected: {},
    selected: {
      ...(action.payload.append ? state.selected : {}),
      ...action.payload.wells
    }
  }),
  // Actions that cause "deselect everything" behavior:
  EDIT_MODE_INGREDIENT_GROUP: (state, action) => selectedWellsInitialState, // ({selected: action.payload.selectedWells, preselected: {}}),
  DESELECT_WELLS: () => selectedWellsInitialState,
  CLOSE_INGREDIENT_SELECTOR: () => selectedWellsInitialState,
  EDIT_INGREDIENT: () => selectedWellsInitialState
}, selectedWellsInitialState)

const highlightedIngredients = handleActions({
  HOVER_WELL_BEGIN: (state, action) => ({ wells: action.payload }),
  HOVER_WELL_END: (state, action) => ({}) // clear highlighting
}, {})

const ingredients = handleActions({
  EDIT_INGREDIENT: (state, action) => {
    const editableIngredFields = ['name', 'serializeName', 'volume', 'concentration', 'description', 'individualize']
    const { groupId, containerId } = action.payload
    if (!isNil(groupId)) {
      // GroupId was given, edit existing ingredient
      return set(
        {...state},
        groupId,
        {
          ...state[groupId],
          ...pick(action.payload, editableIngredFields)
          // TODO: changing wells and wellDetails
        }
      )
    }
    // No groupId, create new ingredient groupId by adding 1 to the highest ID
    // TODO: use uuid
    const newGroupId = Object.keys(state).length === 0
      ? 0
      : Math.max(...Object.keys(state).map(key => parseInt(key))) + 1

    return {
      ...state,
      [newGroupId]: {
        ...pick(action.payload, editableIngredFields),
        locations: { [containerId]: action.payload.wells }
      }
    }
  },
  // Remove the deleted group (referenced by array index)
  DELETE_INGREDIENT: (state, action) => {
    const { wellName, groupId, containerId } = action.payload
    return (wellName)
      ? {
        ...state,
        [groupId]: {
          ...state[groupId],
          locations: {
            ...state[groupId].locations,
            [containerId]: state[groupId].locations[containerId].filter(well => well !== wellName)
          }
        }
      }
      : pickBy(state, (value, key) => key !== groupId)
  }
}, {})

const rootReducer = combineReducers({
  modeLabwareSelection,
  selectedContainer,
  selectedIngredientGroup,
  containers,
  selectedWells,
  ingredients,
  highlightedIngredients
})

// SELECTORS

const rootSelector = state => state.default

const _loadedContainersBySlot = containers =>
  reduce(containers, (acc, container, containerId) => (container.slotName)
    ? {...acc, [container.slotName]: container.type}
    : acc
  , {})

const loadedContainersBySlot = createSelector(
  state => rootSelector(state).containers,
  containers => _loadedContainersBySlot(containers)
)

// const canAdd = createSelector(
//   loadedContainersBySlot,
//   loadedContainers => nextEmptySlot(loadedContainers)
// )

const canAdd = state => rootSelector(state).modeLabwareSelection // false or selected slotName to add labware to, eg 'A2'

// Currently selected container's slot
const selectedContainerSlot = createSelector(
  rootSelector,
  state => get(state, ['selectedContainer', 'slotName'])
)

const selectedContainerSelector = createSelector(
  rootSelector,
  state => state.selectedContainer
)

const containerById = containerId => state => {
  const container = rootSelector(state).containers
  return container && container[containerId]
    ? {
      ...container[containerId],
      containerId
    }
    : null
}

const containersBySlot = createSelector(
  state => rootSelector(state).containers,
  containers => reduce(containers, (acc, containerObj, containerId) =>
    ({
      ...acc,
      // NOTE: containerId added in so you still have a reference
      [containerObj.slotName]: {...containerObj, containerId}
    })
  , {})
)

// Uses selectedSlot to determine container type
const selectedContainerType = createSelector(
  selectedContainerSlot,
  loadedContainersBySlot,
  (slotName, allContainers) => allContainers[slotName]
)

// Given ingredientsForContainer obj and rowNum, colNum,
// returns the ingred data for that well, or `undefined`
// TODO SOON: document the ingredientsForContainer obj returned by that selector (it's upstream of this fn!)
const _ingredAtWell = ingredientsForContainer => ({rowNum, colNum}) => {
  const wellName = toWellName({rowNum, colNum})
  const matchedKey = findKey(ingredientsForContainer, ingred => ingred.wells.includes(wellName))
  const matchedIngred = ingredientsForContainer[matchedKey]

  const ingredientNum = matchedIngred && matchedIngred.wells && matchedIngred.wells.findIndex(w => w === wellName) + 1

  return {...matchedIngred, ingredientNum, wellName}
}

const allIngredients = createSelector(
  rootSelector,
  state => state.ingredients
)

const selectedWellNames = createSelector(
  state => rootSelector(state).selectedWells.selected,
  selectedWells => Object.values(selectedWells).map(well => {
    const col = well[0]
    const row = well[1]
    return toWellName({colNum: col, rowNum: row})
  }) // TODO factor to util
)

const numWellsSelected = createSelector(
  state => rootSelector(state).selectedWells,
  selectedWells => Object.keys(selectedWells.selected).length)

const selectedWellsMaxVolume = createSelector(
  state => rootSelector(state).selectedWells,
  selectedContainerType,
  (selectedWells, selectedContainerType) => {
    const allWells = Object.keys(selectedWells.selected).map(wellKeyToXYList)
    const maxVolumesByWell = getMaxVolumes(selectedContainerType)
    const maxVolumesList = Object.values(pick(maxVolumesByWell, allWells))

    return Math.min(...maxVolumesList)
  }
)

const _ingredientsForContainerId = (allIngredients, containerId) => {
  const ingredGroupFromIdx = (allIngredients, idx) => allIngredients[idx]

  const ingredGroupConvert = (ingredGroup, groupId) => ({
    ...ingredGroup,
    groupId,
    // Convert deck-wide data to container-specific
    wells: ingredGroup.locations[containerId],
    wellDetails: get(ingredGroup, ['wellDetailsByLocation', containerId]),
    // Hide the deck-wide data
    locations: undefined,
    wellDetailsByLocation: undefined
  })

  return Object.keys(allIngredients).map(idx => {
    const ingredGroup = ingredGroupFromIdx(allIngredients, idx)
    return ingredGroup.locations && containerId in ingredGroup.locations
    ? ingredGroupConvert(ingredGroup, idx)
    : false
  }).filter(ingred => ingred !== false)
}
const ingredientsForContainer = createSelector(
  allIngredients,
  selectedContainerSelector,
  (allIngredients, selectedContainer) => {
    return selectedContainer.containerId
    ? _ingredientsForContainerId(allIngredients, selectedContainer.containerId)
    : null
  }
)

// returns selected group id (index in array of all ingredients), or undefined.
// groupId is a string eg '42'
const selectedIngredientGroupId = createSelector(
  rootSelector,
  state => get(state, ['selectedIngredientGroup', 'groupId'])
)

const _selectedIngredientGroupObj = createSelector(
  selectedIngredientGroupId,
  allIngredients,
  (ingredGroupId, allIngredients) => allIngredients[ingredGroupId]
    ? ({...allIngredients[ingredGroupId], groupId: ingredGroupId})
    : null
)

const selectedIngredientProperties = createSelector(
  _selectedIngredientGroupObj,
  ingredGroup => (!isNil(ingredGroup))
    ? pick(ingredGroup, ['name', 'serializeName', 'volume', 'concentration', 'description', 'individualize', 'groupId'])
    : null
)

const _getWellMatrix = (containerType, ingredientsForContainer, selectedWells, highlightedWells) => {
  // selectedWells and highlightedWells args may both be null,
  // they're only relevant to the selected container.
  if (!containerType) {
    return undefined
  }
  const { rows, columns, wellShape, maxVolumes } = containerDims(containerType)

  return range(rows - 1, -1, -1).map(
    rowNum => range(columns).map(
      colNum => {
        const wellKey = colNum + ',' + rowNum // Key in selectedWells from getCollidingWells fn

        // get ingred data, or set to null if the well is empty
        const ingredData = _ingredAtWell(ingredientsForContainer)({rowNum, colNum}) || null

        const isHighlighted = highlightedWells ? wellKey in highlightedWells : false

        return {
          wellShape,
          preselected: selectedWells ? wellKey in selectedWells.preselected : false,
          highlighted: isHighlighted,
          maxVolume: maxVolumes[ingredData.wellName] || maxVolumes.default,
          hovered: highlightedWells && isHighlighted && Object.keys(highlightedWells).length === 1,
          selected: selectedWells ? wellKey in selectedWells.selected : false,
          ...ingredData // TODO later: nest this so it looks cleaner?
        }
      }
    )
  )
}

const allWellMatricesById = createSelector(
  allIngredients,
  state => rootSelector(state).containers,
  (allIngredients, containers, selectedWells) => reduce(containers, (acc, container, containerId) => {
    const wellMatrix = _getWellMatrix(
      container.type,
      _ingredientsForContainerId(allIngredients, containerId),
      null, // selectedWells is only for the selected container, so treat as empty selection.
      null // so ig highlightedWells
    )

    return {
      ...acc,
      [containerId]: wellMatrix
    }
  }, {})
)

const wellMatrixSelectedContainer = createSelector(
  selectedContainerType,
  ingredientsForContainer,
  state => rootSelector(state).selectedWells, // wells are selected only for the selected container.
  state => rootSelector(state).highlightedIngredients.wells,
  _getWellMatrix
)

// TODO: just use the individual selectors separately, no need to combine it into 'activeModals'
// -- so you'd have to refactor the props of the containers that use this selector too
const activeModals = createSelector(
  rootSelector,
  selectedContainerSlot,
  selectedContainerType,
  (state, slotName, containerType) => ({
    labwareSelection: state.modeLabwareSelection !== false,
    ingredientSelection: {
      slotName,
      containerName: containerType
    }
  })
)

// TODO: prune selectors
export const selectors = {
  activeModals,
  allWellMatricesById,
  loadedContainersBySlot,
  containersBySlot,
  canAdd,
  wellMatrixSelectedContainer,
  numWellsSelected,
  selectedWellsMaxVolume,
  selectedWellNames,
  selectedContainerSlot,
  selectedContainer: selectedContainerSelector,
  containerById,
  ingredientsForContainer,
  selectedIngredientProperties,
  selectedIngredientGroupId
}

export default rootReducer
<commit_msg>protocol designer: use max vol of all the wells when none selected; reorder selectors<commit_after>import { combineReducers } from 'redux'
import { handleActions } from 'redux-actions'
import { createSelector } from 'reselect'

import findKey from 'lodash/findKey'
import get from 'lodash/get'
import isNil from 'lodash/isNil'
import pick from 'lodash/pick'
import pickBy from 'lodash/pickBy'
import range from 'lodash/range'
import reduce from 'lodash/reduce'
import set from 'lodash/set' // <- careful, this mutates the object

import { containerDims, toWellName, getMaxVolumes, wellKeyToXYList } from '../constants.js'
import { uuid } from '../utils.js'

const sortedSlotnames = [].concat.apply( // flatten
  [],
  [1, 2, 3].map(num => ['A', 'B', 'C', 'D', 'E'].map(letter => letter + num))
)

// UTILS

const nextEmptySlot = loadedContainersSubstate => {
  // Next empty slot in the sorted slotnames order. Or null if no more slots.
  const nextEmptySlotIdx = sortedSlotnames.findIndex(slot => !(slot in loadedContainersSubstate))
  const result = nextEmptySlotIdx >= sortedSlotnames.length ? null : sortedSlotnames[nextEmptySlotIdx]
  return result
}

// REDUCERS

// modeLabwareSelection: boolean. If true, we're selecting labware to add to a slot
// (this state just toggles a modal)
const modeLabwareSelection = handleActions({
  OPEN_LABWARE_SELECTOR: (state, action) => action.payload.slotName,
  CLOSE_LABWARE_SELECTOR: (state, action) => false,
  CREATE_CONTAINER: (state, action) => false // close window when labware is selected
}, false)

const selectedContainer = handleActions({
  OPEN_INGREDIENT_SELECTOR: (state, action) => action.payload,
  CLOSE_INGREDIENT_SELECTOR: (state, action) => null
}, null)

const selectedIngredientGroup = handleActions({
  EDIT_MODE_INGREDIENT_GROUP: (state, action) => action.payload, // selected ingredient group to edit
  OPEN_INGREDIENT_SELECTOR: (state, action) => null,
  EDIT_INGREDIENT: (state, action) => null, // unselect ingredient group when edited.
  DELETE_INGREDIENT: (state, action) => null, // unselect ingredient group when deleted.
  CLOSE_INGREDIENT_SELECTOR: (state, action) => null
}, null)

const containers = handleActions({
  CREATE_CONTAINER: (state, action) => ({
    ...state,
    [uuid() + ':' + action.payload.containerType]: {
      slotName: action.payload.slotName || nextEmptySlot(_loadedContainersBySlot(state)),
      type: action.payload.containerType,
      name: null // create with null name, so we force explicit naming.
    }
  }),
  DELETE_CONTAINER: (state, action) => {
    // For leaving open slots functionality, do this one-liner instead
    return pickBy(state, (value, key) => key !== action.payload.containerId)

    // TODO: make the slots slide backward again

    // const deletedSlot = action.payload
    // const deletedIdx = sortedSlotnames.findIndex(slot => slot === deletedSlot)
    // // Summary:
    // //  {A1: 'alex', B1: 'brock', C1: 'charlie'} ==(delete slot B1)==> {A1: 'alex', B1: 'charlie'}
    // const nextState = sortedSlotnames.reduce((acc, slotName, i) => slotName === deletedSlot || !(slotName in state)
    //   ? acc
    //   : ({...acc, [sortedSlotnames[i < deletedIdx ? i : i - 1]]: state[slotName]}),
    //   {})
    //
    // return nextState
  },
  MODIFY_CONTAINER: (state, action) => {
    const { containerId, modify } = action.payload
    return {...state, [containerId]: {...state[containerId], ...modify}}
  }
}, {
  'default-trash': {
    type: 'trash-box',
    name: 'Trash',
    slotName: 'E3'
  }
})

const selectedWellsInitialState = {preselected: {}, selected: {}}
const selectedWells = handleActions({
  PRESELECT_WELLS: (state, action) => action.payload.append
    ? {...state, preselected: action.payload.wells}
    : {selected: {}, preselected: action.payload.wells},
  SELECT_WELLS: (state, action) => ({
    preselected: {},
    selected: {
      ...(action.payload.append ? state.selected : {}),
      ...action.payload.wells
    }
  }),
  // Actions that cause "deselect everything" behavior:
  EDIT_MODE_INGREDIENT_GROUP: (state, action) => selectedWellsInitialState, // ({selected: action.payload.selectedWells, preselected: {}}),
  DESELECT_WELLS: () => selectedWellsInitialState,
  CLOSE_INGREDIENT_SELECTOR: () => selectedWellsInitialState,
  EDIT_INGREDIENT: () => selectedWellsInitialState
}, selectedWellsInitialState)

const highlightedIngredients = handleActions({
  HOVER_WELL_BEGIN: (state, action) => ({ wells: action.payload }),
  HOVER_WELL_END: (state, action) => ({}) // clear highlighting
}, {})

const ingredients = handleActions({
  EDIT_INGREDIENT: (state, action) => {
    const editableIngredFields = ['name', 'serializeName', 'volume', 'concentration', 'description', 'individualize']
    const { groupId, containerId } = action.payload
    if (!isNil(groupId)) {
      // GroupId was given, edit existing ingredient
      return set(
        {...state},
        groupId,
        {
          ...state[groupId],
          ...pick(action.payload, editableIngredFields)
          // TODO: changing wells and wellDetails
        }
      )
    }
    // No groupId, create new ingredient groupId by adding 1 to the highest ID
    // TODO: use uuid
    const newGroupId = Object.keys(state).length === 0
      ? 0
      : Math.max(...Object.keys(state).map(key => parseInt(key))) + 1

    return {
      ...state,
      [newGroupId]: {
        ...pick(action.payload, editableIngredFields),
        locations: { [containerId]: action.payload.wells }
      }
    }
  },
  // Remove the deleted group (referenced by array index)
  DELETE_INGREDIENT: (state, action) => {
    const { wellName, groupId, containerId } = action.payload
    return (wellName)
      ? {
        ...state,
        [groupId]: {
          ...state[groupId],
          locations: {
            ...state[groupId].locations,
            [containerId]: state[groupId].locations[containerId].filter(well => well !== wellName)
          }
        }
      }
      : pickBy(state, (value, key) => key !== groupId)
  }
}, {})

const rootReducer = combineReducers({
  modeLabwareSelection,
  selectedContainer,
  selectedIngredientGroup,
  containers,
  selectedWells,
  ingredients,
  highlightedIngredients
})

// SELECTORS

const rootSelector = state => state.default

const _loadedContainersBySlot = containers =>
  reduce(containers, (acc, container, containerId) => (container.slotName)
    ? {...acc, [container.slotName]: container.type}
    : acc
  , {})

const loadedContainersBySlot = createSelector(
  state => rootSelector(state).containers,
  containers => _loadedContainersBySlot(containers)
)

// const canAdd = createSelector(
//   loadedContainersBySlot,
//   loadedContainers => nextEmptySlot(loadedContainers)
// )

const canAdd = state => rootSelector(state).modeLabwareSelection // false or selected slotName to add labware to, eg 'A2'

// Currently selected container's slot
const selectedContainerSlot = createSelector(
  rootSelector,
  state => get(state, ['selectedContainer', 'slotName'])
)

const selectedContainerSelector = createSelector(
  rootSelector,
  state => state.selectedContainer
)

const containerById = containerId => state => {
  const container = rootSelector(state).containers
  return container && container[containerId]
    ? {
      ...container[containerId],
      containerId
    }
    : null
}

const containersBySlot = createSelector(
  state => rootSelector(state).containers,
  containers => reduce(containers, (acc, containerObj, containerId) =>
    ({
      ...acc,
      // NOTE: containerId added in so you still have a reference
      [containerObj.slotName]: {...containerObj, containerId}
    })
  , {})
)

// Uses selectedSlot to determine container type
const selectedContainerType = createSelector(
  selectedContainerSlot,
  loadedContainersBySlot,
  (slotName, allContainers) => allContainers[slotName]
)

// Given ingredientsForContainer obj and rowNum, colNum,
// returns the ingred data for that well, or `undefined`
// TODO SOON: document the ingredientsForContainer obj returned by that selector (it's upstream of this fn!)
const _ingredAtWell = ingredientsForContainer => ({rowNum, colNum}) => {
  const wellName = toWellName({rowNum, colNum})
  const matchedKey = findKey(ingredientsForContainer, ingred => ingred.wells.includes(wellName))
  const matchedIngred = ingredientsForContainer[matchedKey]

  const ingredientNum = matchedIngred && matchedIngred.wells && matchedIngred.wells.findIndex(w => w === wellName) + 1

  return {...matchedIngred, ingredientNum, wellName}
}

const allIngredients = createSelector(
  rootSelector,
  state => state.ingredients
)

// returns selected group id (index in array of all ingredients), or undefined.
// groupId is a string eg '42'
const selectedIngredientGroupId = createSelector(
  rootSelector,
  state => get(state, ['selectedIngredientGroup', 'groupId'])
)

const _selectedIngredientGroupObj = createSelector(
  selectedIngredientGroupId,
  allIngredients,
  (ingredGroupId, allIngredients) => allIngredients[ingredGroupId]
    ? ({...allIngredients[ingredGroupId], groupId: ingredGroupId})
    : null
)

const selectedIngredientProperties = createSelector(
  _selectedIngredientGroupObj,
  ingredGroup => (!isNil(ingredGroup))
    ? pick(ingredGroup, ['name', 'serializeName', 'volume', 'concentration', 'description', 'individualize', 'groupId'])
    : null
)

const selectedWellNames = createSelector(
  state => rootSelector(state).selectedWells.selected,
  selectedWells => Object.values(selectedWells).map(well => {
    const col = well[0]
    const row = well[1]
    return toWellName({colNum: col, rowNum: row})
  }) // TODO factor to util
)

const numWellsSelected = createSelector(
  state => rootSelector(state).selectedWells,
  selectedWells => Object.keys(selectedWells.selected).length)

const selectedWellsMaxVolume = createSelector(
  state => rootSelector(state).selectedWells,
  selectedContainerType,
  (selectedWells, selectedContainerType) => {
    const selectedWellsXY = Object.keys(selectedWells.selected).map(wellKeyToXYList)
    const maxVolumesByWell = getMaxVolumes(selectedContainerType)
    const maxVolumesList = (selectedWellsXY.length > 0)
      // when wells are selected, only look at vols of selected wells
      ? Object.values(pick(maxVolumesByWell, selectedWellsXY))
      // when no wells selected (eg editing ingred group), look at all volumes.
      // TODO LATER: look at filled wells, not all wells.
      : Object.values(maxVolumesByWell)
    return Math.min(...maxVolumesList)
  }
)

const _ingredientsForContainerId = (allIngredients, containerId) => {
  const ingredGroupFromIdx = (allIngredients, idx) => allIngredients[idx]

  const ingredGroupConvert = (ingredGroup, groupId) => ({
    ...ingredGroup,
    groupId,
    // Convert deck-wide data to container-specific
    wells: ingredGroup.locations[containerId],
    wellDetails: get(ingredGroup, ['wellDetailsByLocation', containerId]),
    // Hide the deck-wide data
    locations: undefined,
    wellDetailsByLocation: undefined
  })

  return Object.keys(allIngredients).map(idx => {
    const ingredGroup = ingredGroupFromIdx(allIngredients, idx)
    return ingredGroup.locations && containerId in ingredGroup.locations
    ? ingredGroupConvert(ingredGroup, idx)
    : false
  }).filter(ingred => ingred !== false)
}
const ingredientsForContainer = createSelector(
  allIngredients,
  selectedContainerSelector,
  (allIngredients, selectedContainer) => {
    return selectedContainer.containerId
    ? _ingredientsForContainerId(allIngredients, selectedContainer.containerId)
    : null
  }
)

const _getWellMatrix = (containerType, ingredientsForContainer, selectedWells, highlightedWells) => {
  // selectedWells and highlightedWells args may both be null,
  // they're only relevant to the selected container.
  if (!containerType) {
    return undefined
  }
  const { rows, columns, wellShape, maxVolumes } = containerDims(containerType)

  return range(rows - 1, -1, -1).map(
    rowNum => range(columns).map(
      colNum => {
        const wellKey = colNum + ',' + rowNum // Key in selectedWells from getCollidingWells fn

        // get ingred data, or set to null if the well is empty
        const ingredData = _ingredAtWell(ingredientsForContainer)({rowNum, colNum}) || null

        const isHighlighted = highlightedWells ? wellKey in highlightedWells : false

        return {
          wellShape,
          preselected: selectedWells ? wellKey in selectedWells.preselected : false,
          highlighted: isHighlighted,
          maxVolume: maxVolumes[ingredData.wellName] || maxVolumes.default,
          hovered: highlightedWells && isHighlighted && Object.keys(highlightedWells).length === 1,
          selected: selectedWells ? wellKey in selectedWells.selected : false,
          ...ingredData // TODO later: nest this so it looks cleaner?
        }
      }
    )
  )
}

const allWellMatricesById = createSelector(
  allIngredients,
  state => rootSelector(state).containers,
  (allIngredients, containers, selectedWells) => reduce(containers, (acc, container, containerId) => {
    const wellMatrix = _getWellMatrix(
      container.type,
      _ingredientsForContainerId(allIngredients, containerId),
      null, // selectedWells is only for the selected container, so treat as empty selection.
      null // so ig highlightedWells
    )

    return {
      ...acc,
      [containerId]: wellMatrix
    }
  }, {})
)

const wellMatrixSelectedContainer = createSelector(
  selectedContainerType,
  ingredientsForContainer,
  state => rootSelector(state).selectedWells, // wells are selected only for the selected container.
  state => rootSelector(state).highlightedIngredients.wells,
  _getWellMatrix
)

// TODO: just use the individual selectors separately, no need to combine it into 'activeModals'
// -- so you'd have to refactor the props of the containers that use this selector too
const activeModals = createSelector(
  rootSelector,
  selectedContainerSlot,
  selectedContainerType,
  (state, slotName, containerType) => ({
    labwareSelection: state.modeLabwareSelection !== false,
    ingredientSelection: {
      slotName,
      containerName: containerType
    }
  })
)

// TODO: prune selectors
export const selectors = {
  activeModals,
  allWellMatricesById,
  loadedContainersBySlot,
  containersBySlot,
  canAdd,
  wellMatrixSelectedContainer,
  numWellsSelected,
  selectedWellsMaxVolume,
  selectedWellNames,
  selectedContainerSlot,
  selectedContainer: selectedContainerSelector,
  containerById,
  ingredientsForContainer,
  selectedIngredientProperties,
  selectedIngredientGroupId
}

export default rootReducer
<|endoftext|>


<commit_before>import { combineReducers } from 'redux';
import fuelSavingsAppState from './fuelSavings';
import cmsAppState from './cms.js';

const rootReducer = combineReducers({
  fuelSavingsAppState,
  cmsAppState
});

export default rootReducer;
<commit_msg>Add key names for rootReducer<commit_after>import { combineReducers } from 'redux';
import fuelSavingsAppState from './fuelSavings';
import cmsAppState from './cms.js';

const rootReducer = combineReducers({
  fuelSavingsAppState: fuelSavingsAppState,
  cmsAppState: cmsAppState
});

export default rootReducer;
<|endoftext|>


<commit_before>'use strict';

/**
 * Creates a new Type editor and sets up the core
 * modules used for WYSIWYG editing. The core
 * class only holds methods for setting and retrieving
 * options as well getters and setters for instances
 * of core modules.
 *
 * @param {{}|Element} options - Either pass
 *     an associative array with options for this
 *     editor or the root element that should be
 *     used to modify its contents for WYSIWYG
 *     editing
 * @param {Element} options.el The root element
 *     that should be used to modify its contents
 *     for WYSIWYG editing
 * @constructor
 */
function Type(options) {

  // Allow passing an element as only parameter
  if (Type.DomUtilities.isNode(options)) {
    options = {el: options};
  }

  // If no element has been passed, interrupt
  if (!options.el) {
    throw new Error('You must provide an element as root node for the editor\'s contents.');
  }

  // Set settings for this editor
  this._root = null;
  this._options = null;
  this.options(options);

  // Enable editing mode on root element
  this._setElementEditable(this._root);

  // Trigger events
  Type.trigger('ready', this);

}

/**
 * Inherit event system
 */
Type.OOP.inherits(Type, Type.Events);

(function() {

  /**
   * Allows fast detection if an object is a Type Editor
   * instance (or class)
   *
   * @type {boolean}
   */
  this.typeEditor = true;

  /**
   * Holds the default options for every editor. These options
   * will be extended by the options passed to each instance
   * on instantiation.
   *
   * @type {{el: null, undoSteps: number}}
   * @private
   */
  this._defaultOptions = {
    el: null,
    undoSteps: 20,
  };

  /**
   * Sets or gets the options to be used by this Type instance.
   * Parameters can be passed as you know it from jQuery:
   *
   * Pass a single string to get an option:
   * this.options('el')
   * returns your editor's TypeContents baseelement
   *
   * Pass a name value combination to set a specific option
   * this.options('el', myElement)
   * sets the base element
   *
   * Pass an object to set multiple options
   * this.options({el: myElement, foo:bar})
   * sets both parameters
   *
   * @param {(string|{el: Element}|{}|*)} options - Either a plain object
   *     with keys and values to be set or a string that will
   *     be used as a name for a option. If you pass a string,
   *     pass a second parameter to set that option or no
   *     second parameter to retrieve that option.
   * @param {*} [value] - If the first parameter is a string,
   *     this value will be set to the key of the given first
   *     parameter. Any arbitrary value can be set.
   * @returns {Type|*} Returns the type instance if you set an
   *     option or the according value if you get an option
   */
  this.options = function(options, value) {

    var opts;

    // Load default options if there are no instance options yet
    this._options = this._options || Type.Utilities.extend({}, this._defaultOptions);

    // Pass a single option name to fetch it
    if (typeof options === 'string' && arguments.length === 1) {
      return this._options[options];
    }

    // Pass an option name and a value to set it
    if (typeof options === 'string' && arguments.length === 2) {
      opts = {};
      opts[options] = value;
      options = opts;
    }

    // Pass an object of key-values to set them
    if (typeof options === 'object') {
      Type.Utilities.extend(this._options, options);
    }

    // If the el option has been passed copy it for quick access
    if (options.el) {
      this._root = options.el;
    }

    // Chaining
    return arguments.length ? this : this._options;

  };

  /**
   * Creates a {Type.DomWalker} that ist constrained to this
   * instance's root element unless you explicitly pass a
   * constrainingNode as argument. All other DomWalker options
   * can also be passed to this as usual.
   *
   * @param {Node} node - Any DOM {Node} to be set as starting
   *     node for the DomWalker
   * @param {Node|string|Function|{constrainingNode: Node, filter: string|Function}} [options]
   *     See {Type.DomWalker} for a description of possible arguments
   * @returns {Type.DomWalker}
   */
  this.createDomWalker = function(node, options) {
    options = Type.DomWalker.loadOptions(options || {});
    options.constrainingNode = options.constrainingNode || this._root;
    return new Type.DomWalker(node, options);
  };

  /**
   * Getter for this instance's root element, i.e. the
   * element that contains this editor's text.
   * @returns {Element}
   */
  this.getRoot = function() {
    return this._root;
  };

  /**
   * Sets the editing mode of an element. The second parameter
   * determines if the editing mode should be enabled or disabled.
   * If no second parameter is give this defaults to true.
   *
   * @param {Element} el - The element to set the editing mode for
   * @param {boolean|string} [val] - Set to true to enable editing mode
   *     or false to disable it. Optional, defaults to true.
   * @returns {Type}
   * @private
   */
  this._setElementEditable = function(el, val) {
    val = !val ? 'false' : 'true';
    el.setAttribute('contenteditable', val);
    return this;
  };

}).call(Type.prototype);

/**
 * Exposes Type's prototype as jQuery-style shorthand variable
 * @type {Object}
 */
Type.fn = Type.prototype;

/**
 * The namespace for Type actions
 * @type {{}}
 */
Type.Actions = {};

/**
 * Module Exports for CommonJs
 * @type {Type}
 */
module.exports = Type;
<commit_msg>Started reworking type js<commit_after>'use strict';

/**
 * Creates a new Type editor and sets up the core
 * modules used for WYSIWYG editing. The core
 * class only holds methods for setting and retrieving
 * options as well getters and setters for instances
 * of core modules.
 *
 * @param {{}|Element} options - Either pass
 *     an associative array with options for this
 *     editor or the root element that should be
 *     used to modify its contents for WYSIWYG
 *     editing
 * @param {Element} options.el The root element
 *     that should be used to modify its contents
 *     for WYSIWYG editing
 * @constructor
 */
function Type(options) {

  // Allow passing an element as only parameter
  if (Type.DomUtilities.isNode(options)) {
    options = {el: options};
  }

  // If no element has been passed, interrupt
  if (!options.el) {
    throw new Error('You must provide an element as root node for the editor\'s contents.');
  }

  // Set settings for this editor
  this._options = null;
  this.options(options);

  // Enable editing mode on root element
  this._setElementEditable(options.el);

  // Trigger events
  Type.trigger('ready', this);

}

/**
 * Inherit event system
 */
Type.OOP.inherits(Type, Type.Events);

(function() {

  /**
   * Allows fast detection if an object is a Type Editor
   * instance (or class)
   *
   * @type {boolean}
   */
  this.typeEditor = true;

  /**
   * Holds the default options for every editor. These options
   * will be extended by the options passed to each instance
   * on instantiation.
   *
   * @type {{el: null, undoSteps: number}}
   * @private
   */
  this._defaultOptions = {
    el: null,
    undoSteps: 20,
  };

  /**
   * Sets or gets the options to be used by this Type instance.
   * Parameters can be passed as you know it from jQuery:
   *
   * Pass a single string to get an option:
   * this.options('el')
   * returns your editor's TypeContents baseelement
   *
   * Pass a name value combination to set a specific option
   * this.options('el', myElement)
   * sets the base element
   *
   * Pass an object to set multiple options
   * this.options({el: myElement, foo:bar})
   * sets both parameters
   *
   * @param {(string|{el: Element}|{}|*)} options - Either a plain object
   *     with keys and values to be set or a string that will
   *     be used as a name for a option. If you pass a string,
   *     pass a second parameter to set that option or no
   *     second parameter to retrieve that option.
   * @param {*} [value] - If the first parameter is a string,
   *     this value will be set to the key of the given first
   *     parameter. Any arbitrary value can be set.
   * @returns {Type|*} Returns the type instance if you set an
   *     option or the according value if you get an option
   */
  this.options = function(options, value) {

    var opts;

    // Load default options if there are no instance options yet
    this._options = this._options || Type.Utilities.extend({}, this._defaultOptions);

    // Pass a single option name to fetch it
    if (typeof options === 'string' && arguments.length === 1) {
      return this._options[options];
    }

    // Pass an option name and a value to set it
    if (typeof options === 'string' && arguments.length === 2) {
      opts = {};
      opts[options] = value;
      options = opts;
    }

    // Pass an object of key-values to set them
    if (typeof options === 'object') {
      Type.Utilities.extend(this._options, options);
    }

    // Chaining
    return arguments.length ? this : this._options;

  };

  /**
   * Creates a {Type.DomWalker} that ist constrained to this
   * instance's root element unless you explicitly pass a
   * constrainingNode as argument. All other DomWalker options
   * can also be passed to this as usual.
   *
   * @param {Node} node - Any DOM {Node} to be set as starting
   *     node for the DomWalker
   * @param {Node|string|Function|{constrainingNode: Node, filter: string|Function}} [options]
   *     See {Type.DomWalker} for a description of possible arguments
   * @returns {Type.DomWalker}
   */
  this.createDomWalker = function(node, options) {
    options = Type.DomWalker.loadOptions(options || {});
    options.constrainingNode = options.constrainingNode || this._root;
    return new Type.DomWalker(node, options);
  };

  /**
   * Getter for this instance's root element, i.e. the
   * element that contains this editor's text.
   * @returns {Element}
   */
  this.getEl = function() {
    return this._options.el;
  };

  /**
   * Sets the editing mode of an element. The second parameter
   * determines if the editing mode should be enabled or disabled.
   * If no second parameter is give this defaults to true.
   *
   * @param {Element} el - The element to set the editing mode for
   * @param {boolean|string} [val] - Set to true to enable editing mode
   *     or false to disable it. Optional, defaults to true.
   * @returns {Type}
   * @private
   */
  this._setElementEditable = function(el, val) {
    val = !val ? 'false' : 'true';
    el.setAttribute('contenteditable', val);
    return this;
  };

}).call(Type.prototype);

/**
 * Exposes Type's prototype as jQuery-style shorthand variable
 * @type {Object}
 */
Type.fn = Type.prototype;

/**
 * Module Exports for CommonJs
 * @type {Type}
 */
module.exports = Type;
<|endoftext|>


<commit_before>import {combineReducers} from 'redux';
import {reducer as notifications} from 'react-notification-system-redux';
import { reducer as formReducer } from 'redux-form';
import Chess from 'chess.js';

import FourChess from '../../common/fourchess';
import rooms from './rooms_reducer';
import AuthReducer from './auth_reducer';
import newGameOptions from './new_game_reducer';

import {SELECTED_ROOM,LOGOUT_SUCCESS} from '../actions/types';

function activeThread (state = 'Global', action) {
    switch(action.type) {
        case SELECTED_ROOM:
            return action.payload;
        case LOGOUT_SUCCESS:
            return 'Global';
        default:
            return state;
    }
}

function openThreads(state = {}, action) {
    let obj = null, newState = null;
    let roomName = null;
    switch(action.type) {
        case 'user-room-joined':
            const users = action.payload.users;
            roomName = action.payload.room.name;
            obj = {...state[roomName], users};
            return {...state, [roomName]: obj};
        case 'user-room-left':
            roomName = action.payload.name;
            const user = action.payload.user;
            newState = Object.assign({}, state);
            newState[roomName].users = newState[roomName].users.filter((member) => {
                return user.user._id !== member._id;
            });
            return newState;
        case 'joined-room':
            if(action.payload.fen) {
                if(action.payload.gameType == 'four-player') {
                    action.payload.game = new FourChess();
                    action.payload.game.position(action.payload.fen);
                } else if(action.payload.gameType == 'two-player'){
                    action.payload.game = new Chess(action.payload.fen);
                }
            }
            return {...state, [action.payload.room.name]: action.payload };
        case 'left-room':
            newState = Object.assign({}, state);
            delete newState[action.payload];
            return newState;
        case 'receive-message':
            const messages = [...state[action.payload.thread].messages, action.payload];
            obj = {...state[action.payload.thread], messages};
            return {...state, [action.payload.thread]: obj };
        case 'new-move':
            newState = Object.assign({}, state);
            newState[action.payload.thread].game = new Chess(action.payload.fen);
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread][action.payload.lastTurn].time = action.payload.time;
            return newState;
        case 'four-new-move':
            newState = Object.assign({}, state);
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread].game = new FourChess();
            newState[action.payload.thread].game.position(action.payload.fen);
            newState[action.payload.thread].turn = newState[action.payload.thread].game.turn();
            //newState[action.payload.thread][action.payload.lastTurn].time = action.payload.time;
            return newState;
        case 'four-resign':
            newState = Object.assign({}, state);
            switch(action.payload.color) {
                case 'w':
                    newState[action.payload.thread].white.resigned = true;
                    break;
                case 'b':
                    newState[action.payload.thread].black.resigned = true;
                    break;
                case 'g':
                    newState[action.payload.thread].gold.resigned = true;
                    break;
                case 'r':
                    newState[action.payload.thread].red.resigned = true;
                    break;
            }
            return newState;
        case 'game-over':
            newState = Object.assign({}, state);
            delete newState[action.payload].game;
            delete newState[action.payload].fen;
            delete newState[action.payload].pgn;
            delete newState[action.payload].white;
            delete newState[action.payload].black;
            delete newState[action.payload].gold;
            delete newState[action.payload].red;
            return newState;
        case 'timer-sync':
            if(state[action.payload.thread][action.payload.turn]) {
                newState = Object.assign({}, state);
                newState[action.payload.thread][action.payload.turn].time = action.payload.timeLeft;
                return newState;
            }
            return state;
        case 'sit-down-white':
            newState = Object.assign({}, state);
            newState[action.payload.thread].white = action.payload.room;
            return newState;
        case 'sit-down-black':
            newState = Object.assign({}, state);
            newState[action.payload.thread].black = action.payload.room;
            return newState;
        case 'sit-down-gold':
            newState = Object.assign({}, state);
            newState[action.payload.thread].gold = action.payload.room;
            return newState;
        case 'sit-down-red':
            newState = Object.assign({}, state);
            newState[action.payload.thread].red = action.payload.room;
            return newState;
        case 'up-white':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].white;
            return newState;
        case 'up-black':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].black;
            return newState;
        case 'up-gold':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].gold;
            return newState;
        case 'up-red':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].red;
            return newState;
        case 'game-started':
            newState = Object.assign({}, state);
            newState[action.payload.thread].game = new Chess();
            newState[action.payload.thread].fen = action.payload.fen;
            return newState;
        case 'four-game-started':
            newState = Object.assign({}, state);
            newState[action.payload.thread].game = new FourChess();
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread].turn = newState[action.payload.thread].game.turn();
            return newState;
        case 'pause':
            newState = Object.assign({}, state);
            newState[action.payload.thread].paused = true;
            return newState;
        case 'resume':
            newState = Object.assign({}, state);
            newState[action.payload.thread].paused = false;
            return newState;
        case 'draw-request':
            return state;
        case LOGOUT_SUCCESS:
            return {};
        default:
            return state;
    }
}

function connection(state = {status: false, error: false}, action) {
    switch(action.type) {
        case 'duplicate-login':
            return {status: false, error: true};
        case 'disconnect':
            return {status: false};
        case 'connected':
            return {status: true};
        default:
            return state;
    }
}

function activeGame(state=false, action) {
    switch(action.type) {
        default:
            return state;
    }
}

const rootReducer = combineReducers({
    connection,
    notifications,  //notification-center lib
    rooms,      //A list of all available Chat Rooms
    activeThread,
    openThreads,
    newGameOptions,
    auth: AuthReducer,
    form: formReducer,
});

export default rootReducer;
<commit_msg>Player card border toggling for 2-player<commit_after>import {combineReducers} from 'redux';
import {reducer as notifications} from 'react-notification-system-redux';
import { reducer as formReducer } from 'redux-form';
import Chess from 'chess.js';

import FourChess from '../../common/fourchess';
import rooms from './rooms_reducer';
import AuthReducer from './auth_reducer';
import newGameOptions from './new_game_reducer';

import {SELECTED_ROOM,LOGOUT_SUCCESS} from '../actions/types';

function activeThread (state = 'Global', action) {
    switch(action.type) {
        case SELECTED_ROOM:
            return action.payload;
        case LOGOUT_SUCCESS:
            return 'Global';
        default:
            return state;
    }
}

function openThreads(state = {}, action) {
    let obj = null, newState = null;
    let roomName = null;
    switch(action.type) {
        case 'user-room-joined':
            const users = action.payload.users;
            roomName = action.payload.room.name;
            obj = {...state[roomName], users};
            return {...state, [roomName]: obj};
        case 'user-room-left':
            roomName = action.payload.name;
            const user = action.payload.user;
            newState = Object.assign({}, state);
            newState[roomName].users = newState[roomName].users.filter((member) => {
                return user.user._id !== member._id;
            });
            return newState;
        case 'joined-room':
            if(action.payload.fen) {
                if(action.payload.gameType == 'four-player') {
                    action.payload.game = new FourChess();
                    action.payload.game.position(action.payload.fen);
                } else if(action.payload.gameType == 'two-player'){
                    action.payload.game = new Chess(action.payload.fen);
                }
            }
            return {...state, [action.payload.room.name]: action.payload };
        case 'left-room':
            newState = Object.assign({}, state);
            delete newState[action.payload];
            return newState;
        case 'receive-message':
            const messages = [...state[action.payload.thread].messages, action.payload];
            obj = {...state[action.payload.thread], messages};
            return {...state, [action.payload.thread]: obj };
        case 'new-move':
            newState = Object.assign({}, state);
            newState[action.payload.thread].game = new Chess(action.payload.fen);
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread].turn = newState[action.payload.thread].game.turn();
            newState[action.payload.thread][action.payload.lastTurn].time = action.payload.time;
            return newState;
        case 'four-new-move':
            newState = Object.assign({}, state);
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread].game = new FourChess();
            newState[action.payload.thread].game.position(action.payload.fen);
            newState[action.payload.thread].turn = newState[action.payload.thread].game.turn();
            //newState[action.payload.thread][action.payload.lastTurn].time = action.payload.time;
            return newState;
        case 'four-resign':
            newState = Object.assign({}, state);
            switch(action.payload.color) {
                case 'w':
                    newState[action.payload.thread].white.resigned = true;
                    break;
                case 'b':
                    newState[action.payload.thread].black.resigned = true;
                    break;
                case 'g':
                    newState[action.payload.thread].gold.resigned = true;
                    break;
                case 'r':
                    newState[action.payload.thread].red.resigned = true;
                    break;
            }
            return newState;
        case 'game-over':
            newState = Object.assign({}, state);
            delete newState[action.payload].game;
            delete newState[action.payload].fen;
            delete newState[action.payload].pgn;
            delete newState[action.payload].white;
            delete newState[action.payload].black;
            delete newState[action.payload].gold;
            delete newState[action.payload].red;
            return newState;
        case 'timer-sync':
            if(state[action.payload.thread][action.payload.turn]) {
                newState = Object.assign({}, state);
                newState[action.payload.thread][action.payload.turn].time = action.payload.timeLeft;
                return newState;
            }
            return state;
        case 'sit-down-white':
            newState = Object.assign({}, state);
            newState[action.payload.thread].white = action.payload.room;
            return newState;
        case 'sit-down-black':
            newState = Object.assign({}, state);
            newState[action.payload.thread].black = action.payload.room;
            return newState;
        case 'sit-down-gold':
            newState = Object.assign({}, state);
            newState[action.payload.thread].gold = action.payload.room;
            return newState;
        case 'sit-down-red':
            newState = Object.assign({}, state);
            newState[action.payload.thread].red = action.payload.room;
            return newState;
        case 'up-white':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].white;
            return newState;
        case 'up-black':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].black;
            return newState;
        case 'up-gold':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].gold;
            return newState;
        case 'up-red':
            newState = Object.assign({}, state);
            delete newState[action.payload.name].red;
            return newState;
        case 'game-started':
            newState = Object.assign({}, state);
            newState[action.payload.thread].game = new Chess();
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread].turn = newState[action.payload.thread].game.turn();
            return newState;
        case 'four-game-started':
            newState = Object.assign({}, state);
            newState[action.payload.thread].game = new FourChess();
            newState[action.payload.thread].fen = action.payload.fen;
            newState[action.payload.thread].turn = newState[action.payload.thread].game.turn();
            return newState;
        case 'pause':
            newState = Object.assign({}, state);
            newState[action.payload.thread].paused = true;
            return newState;
        case 'resume':
            newState = Object.assign({}, state);
            newState[action.payload.thread].paused = false;
            return newState;
        case 'draw-request':
            return state;
        case LOGOUT_SUCCESS:
            return {};
        default:
            return state;
    }
}

function connection(state = {status: false, error: false}, action) {
    switch(action.type) {
        case 'duplicate-login':
            return {status: false, error: true};
        case 'disconnect':
            return {status: false};
        case 'connected':
            return {status: true};
        default:
            return state;
    }
}

function activeGame(state=false, action) {
    switch(action.type) {
        default:
            return state;
    }
}

const rootReducer = combineReducers({
    connection,
    notifications,  //notification-center lib
    rooms,      //A list of all available Chat Rooms
    activeThread,
    openThreads,
    newGameOptions,
    auth: AuthReducer,
    form: formReducer,
});

export default rootReducer;
<|endoftext|>


<commit_before>import {List, Map} from 'immutable';

export function setEntries(state, entries) {
  return state.set('entries', List(entries));
}

function getWinners(vote) {
  if (!vote) return [];
  const [a, b] = vote.get('pair');
  const aVotes = vote.getIn(['tally', a], 0);
  const bVotes = vote.getIn(['tally', b], 0);
  if (aVotes > bVotes) return [a];
  if (aVotes < bVotes) return [b];
  return [a, b];
}

export function next(state) {
  const winners = getWinners(state.get('vote'));
  const entries = state.get('entries', List()).concat(winners);

  if(entries.size === 1) {
    return Map({winner: entries.first()});
  }

  return state.merge({
    entries: entries.skip(2),
    vote: Map({
      pair: entries.take(2)
    })
  });
}

export function vote(state, entry) {
  return state.updateIn(['vote', 'tally', entry],
                        0,
                        tally => tally + 1);
}
<commit_msg>Future proofing the function next by explicitly removing vote, and entries submaps from the state and ading the winner key.<commit_after>import {List, Map} from 'immutable';

export function setEntries(state, entries) {
  return state.set('entries', List(entries));
}

function getWinners(vote) {
  if (!vote) return [];
  const [a, b] = vote.get('pair');
  const aVotes = vote.getIn(['tally', a], 0);
  const bVotes = vote.getIn(['tally', b], 0);
  if (aVotes > bVotes) return [a];
  if (aVotes < bVotes) return [b];
  return [a, b];
}

export function next(state) {
  const winners = getWinners(state.get('vote'));
  const entries = state.get('entries', List()).concat(winners);

  if(entries.size === 1) {
    return state.remove('vote')
                .remove('entries')
                .set('winner', entries.first());
  }

  return state.merge({
    entries: entries.skip(2),
    vote: Map({
      pair: entries.take(2)
    })
  });
}

export function vote(state, entry) {
  return state.updateIn(['vote', 'tally', entry],
                        0,
                        tally => tally + 1);
}
<|endoftext|>