import { LoadingSpinner } from '@sourcegraph/react-loading-spinner';
import AlertCircleIcon from 'mdi-react/AlertCircleIcon';
import * as React from 'react';
import { Redirect } from 'react-router';
import { combineLatest, Subject, Subscription, throwError } from 'rxjs';
import { catchError, delay, distinctUntilChanged, map, repeatWhen, startWith, switchMap, tap } from 'rxjs/operators';
import { addCommentToThread, fetchDiscussionThreadAndComments, updateComment } from '../../../discussions/backend';
import { DiscussionsComment } from '../../../discussions/DiscussionsComment';
import { eventLogger } from '../../../tracking/eventLogger';
import { asError } from '../../../util/errors';
import { formatHash } from '../../../util/url';
import { DiscussionsInput, TitleMode } from './DiscussionsInput';
import { DiscussionsNavbar } from './DiscussionsNavbar';
export class DiscussionsThread extends React.PureComponent {
    constructor(props) {
        super(props);
        this.componentUpdates = new Subject();
        this.subscriptions = new Subscription();
        this.onSubmit = (title, contents) => {
            eventLogger.log('RepliedToDiscussion');
            return addCommentToThread(this.props.threadID, contents).pipe(tap(thread => this.setState({ thread })), map(thread => undefined), catchError(e => throwError('Error creating comment: ' + asError(e).message)));
        };
        this.onCommentReport = (comment, reason) => updateComment({ commentID: comment.id, report: reason }).pipe(tap(thread => this.setState({ thread })), map(thread => undefined));
        this.onCommentClearReports = (comment) => updateComment({ commentID: comment.id, clearReports: true }).pipe(tap(thread => this.setState({ thread })), map(thread => undefined));
        this.onCommentDelete = (comment) => updateComment({ commentID: comment.id, delete: true }).pipe(tap(thread => this.setState({ thread })), map(thread => undefined));
        this.state = {
            loading: true,
        };
    }
    componentDidMount() {
        eventLogger.logViewEvent('DiscussionsThread');
        // TODO(slimsag:discussions): ASAP: changing threadID manually in URL does not work. Can't click links to threads/comments effectively.
        this.subscriptions.add(combineLatest(this.componentUpdates.pipe(startWith(this.props)))
            .pipe(distinctUntilChanged(([a], [b]) => a.threadID === b.threadID), switchMap(([props]) => fetchDiscussionThreadAndComments(props.threadID).pipe(map(thread => ({ thread, error: undefined, loading: false })), catchError(error => {
            console.error(error);
            return [{ error, loading: false }];
        }), repeatWhen(delay(2500)))))
            .subscribe(stateUpdate => this.setState(state => (Object.assign({}, state, stateUpdate))), err => console.error(err)));
        this.componentUpdates.next(this.props);
    }
    componentDidUpdate() {
        this.componentUpdates.next(this.props);
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    render() {
        // TODO(slimsag:discussions): future: test error state + cleanup CSS
        const { error, loading, thread } = this.state;
        const { location, commentID, user } = this.props;
        // If the thread is loaded, ensure that the URL hash is updated to
        // reflect the line that the discussion was created on.
        if (thread) {
            const desiredHash = this.urlHashWithLine(thread, commentID);
            if (desiredHash !== location.hash) {
                const discussionURL = location.pathname + location.search + desiredHash;
                return React.createElement(Redirect, { to: discussionURL });
            }
        }
        return (React.createElement("div", { className: "discussions-thread" },
            React.createElement(DiscussionsNavbar, Object.assign({}, this.props, { threadTitle: thread ? thread.title : undefined })),
            loading && React.createElement(LoadingSpinner, { className: "icon-inline" }),
            error && (React.createElement("div", { className: "discussions-thread__error alert alert-danger" },
                React.createElement(AlertCircleIcon, { className: "icon-inline discussions-thread__error-icon" }),
                "Error loading thread: ",
                error.message)),
            thread && (React.createElement("div", { className: "discussions-thread__comments" },
                thread.comments.nodes.map(node => (React.createElement(DiscussionsComment, Object.assign({ key: node.id }, this.props, { comment: node, isSiteAdmin: !!user && user.siteAdmin, onReport: this.onCommentReport, onClearReports: this.onCommentClearReports, onDelete: this.onCommentDelete })))),
                React.createElement(DiscussionsInput, Object.assign({ key: "input", submitLabel: "Comment", titleMode: TitleMode.None, onSubmit: this.onSubmit }, this.props))))));
    }
    /**
     * Produces a URL hash for linking to the given discussion thread and the
     * line that it was created on.
     * @param thread The thread to link to.
     */
    urlHashWithLine(thread, commentID) {
        const hash = new URLSearchParams();
        hash.set('tab', 'discussions');
        hash.set('threadID', thread.id);
        if (commentID) {
            hash.set('commentID', commentID);
        }
        return thread.target.__typename === 'DiscussionThreadTargetRepo' && thread.target.selection !== null
            ? formatHash({
                line: thread.target.selection.startLine,
                character: thread.target.selection.startCharacter,
                endLine: thread.target.selection.endLine,
                endCharacter: thread.target.selection.endCharacter,
            }, hash)
            : '#' + hash.toString();
    }
}
//# sourceMappingURL=DiscussionsThread.js.map