import * as tslib_1 from "tslib";
import { Notifications } from '@sourcegraph/extensions-client-common/lib/app/notifications/Notifications';
import { createController as createExtensionsController } from '@sourcegraph/extensions-client-common/lib/client/controller';
import { connectAsPage } from '@sourcegraph/extensions-client-common/lib/messaging';
import AlertCircleIcon from 'mdi-react/AlertCircleIcon';
import ServerIcon from 'mdi-react/ServerIcon';
import * as React from 'react';
import { Route } from 'react-router';
import { BrowserRouter } from 'react-router-dom';
import { combineLatest, from, Subscription } from 'rxjs';
import { startWith } from 'rxjs/operators';
import { EMPTY_ENVIRONMENT as EXTENSIONS_EMPTY_ENVIRONMENT } from 'sourcegraph/module/client/environment';
import { currentUser } from './auth';
import { FeedbackText } from './components/FeedbackText';
import { HeroPage } from './components/HeroPage';
import { Tooltip } from './components/tooltip/Tooltip';
import { createExtensionsContextController } from './extensions/ExtensionsClientCommonContext';
import { createMessageTransports, } from './extensions/ExtensionsClientCommonContext';
import { Layout } from './Layout';
import { updateUserSessionStores } from './marketing/util';
import { clientConfiguration } from './settings/configuration';
import { eventLogger } from './tracking/eventLogger';
import { isErrorLike } from './util/errors';
const LIGHT_THEME_LOCAL_STORAGE_KEY = 'light-theme';
/** A fallback configuration subject that can be constructed synchronously at initialization time. */
const SITE_SUBJECT_NO_ADMIN = {
    id: window.context.siteGQLID,
    viewerCanAdminister: false,
};
/**
 * The root component
 */
