<commit_before>import React from 'react';
import _ from 'lodash';
import slugid from 'slugid';
import ReactDOM from 'react-dom';
import {MultiTrackContainer} from './MultiTrackContainer.jsx';
import {Responsive, WidthProvider} from 'react-grid-layout';
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

const ResponsiveReactGridLayout = WidthProvider(Responsive);

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

        this.viewConfig = this.props.viewConfig;

          this.state = {
            currentBreakpoint: 'lg',
            mounted: false
          }
    }


  componentDidMount() {
    this.setState({mounted: true});
  }

    componentWillReceiveProps(newProps) {
        //console.log('newProps:', newProps);
    }

    shouldComponentUpdate(nextProps, nextState) {
        /*
        console.log('oldProps.text:', this.props.viewConfig.text);
        console.log('newProps.text:', nextProps.viewConfig.text);
        */
        if (nextProps.viewConfig.text == this.props.viewConfig.text) {
            //console.log('not updating...');
            return false;
        }

        return true;
    }


  onBreakpointChange(breakpoint) {
    this.setState({
      currentBreakpoint: breakpoint
    });
  };

  handleLayoutChange(layout, layouts) {
    //this.props.onLayoutChange(layout, layouts);
  };

  onNewLayout() {

  };

  onResize(layout, oldItem, newItem, placeholder, e, element) {
      //console.log('layout:', layout, 'oldItem:', oldItem, 'newItem:', newItem, 'placeholder:', placeholder, 'e:', e, 'element:', element);

  }

  generateViewLayout(viewConfig) {
    let minTrackHeight = 30;
    let totalHeight = 0

    for (let i = 0; i < viewConfig.tracks.length; i++) {
        let track = viewConfig.tracks[i];

        if (!track.height)
            totalHeight += minTrackHeight;
        else
            totalHeight += track.height;
    }

    if (viewConfig.searchBox)
        totalHeight += 30;
    
    let heightGrid = Math.ceil(totalHeight / this.props.rowHeight);

    let layout = {
        x: 0,
        y: 0,
        w: 6,
    };

    layout.h = heightGrid;
    layout.minH = heightGrid;
    layout.maxH = heightGrid;

    if (layout in viewConfig)
        layout = viewConfig.layout;

    layout.i = slugid.nice();

    return layout;
  }

  handleCloseView(uid) {
      /**
       * A view needs to be closed. Remove it from from the viewConfig and then clean
       * up all of its connections (zoom links, workers, etc...)
       *
       * @param {uid} This view's identifier
       */
    
      console.log('closing a view:', uid);
      let viewConfigObject = JSON.parse(this.props.viewConfig.text);

      // check if this is the only view
      // if it is, don't close it (display an error message)
      if (viewConfigObject.views.length == 1) {
            console.log("Can't close the only view");
            return;
      }


      let viewsToClose = viewConfigObject.views.filter((d) => { return d.uid == uid; });

      console.log('views to close:', viewsToClose);
      // send an event to the app telling it that we're closing some views so 
      // that it can clean up after them

      let filteredViews = viewConfigObject.views.filter((d) => { 
          console.log('d:', d);
          return d.uid != uid;
      });
      console.log('filteredViews:', filteredViews);
      viewConfigObject.views = filteredViews;
      let newViewConfigText = JSON.stringify(viewConfigObject);

      this.props.onNewConfig(newViewConfigText);
  }

  handleAddView() {
      /**
       * User clicked on the "Add View" button. We'll duplicate the last
       * view.
       */
      console.log('addView clicked');
      console.log('this.viewConfig:', this.props.viewConfig);

      let freshViewConfig = JSON.parse(this.props.viewConfig.text);
      let views = freshViewConfig.views;
      let lastView = views[views.length-1];

      let maxY = 0;

      for (let i = 0; i < views.length; i++) {
          let view = views[i];

          // we don't want to serialize zoom dispatches
          if ('zoomDispatch' in view)
              delete view.zoomDispatch;

          if ('layout' in view) {
              if ('minH' in view.layout)
                    maxY += Math.max(maxY, view.layout.y + view.layout.minH);
              else
                    maxY += Math.max(maxY, view.layout.y + 1);
          }
      }


      let newView = JSON.parse(JSON.stringify(lastView));   //ghetto copy

      // place this new view below all the others
      newView.layout.x = 0;
      newView.layout.y = maxY;

      // give it its own unique id
      newView.uid = slugid.nice();

      freshViewConfig.views.push(newView); 
      let newViewConfigText = JSON.stringify(freshViewConfig);

      console.log('lastView:', lastView);
      console.log('newViewConfig:', newViewConfigText);

      this.props.onNewConfig(newViewConfigText);
  }

  render() {
    let imgStyle = { right: 5,
                    top: 2,
                     position: 'absolute' }
    return (
      <div>
        <ResponsiveReactGridLayout
          {...this.props}
          draggableHandle={'.multitrack-header'}<commit_msg>Don't crash when destroying views<commit_after>import React from 'react';
import _ from 'lodash';
import slugid from 'slugid';
import ReactDOM from 'react-dom';
import {MultiTrackContainer} from './MultiTrackContainer.jsx';
import {Responsive, WidthProvider} from 'react-grid-layout';
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

const ResponsiveReactGridLayout = WidthProvider(Responsive);

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

        this.viewConfig = this.props.viewConfig;

          this.state = {
            currentBreakpoint: 'lg',
            mounted: false
          }
    }


  componentDidMount() {
    this.setState({mounted: true});
  }

    componentWillReceiveProps(newProps) {
        //console.log('newProps:', newProps);
    }

    shouldComponentUpdate(nextProps, nextState) {
        /*
        console.log('oldProps.text:', this.props.viewConfig.text);
        console.log('newProps.text:', nextProps.viewConfig.text);
        */
        if (nextProps.viewConfig.text == this.props.viewConfig.text) {
            //console.log('not updating...');
            return false;
        }

        return true;
    }


  onBreakpointChange(breakpoint) {
    this.setState({
      currentBreakpoint: breakpoint
    });
  };

  handleLayoutChange(layout, layouts) {
    //this.props.onLayoutChange(layout, layouts);
  };

  onNewLayout() {

  };

  onResize(layout, oldItem, newItem, placeholder, e, element) {
      //console.log('layout:', layout, 'oldItem:', oldItem, 'newItem:', newItem, 'placeholder:', placeholder, 'e:', e, 'element:', element);

  }

  generateViewLayout(viewConfig) {
    let minTrackHeight = 30;
    let totalHeight = 0

    for (let i = 0; i < viewConfig.tracks.length; i++) {
        let track = viewConfig.tracks[i];

        if (!track.height)
            totalHeight += minTrackHeight;
        else
            totalHeight += track.height;
    }

    if (viewConfig.searchBox)
        totalHeight += 30;
    
    let heightGrid = Math.ceil(totalHeight / this.props.rowHeight);

    let layout = {
        x: 0,
        y: 0,
        w: 6,
    };

    layout.h = heightGrid;
    layout.minH = heightGrid;
    layout.maxH = heightGrid;

    if (layout in viewConfig)
        layout = viewConfig.layout;

    layout.i = slugid.nice();

    return layout;
  }

  handleCloseView(uid) {
      /**
       * A view needs to be closed. Remove it from from the viewConfig and then clean
       * up all of its connections (zoom links, workers, etc...)
       *
       * @param {uid} This view's identifier
       */
    
      console.log('closing a view:', uid);
      let viewConfigObject = JSON.parse(this.props.viewConfig.text);

      // check if this is the only view
      // if it is, don't close it (display an error message)
      if (viewConfigObject.views.length == 1) {
            console.log("Can't close the only view");
            return;
      }


      let viewsToClose = viewConfigObject.views.filter((d) => { return d.uid == uid; });

      console.log('views to close:', viewsToClose);
      // send an event to the app telling it that we're closing some views so 
      // that it can clean up after them

      let filteredViews = viewConfigObject.views.filter((d) => { 
          console.log('d:', d);
          return d.uid != uid;
      });

      this.removeZoomDispatch(filteredViews);

      console.log('filteredViews:', filteredViews);
      viewConfigObject.views = filteredViews;
      let newViewConfigText = JSON.stringify(viewConfigObject);
      console.log('newViewConfigText:', newViewConfigText);

      this.props.onNewConfig(newViewConfigText);
  }

  removeZoomDispatch(views) {
      /**
       * Remove all zoom dispatches from views so that
       * we don't have issues when recreating them.
       *
       * @param {views} An array of views
       * @return views The same set of views, with any zoomDispatch members excised
       */
      for (let i = 0; i < views.length; i++) {
          let view = views[i];

        if ('zoomDispatch' in view)
            delete view.zoomDispatch;
      }

      return views;
  }

  handleAddView() {
      /**
       * User clicked on the "Add View" button. We'll duplicate the last
       * view.
       */
      console.log('addView clicked');
      console.log('this.viewConfig:', this.props.viewConfig);

      let freshViewConfig = JSON.parse(this.props.viewConfig.text);
      let views = freshViewConfig.views;
      let lastView = views[views.length-1];

      let maxY = 0;

      for (let i = 0; i < views.length; i++) {
          let view = views[i];

          if ('layout' in view) {
              if ('minH' in view.layout)
                    maxY += Math.max(maxY, view.layout.y + view.layout.minH);
              else
                    maxY += Math.max(maxY, view.layout.y + 1);
          }
      }

      this.removeZoomDispatch(views);

      let newView = JSON.parse(JSON.stringify(lastView));   //ghetto copy

      // place this new view below all the others
      newView.layout.x = 0;
      newView.layout.y = maxY;

      // give it its own unique id
      newView.uid = slugid.nice();

      freshViewConfig.views.push(newView); 
      let newViewConfigText = JSON.stringify(freshViewConfig);

      console.log('lastView:', lastView);
      console.log('newViewConfig:', newViewConfigText);

      this.props.onNewConfig(newViewConfigText);
  }

  render() {
    let imgStyle = { right: 5,
                    top: 2,
                     position: 'absolute' }
    return (
      <div>
        <ResponsiveReactGridLayout
          {...this.props}
          draggableHandle={'.multitrack-header'}
{'fraction_non_alphanumeric': 0.12207297150117989, 'fraction_numerical': 0.0031766200762388818, 'mean_word_length': 2.2370740305522916, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4312934', 'n_tokens_mistral': 3357, 'n_tokens_neox': 3178, 'n_words': 798}