github.com/stampzilla/stampzilla-go@v2.0.0-rc9+incompatible/nodes/stampzilla-server/web/src/routes/automation/components/Scene.js (about)

     1  import React from 'react';
     2  import { connect } from 'react-redux';
     3  import diff from 'immutable-diff';
     4  import classnames from 'classnames';
     5  
     6  import './Scene.scss';
     7  import Trait from '../../dashboard/Trait';
     8  import { traitPriority, traitNames, traitStates } from '../../dashboard/Device';
     9  
    10  // List of traits that is used to filter out useable devices
    11  const traits = ['OnOff', 'Brightness', 'ColorSetting'];
    12  
    13  const checkBox = ({
    14    device, trait, checked, onChange,
    15  }) => (
    16    <div className="checkbox custom-control custom-checkbox ">
    17      <input
    18        type="checkbox"
    19        className="custom-control-input"
    20        id={`${device.get('id')}${trait}`}
    21        checked={checked || false}
    22        onChange={event => onChange(event.target.checked)}
    23      />
    24      <label
    25        className="custom-control-label"
    26        htmlFor={`${device.get('id')}${trait}`}
    27      />
    28    </div>
    29  );
    30  
    31  class Scene extends React.Component {
    32    constructor(props) {
    33      super(props);
    34  
    35      this.state = {
    36        recording: false,
    37      };
    38    }
    39  
    40    componentWillReceiveProps(props) {
    41      const { devices } = this.props;
    42  
    43      const { recording } = this.state;
    44      const states = (this.props && this.props.states) || {};
    45  
    46      const useableDevices = devices.reduce((acc, dev) => {
    47        if (
    48          traits.find(trait => (dev.get('traits') || []).indexOf(trait) !== -1)
    49        ) {
    50          acc.push(dev.get('id'));
    51        }
    52        return acc;
    53      }, []);
    54  
    55      if (recording) {
    56        const changes = diff(props.devices, this.props.devices);
    57  
    58        changes.forEach((change) => {
    59          const [device, type, key] = change.get('path').toJS();
    60          if (type !== 'state') {
    61            return; // No state change
    62          }
    63          if (useableDevices.indexOf(device) === -1) {
    64            return; // Not a useable device
    65          }
    66  
    67          states[device] = {
    68            ...states[device],
    69            [key]: props.devices.getIn(change.get('path')),
    70          };
    71        });
    72  
    73        if (states !== this.props.states) {
    74          this.onStateChange(states);
    75        }
    76      }
    77    }
    78  
    79    onStateChange = (states) => {
    80      const { onChange } = this.props;
    81      if (onChange) {
    82        onChange(states);
    83      }
    84    };
    85  
    86    onToggleDevice = device => (checked) => {
    87      const states = (this.props && this.props.states) || {};
    88      if (checked) {
    89        states[device.get('id')] = states[device.get('id')] || {};
    90      } else {
    91        delete states[device.get('id')];
    92      }
    93      this.onStateChange(states);
    94    };
    95    onToggleTrait = (device, trait) => (checked) => {
    96      const states = (this.props && this.props.states) || {};
    97      if (checked) {
    98        states[device.get('id')] = states[device.get('id')] || {};
    99        states[device.get('id')][traitStates[trait]] = device.getIn([
   100          'state',
   101          traitStates[trait],
   102        ]);
   103      } else {
   104        delete states[device.get('id')][traitStates[trait]];
   105      }
   106      this.onStateChange(states);
   107    };
   108  
   109    onChangeTrait = (device, trait) => (value) => {
   110      const states = (this.props && this.props.states) || {};
   111  
   112      states[device.get('id')] = states[device.get('id')] || {};
   113      states[device.get('id')][traitStates[trait]] = value;
   114  
   115      this.onStateChange(states);
   116    };
   117  
   118    render() {
   119      const { devices, states } = this.props;
   120      const { recording } = this.state;
   121  
   122      const useableDevices = devices
   123        .reduce((acc, dev) => {
   124          if (
   125            traits.find(trait => (dev.get('traits') || []).indexOf(trait) !== -1)
   126          ) {
   127            acc.push(dev);
   128          }
   129          return acc;
   130        }, [])
   131        .sort((a, b) => a.get('name').localeCompare(b.get('name')));
   132  
   133      return (
   134        <div className="saved-state-builder">
   135          <div className="menu mb-1">
   136            <button
   137              className={recording ? 'btn btn-danger' : 'btn btn-secondary'}
   138              onClick={() => this.setState({ recording: !recording })}
   139              type="button"
   140            >
   141              {recording ? 'Recording' : 'Record'}
   142            </button>
   143            <button className="btn btn-secondary ml-1 hide" type="button">
   144              Select all lights
   145            </button>
   146          </div>
   147          <div className="devices">
   148            {useableDevices.map((dev) => {
   149              const sortedTraits =
   150                dev.get('traits') &&
   151                dev.get('traits').sort((a, b) => {
   152                  const prioA = traitPriority.findIndex(trait => trait === a);
   153                  const prioB = traitPriority.findIndex(trait => trait === b);
   154                  return prioA - prioB;
   155                });
   156              const selected =
   157                states && Object.keys(states).indexOf(dev.get('id')) !== -1;
   158  
   159              return (
   160                <div
   161                  key={dev.get('id')}
   162                  className={classnames({
   163                    selected,
   164                  })}
   165                >
   166                  <div className="d-flex mt-2">
   167                    {checkBox({
   168                      device: dev,
   169                      checked: selected || false,
   170                      onChange: this.onToggleDevice(dev),
   171                    })}
   172                    {dev.get('name')}
   173                  </div>
   174  
   175                  {sortedTraits &&
   176                    sortedTraits.map(trait => (
   177                      <div className="d-flex ml-3 trait" key={trait}>
   178                        {checkBox({
   179                          device: dev,
   180                          trait: traitStates[trait],
   181                          checked:
   182                            states &&
   183                            states[dev.get('id')] &&
   184                            states[dev.get('id')][traitStates[trait]] !==
   185                              undefined,
   186                          onChange: this.onToggleTrait(dev, trait),
   187                        })}
   188                        <div className="mr-2">{traitNames[trait] || trait}</div>
   189                        <div className="flex-grow-1 d-flex align-items-center justify-content-end">
   190                          <Trait
   191                            trait={trait}
   192                            device={dev}
   193                            state={
   194                              states &&
   195                              states[dev.get('id')] &&
   196                              states[dev.get('id')][traitStates[trait]] !==
   197                                undefined
   198                                ? states[dev.get('id')][traitStates[trait]]
   199                                : traitStates[trait] &&
   200                                  dev.getIn(['state', traitStates[trait]])
   201                            }
   202                            onChange={this.onChangeTrait(dev, trait)}
   203                          />
   204                        </div>
   205                      </div>
   206                    ))}
   207                </div>
   208              );
   209            })}
   210          </div>
   211        </div>
   212      );
   213    }
   214  }
   215  
   216  const mapStateToProps = state => ({
   217    devices: state.getIn(['devices', 'list']),
   218  });
   219  
   220  export default connect(mapStateToProps)(Scene);