export class SourcegraphWebApp extends React.Component {
    constructor(props) {
        super(props);
        this.subscriptions = new Subscription();
        this.onThemeChange = () => {
            this.setState(state => ({ isLightTheme: !state.isLightTheme }), () => {
                eventLogger.log(this.state.isLightTheme ? 'LightThemeClicked' : 'DarkThemeClicked');
            });
        };
        this.onMainPage = (mainPage) => {
            this.setState(state => ({ isMainPage: mainPage }));
        };
        this.onNavbarQueryChange = (navbarSearchQuery) => {
            this.setState({ navbarSearchQuery });
        };
        this.onHelpPopoverToggle = (visible) => {
            eventLogger.log('HelpPopoverToggled');
            this.setState(prevState => ({
                // If visible is any non-boolean type (e.g., MouseEvent), treat it as undefined. This lets callers use
                // onHelpPopoverToggle directly in an event handler without wrapping it in an another function.
                showHelpPopover: visible !== true && visible !== false ? !prevState.showHelpPopover : visible,
            }));
        };
        this.extensionsOnVisibleTextDocumentsChange = (visibleTextDocuments) => {
            this.setState(prevState => ({ extensionsEnvironment: Object.assign({}, prevState.extensionsEnvironment, { visibleTextDocuments }) }), () => this.state.extensionsController.setEnvironment(this.state.extensionsEnvironment));
        };
        const clientConnection = connectAsPage();
        const extensions = createExtensionsContextController(clientConnection);
        this.state = {
            isLightTheme: localStorage.getItem(LIGHT_THEME_LOCAL_STORAGE_KEY) !== 'false',
            navbarSearchQuery: '',
            showHelpPopover: false,
            configurationCascade: { subjects: null, merged: null },
            extensions,
            extensionsEnvironment: EXTENSIONS_EMPTY_ENVIRONMENT,
            extensionsController: createExtensionsController(extensions.context, createMessageTransports),
            viewerSubject: SITE_SUBJECT_NO_ADMIN,
            clientConnection,
            isMainPage: false,
        };
    }
    componentDidMount() {
        updateUserSessionStores();
        document.body.classList.add('theme');
        this.subscriptions.add(currentUser.subscribe(user => this.setState({ user }), () => this.setState({ user: null })));
        this.state.clientConnection
            .then(connection => {
            connection
                .getSettings()
                .then(settings => clientConfiguration.next(settings))
                .catch(error => console.error(error));
            connection.onSettings(settings => clientConfiguration.next(settings));
        })
            .catch(error => console.error(error));
        this.subscriptions.add(combineLatest(from(this.state.extensions.context.configurationCascade).pipe(startWith(null)), currentUser.pipe(startWith(null)), clientConfiguration).subscribe(([cascade, user, clientConfiguration]) => {
            this.setState(() => {
                if (clientConfiguration !== undefined) {
                    return {
                        viewerSubject: {
                            id: 'Client',
                            viewerCanAdminister: true,
                        },
                    };
                }
                else if (user) {
                    return { viewerSubject: user };
                }
                else if (cascade &&
                    !isErrorLike(cascade) &&
                    cascade.subjects &&
                    !isErrorLike(cascade.subjects) &&
                    cascade.subjects.length > 0) {
                    return { viewerSubject: cascade.subjects[0].subject };
                }
                else {
                    return { viewerSubject: SITE_SUBJECT_NO_ADMIN };
                }
            });
        }));
        this.subscriptions.add(this.state.extensionsController);
        this.subscriptions.add(this.state.extensions.context.configurationCascade.subscribe(v => this.onConfigurationCascadeChange(v), err => console.error(err)));
        // Keep the Sourcegraph extensions controller's extensions up-to-date.
        //
        // TODO(sqs): handle loading and errors
        this.subscriptions.add(this.state.extensions.viewerConfiguredExtensions.subscribe(extensions => this.onViewerConfiguredExtensionsChange(extensions), err => console.error(err)));
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
        document.body.classList.remove('theme');
        document.body.classList.remove('theme-light');
        document.body.classList.remove('theme-dark');
    }
    componentDidUpdate() {
        // Always show MainPage in dark theme look
        if (this.state.isMainPage && this.state.isLightTheme) {
            document.body.classList.remove('theme-light');
            document.body.classList.add('theme-dark');
        }
        else {
            localStorage.setItem(LIGHT_THEME_LOCAL_STORAGE_KEY, this.state.isLightTheme + '');
            document.body.classList.toggle('theme-light', this.state.isLightTheme);
            document.body.classList.toggle('theme-dark', !this.state.isLightTheme);
        }
    }
    render() {
        if (this.state.error) {
            return React.createElement(HeroPage, { icon: AlertCircleIcon, title: 'Something happened', subtitle: this.state.error.message });
        }
        if (window.pageError && window.pageError.statusCode !== 404) {
            const statusCode = window.pageError.statusCode;
            const statusText = window.pageError.statusText;
            const errorMessage = window.pageError.error;
            const errorID = window.pageError.errorID;
            let subtitle;
            if (errorID) {
                subtitle = React.createElement(FeedbackText, { headerText: "Sorry, there's been a problem." });
            }
            if (errorMessage) {
                subtitle = (React.createElement("div", { className: "app__error" },
                    subtitle,
                    subtitle && React.createElement("hr", null),
                    React.createElement("pre", null, errorMessage)));
            }
            else {
                subtitle = React.createElement("div", { className: "app__error" }, subtitle);
            }
            return React.createElement(HeroPage, { icon: ServerIcon, title: `${statusCode}: ${statusText}`, subtitle: subtitle });
        }
        const { user } = this.state;
        if (user === undefined) {
            return null;
        }
        const _a = this.props, { children } = _a, props = tslib_1.__rest(_a, ["children"]);
        return (React.createElement(React.Fragment, null,
            React.createElement(BrowserRouter, { key: 0 },
                React.createElement(Route, { path: "/", 
                    // tslint:disable-next-line:jsx-no-lambda RouteProps.render is an exception
                    render: routeComponentProps => (React.createElement(Layout, Object.assign({}, props, routeComponentProps, { user: user, viewerSubject: this.state.viewerSubject, configurationCascade: this.state.configurationCascade, 
                        // Theme
                        isLightTheme: this.state.isLightTheme, onThemeChange: this.onThemeChange, isMainPage: this.state.isMainPage, onMainPage: this.onMainPage, 
                        // Search query
                        navbarSearchQuery: this.state.navbarSearchQuery, onNavbarQueryChange: this.onNavbarQueryChange, 
                        // Help popover
                        showHelpPopover: this.state.showHelpPopover, onHelpPopoverToggle: this.onHelpPopoverToggle, 
                        // Extensions
                        extensions: this.state.extensions, extensionsEnvironment: this.state.extensionsEnvironment, extensionsOnVisibleTextDocumentsChange: this.extensionsOnVisibleTextDocumentsChange, extensionsController: this.state.extensionsController, clientConnection: this.state.clientConnection }))) })),
            React.createElement(Tooltip, { key: 1 }),
            React.createElement(Notifications, { key: 2, extensionsController: this.state.extensionsController })));
    }
    onConfigurationCascadeChange(configurationCascade) {
        this.setState(prevState => {
            const update = {
                configurationCascade,
                extensionsEnvironment: prevState.extensionsEnvironment,
            };
            if (configurationCascade.subjects !== null &&
                !isErrorLike(configurationCascade.subjects) &&
                configurationCascade.merged !== null &&
                !isErrorLike(configurationCascade.merged)) {
                // Only update Sourcegraph extensions environment configuration if the configuration was
                // successfully parsed.
                //
                // TODO(sqs): Think through how this error should be handled.
                update.extensionsEnvironment = Object.assign({}, prevState.extensionsEnvironment, { configuration: {
                        subjects: configurationCascade.subjects.filter((subject) => subject.settings !== null && !isErrorLike(subject.settings)),
                        merged: configurationCascade.merged,
                    } });
            }
            return update;
        }, () => this.state.extensionsController.setEnvironment(this.state.extensionsEnvironment));
    }
    onViewerConfiguredExtensionsChange(viewerConfiguredExtensions) {
        this.setState(prevState => ({
            extensionsEnvironment: Object.assign({}, prevState.extensionsEnvironment, { extensions: viewerConfiguredExtensions }),
        }), () => this.state.extensionsController.setEnvironment(this.state.extensionsEnvironment));
    }
}
//# sourceMappingURL=SourcegraphWebApp.js.map