import { ActionItem } from '@sourcegraph/extensions-client-common/lib/app/actions/ActionItem';
import { ActionsContainer } from '@sourcegraph/extensions-client-common/lib/app/actions/ActionsContainer';
import { LoadingSpinner } from '@sourcegraph/react-loading-spinner';
import { escapeRegExp, upperFirst } from 'lodash';
import FolderIcon from 'mdi-react/FolderIcon';
import HistoryIcon from 'mdi-react/HistoryIcon';
import SourceBranchIcon from 'mdi-react/SourceBranchIcon';
import SourceCommitIcon from 'mdi-react/SourceCommitIcon';
import TagIcon from 'mdi-react/TagIcon';
import UserIcon from 'mdi-react/UserIcon';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Subject, Subscription } from 'rxjs';
import { catchError, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators';
import { ContributableMenu } from 'sourcegraph/module/protocol';
import { gql, queryGraphQL } from '../backend/graphql';
import { FilteredConnection } from '../components/FilteredConnection';
import { Form } from '../components/Form';
import { PageTitle } from '../components/PageTitle';
import { displayRepoPath } from '../components/RepoFileLink';
import { isDiscussionsEnabled } from '../discussions';
import { DiscussionsList } from '../discussions/DiscussionsList';
import { OpenHelpPopoverButton } from '../global/OpenHelpPopoverButton';
import { searchQueryForRepoRev } from '../search';
import { submitSearch } from '../search/helpers';
import { QueryInput } from '../search/input/QueryInput';
import { SearchButton } from '../search/input/SearchButton';
import { eventLogger } from '../tracking/eventLogger';
import { asError, createAggregateError, isErrorLike } from '../util/errors';
import { RepositoryIcon } from '../util/icons'; // TODO: Switch to mdi icon
import { memoizeObservable } from '../util/memoize';
import { basename } from '../util/path';
import { fetchTree } from './backend';
import { GitCommitNode } from './commits/GitCommitNode';
import { gitCommitFragment } from './commits/RepositoryCommitsPage';
const TreeEntry = ({ isDir, name, parentPath, url }) => {
    const filePath = parentPath ? parentPath + '/' + name : name;
    return (React.createElement(Link, { to: url, className: "tree-entry", title: filePath },
        name,
        isDir && '/'));
};
const fetchTreeCommits = memoizeObservable((args) => queryGraphQL(gql `
                query TreeCommits($repo: ID!, $revspec: String!, $first: Int, $filePath: String) {
                    node(id: $repo) {
                        ... on Repository {
                            commit(rev: $revspec) {
                                ancestors(first: $first, path: $filePath) {
                                    nodes {
                                        ...GitCommitFields
                                    }
                                    pageInfo {
                                        hasNextPage
                                    }
                                }
                            }
                        }
                    }
                }
                ${gitCommitFragment}
            `, args).pipe(map(({ data, errors }) => {
    if (!data || !data.node) {
        throw createAggregateError(errors);
    }
    const repo = data.node;
    if (!repo.commit || !repo.commit.ancestors || !repo.commit.ancestors.nodes) {
        throw createAggregateError(errors);
    }
    return repo.commit.ancestors;
})), args => `${args.repo}:${args.revspec}:${args.first}:${args.filePath}`);
export class TreePage extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = { query: '' };
        this.componentUpdates = new Subject();
        this.subscriptions = new Subscription();
        this.onQueryChange = (query) => this.setState({ query });
        this.onSubmit = (event) => {
            event.preventDefault();
            submitSearch(this.props.history, { query: this.getQueryPrefix() + this.state.query }, this.props.filePath ? 'tree' : 'repo');
        };
        this.queryCommits = (args) => fetchTreeCommits(Object.assign({}, args, { repo: this.props.repoID, revspec: this.props.rev || '', filePath: this.props.filePath }));
    }
    logViewEvent(props) {
        if (props.filePath === '') {
            eventLogger.logViewEvent('Repository');
        }
        else {
            eventLogger.logViewEvent('Tree');
        }
    }
    componentDidMount() {
        this.subscriptions.add(this.componentUpdates
            .pipe(distinctUntilChanged((x, y) => x.repoPath === y.repoPath &&
            x.rev === y.rev &&
            x.commitID === y.commitID &&
            x.filePath === y.filePath), tap(props => this.logViewEvent(props)), switchMap(props => fetchTree({
            repoPath: props.repoPath,
            commitID: props.commitID,
            rev: props.rev,
            filePath: props.filePath,
            first: 2500,
        }).pipe(catchError(err => [asError(err)]), map(c => ({ treeOrError: c })), startWith({ treeOrError: undefined }))))
            .subscribe(stateUpdate => this.setState(stateUpdate), err => console.error(err)));
        this.componentUpdates.next(this.props);
    }
    componentWillReceiveProps(newProps) {
        this.componentUpdates.next(newProps);
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    getQueryPrefix() {
        let queryPrefix = searchQueryForRepoRev(this.props.repoPath, this.props.rev);
        if (this.props.filePath) {
            queryPrefix += `file:^${escapeRegExp(this.props.filePath)}/ `;
        }
        return queryPrefix;
    }
    render() {
        return (React.createElement("div", { className: "tree-page" },
            React.createElement(PageTitle, { title: this.getPageTitle() }),
            this.state.treeOrError === undefined && (React.createElement("div", null,
                React.createElement(LoadingSpinner, { className: "icon-inline tree-page__entries-loader" }),
                " Loading files and directories")),
            this.state.treeOrError !== undefined &&
                (isErrorLike(this.state.treeOrError) ? (React.createElement("div", { className: "alert alert-danger" }, upperFirst(this.state.treeOrError.message))) : (React.createElement(React.Fragment, null,
                    this.state.treeOrError.isRoot ? (React.createElement("header", null,
                        React.createElement("h2", { className: "tree-page__title" },
                            React.createElement(RepositoryIcon, { className: "icon-inline" }),
                            ' ',
                            displayRepoPath(this.props.repoPath)),
                        this.props.repoDescription && React.createElement("p", null, this.props.repoDescription),
                        React.createElement("div", { className: "btn-group mb-3" },
                            React.createElement(Link, { className: "btn btn-secondary", to: `${this.state.treeOrError.url}/-/commits` },
                                React.createElement(SourceCommitIcon, { className: "icon-inline" }),
                                " Commits"),
                            React.createElement(Link, { className: "btn btn-secondary", to: `/${this.props.repoPath}/-/branches` },
                                React.createElement(SourceBranchIcon, { className: "icon-inline" }),
                                " Branches"),
                            React.createElement(Link, { className: "btn btn-secondary", to: `/${this.props.repoPath}/-/tags` },
                                React.createElement(TagIcon, { className: "icon-inline" }),
                                " Tags"),
                            React.createElement(Link, { className: "btn btn-secondary", to: this.props.rev
                                    ? `/${this.props.repoPath}/-/compare/...${encodeURIComponent(this.props.rev)}`
                                    : `/${this.props.repoPath}/-/compare` },
                                React.createElement(HistoryIcon, { className: "icon-inline" }),
                                " Compare"),
                            React.createElement(Link, { className: `btn btn-secondary`, to: `/${this.props.repoPath}/-/stats/contributors` },
                                React.createElement(UserIcon, { className: "icon-inline" }),
                                " Contributors")))) : (React.createElement("header", null,
                        React.createElement("h2", { className: "tree-page__title" },
                            React.createElement(FolderIcon, { className: "icon-inline" }),
                            " ",
                            this.props.filePath))),
                    React.createElement("section", { className: "tree-page__section" },
                        React.createElement("h3", { className: "tree-page__section-header" },
                            "Search in this ",
                            this.props.filePath ? 'tree' : 'repository'),
                        React.createElement(Form, { className: "tree-page__section-search", onSubmit: this.onSubmit },
                            React.createElement(QueryInput, { value: this.state.query, onChange: this.onQueryChange, prependQueryForSuggestions: this.getQueryPrefix(), autoFocus: true, location: this.props.location, history: this.props.history, placeholder: "" }),
                            React.createElement(SearchButton, null),
                            React.createElement(OpenHelpPopoverButton, { onHelpPopoverToggle: this.props.onHelpPopoverToggle }))),
                    !isDiscussionsEnabled(this.props.configurationCascade) &&
                        this.state.treeOrError.directories.length > 0 && (React.createElement("section", { className: "tree-page__section" },
                        React.createElement("h3", { className: "tree-page__section-header" }, "Directories"),
                        React.createElement("div", { className: "tree-page__entries tree-page__entries-directories" }, this.state.treeOrError.directories.map((e, i) => (React.createElement(TreeEntry, { key: i, isDir: true, name: e.name, parentPath: this.props.filePath, url: e.url })))))),
                    !isDiscussionsEnabled(this.props.configurationCascade) &&
                        this.state.treeOrError.files.length > 0 && (React.createElement("section", { className: "tree-page__section" },
                        React.createElement("h3", { className: "tree-page__section-header" }, "Files"),
                        React.createElement("div", { className: "tree-page__entries tree-page__entries-files" }, this.state.treeOrError.files.map((e, i) => (React.createElement(TreeEntry, { key: i, isDir: false, name: e.name, parentPath: this.props.filePath, url: e.url })))))),
                    isDiscussionsEnabled(this.props.configurationCascade) && (React.createElement("div", { className: "tree-page__section mt-2 tree-page__section--discussions" },
                        React.createElement("h3", { className: "tree-page__section-header" }, "Discussions"),
                        React.createElement(DiscussionsList, { repoID: this.props.repoID, rev: this.props.rev, filePath: this.props.filePath + '/**' || undefined, history: this.props.history, location: this.props.location, noun: "discussion in this tree", pluralNoun: "discussions in this tree", defaultFirst: 2, hideSearch: true }))),
                    React.createElement(ActionsContainer, { menu: ContributableMenu.DirectoryPage, 
                        // tslint:disable-next-line:jsx-no-lambda
                        render: items => (React.createElement("section", { className: "tree-page__section" },
                            React.createElement("h3", { className: "tree-page__section-header" }, "Actions"),
                            items.map((item, i) => (React.createElement(ActionItem, Object.assign({ key: i }, item, { className: "btn btn-secondary mr-1 mb-1", extensionsController: this.props.extensionsController, extensions: this.props.extensions })))))), empty: null, extensionsController: this.props.extensionsController, extensions: this.props.extensions }),
                    React.createElement("div", { className: "tree-page__section" },
                        React.createElement("h3", { className: "tree-page__section-header" }, "Changes"),
                        React.createElement(FilteredConnection, { className: "mt-2 tree-page__section--commits", listClassName: "list-group list-group-flush", noun: "commit in this tree", pluralNoun: "commits in this tree", queryConnection: this.queryCommits, nodeComponent: GitCommitNode, nodeComponentProps: {
                                repoName: this.props.repoPath,
                                className: 'list-group-item',
                                compact: true,
                            }, updateOnChange: `${this.props.repoPath}:${this.props.rev}:${this.props.filePath}`, defaultFirst: 7, history: this.props.history, shouldUpdateURLQuery: false, hideSearch: true, location: this.props.location })))))));
    }
    getPageTitle() {
        const repoStr = displayRepoPath(this.props.repoPath);
        if (this.props.filePath) {
            return `${basename(this.props.filePath)} - ${repoStr}`;
        }
        return `${repoStr}`;
    }
}
//# sourceMappingURL=TreePage.js.map