import React, { Component } from "react"; import PropTypes from "prop-types"; import { observable, action } from "mobx"; import { observer } from "mobx-react"; import moment from "moment"; import MasonryInfiniteScroller from "react-masonry-infinite"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch"; import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { SilenceFormStore } from "Stores/SilenceFormStore"; import { AlertGroup } from "./AlertGroup"; import { GridSizesConfig } from "./Constants"; import "./index.css"; const AlertGrid = observer( class AlertGrid extends Component { static propTypes = { alertStore: PropTypes.instanceOf(AlertStore).isRequired, settingsStore: PropTypes.instanceOf(Settings).isRequired, silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired }; // store reference to generated masonry component so we can call it // to repack the grid after any component was re-rendered, which could // alter its size breaking grid layout masonryComponentReference = observable( { ref: false }, {}, { name: "Masonry reference" } ); // store it for later storeMasonryRef = action(ref => { this.masonryComponentReference.ref = ref; }); // used to call forcePack() which will repack all grid elements // (alert groups), this needs to be called if any group size changes masonryRepack = action(() => { if (this.masonryComponentReference.ref !== false) { this.masonryComponentReference.ref.forcePack(); } }); // how many alert groups to render // FIXME reset on filter change initial = 50; groupsToRender = observable( { value: this.initial }, {}, { name: "Groups to render" } ); // how many groups add to render count when user scrolls to the bottom loadMoreStep = 30; // loadMore = action(() => { const { alertStore } = this.props; this.groupsToRender.value = Math.min( this.groupsToRender.value + this.loadMoreStep, Object.keys(alertStore.data.groups).length ); }); compare = (a, b) => { const { alertStore, settingsStore } = this.props; // don't sort if sorting is disabled if ( settingsStore.gridConfig.config.sortOrder === settingsStore.gridConfig.options.disabled.value ) return 0; const getLabelValue = g => { // if timestamp sort is enabled use latest alert for sorting if ( settingsStore.gridConfig.config.sortOrder === settingsStore.gridConfig.options.startsAt.value ) { return moment.max(g.alerts.map(a => moment(a.startsAt))); } const labelName = settingsStore.gridConfig.config.sortLabel; const labelValue = g.labels[labelName] || g.shared.labels[labelName] || g.alerts[0].labels[labelName]; let mappedValue; // check if we have a mapping for label value if ( labelValue !== undefined && alertStore.settings.values.sorting.valueMapping[labelName] !== undefined ) { mappedValue = alertStore.settings.values.sorting.valueMapping[labelName][ labelValue ]; } // if we have a mapped value then return it, if not return original value return mappedValue !== undefined ? mappedValue : labelValue; }; const val = settingsStore.gridConfig.config.reverseSort ? -1 : 1; const av = getLabelValue(a); const bv = getLabelValue(b); if (av === undefined && av === undefined) { // if both alerts lack the label they are equal return 0; } else if (av === undefined || av > bv) { // if first one lacks it it's should be rendered after alerts with that label return val; } else if (bv === undefined || av < bv) { // if the first one has label but the second doesn't then the second should be rendered after the first return val * -1; } else { return 0; } }; componentDidUpdate() { // whenever grid component re-renders we need to ensure that grid elements // are packed correctly this.masonryRepack(); } render() { const { alertStore, settingsStore, silenceFormStore } = this.props; return ( } > {Object.values(alertStore.data.groups) .sort(this.compare) .slice(0, this.groupsToRender.value) .map(group => ( 1 } afterUpdate={this.masonryRepack} settingsStore={settingsStore} silenceFormStore={silenceFormStore} /> ))} ); } } ); export { AlertGrid };