import { LoadingSpinner } from '@sourcegraph/react-loading-spinner';
import * as React from 'react';
import { merge, of, Subject, Subscription } from 'rxjs';
import { catchError, debounceTime, delay, distinctUntilChanged, filter, mergeMap, share, switchMap, takeUntil, } from 'rxjs/operators';
import { fetchTreeEntries } from '../repo/backend';
import { asError, isErrorLike } from '../util/errors';
import { ChildTreeLayer } from './ChildTreeLayer';
import { treePadding } from './util';
import { hasSingleChild, singleChildEntriesToGitTree } from './util';
const maxEntries = 2500;
const LOADING = 'loading';
export class TreeRoot extends React.Component {
    constructor(props) {
        super(props);
        this.subscriptions = new Subscription();
        this.componentUpdates = new Subject();
        this.rowHovers = new Subject();
        /**
         * Prefetches the children of hovered tree rows. Gets passed from the root tree layer to child tree layers
         * through the onHover prop. This method only gets called on the root tree layer component so we can debounce
         * the hover prefetch requests.
         */
        this.fetchChildContents = (path) => {
            this.rowHovers.next(path);
        };
        this.setChildNode = (node, index) => {
            this.node.childNodes[index] = node;
        };
        this.node = {
            index: this.props.index,
            parent: this.props.parent,
            childNodes: [],
            path: '',
            url: '',
        };
        this.state = {};
    }
    componentDidMount() {
        // Set this row as a childNode of its TreeLayer parent
        this.props.setChildNodes(this.node, this.node.index);
        this.subscriptions.add(this.componentUpdates
            .pipe(distinctUntilChanged((x, y) => x.repoPath === y.repoPath &&
            x.rev === y.rev &&
            x.commitID === y.commitID &&
            x.parentPath === y.parentPath &&
            x.isExpanded === y.isExpanded &&
            x.location === y.location), filter(props => props.isExpanded), switchMap(props => {
            const treeFetch = fetchTreeEntries({
                repoPath: props.repoPath,
                rev: props.rev,
                commitID: props.commitID,
                filePath: props.parentPath || '',
                first: maxEntries,
            }).pipe(catchError(err => [asError(err)]), share());
            return merge(treeFetch, of(LOADING).pipe(delay(300), takeUntil(treeFetch)));
        }))
            .subscribe(treeOrError => this.setState({ treeOrError }), err => console.error(err)));
        // This handles pre-fetching when a user
        // hovers over a directory. The `subscribe` is empty because
        // we simply want to cache the network request.
        this.subscriptions.add(this.rowHovers
            .pipe(debounceTime(100), mergeMap(path => fetchTreeEntries({
            repoPath: this.props.repoPath,
            rev: this.props.rev,
            commitID: this.props.commitID,
            filePath: path,
            first: maxEntries,
        }).pipe(catchError(err => [asError(err)]))))
            .subscribe());
        // When we're at the root tree layer, fetch the tree contents on mount.
        this.componentUpdates.next(this.props);
    }
    componentDidUpdate(prevProps) {
        this.componentUpdates.next(this.props);
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    render() {
        const { treeOrError } = this.state;
        let singleChildTreeEntry = {};
        if (treeOrError &&
            treeOrError !== LOADING &&
            !isErrorLike(treeOrError) &&
            hasSingleChild(treeOrError.entries)) {
            singleChildTreeEntry = singleChildEntriesToGitTree(treeOrError.entries);
        }
        return (React.createElement("table", { className: "tree-layer", tabIndex: 0 },
            React.createElement("tbody", null,
                React.createElement("tr", null,
                    React.createElement("td", { className: "tree__cell" }, treeOrError === LOADING ? (React.createElement("div", { className: "tree__row-loader" },
                        React.createElement(LoadingSpinner, { className: "icon-inline tree-page__entries-loader" }),
                        "Loading tree")) : isErrorLike(treeOrError) ? (React.createElement("div", { className: "tree__row tree__row-alert alert alert-danger", 
                        // tslint:disable-next-line:jsx-ban-props (needed because of dynamic styling)
                        style: treePadding(this.props.depth, true) },
                        "Error loading tree: ",
                        treeOrError.message)) : (treeOrError && (React.createElement(ChildTreeLayer, Object.assign({}, this.props, { parent: this.node, depth: -1, entries: treeOrError.entries, singleChildTreeEntry: singleChildTreeEntry, childrenEntries: singleChildTreeEntry.children, onHover: this.fetchChildContents, setChildNodes: this.setChildNode })))))))));
    }
}
//# sourceMappingURL=TreeRoot.js.map