import { LoadingSpinner } from '@sourcegraph/react-loading-spinner';
import { upperFirst } from 'lodash';
import AddIcon from 'mdi-react/AddIcon';
import CloseIcon from 'mdi-react/CloseIcon';
import EmailOpenOutlineIcon from 'mdi-react/EmailOpenOutlineIcon';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { merge, of, Subject, Subscription } from 'rxjs';
import { catchError, distinctUntilChanged, filter, map, mergeMap, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { gql, mutateGraphQL } from '../../backend/graphql';
import { CopyableText } from '../../components/CopyableText';
import { DismissibleAlert } from '../../components/DismissibleAlert';
import { Form } from '../../components/Form';
import { eventLogger } from '../../tracking/eventLogger';
import { createAggregateError } from '../../util/errors';
function inviteUserToOrganization(username, organization) {
    return mutateGraphQL(gql `
            mutation InviteUserToOrganization($organization: ID!, $username: String!) {
                inviteUserToOrganization(organization: $organization, username: $username) {
                    sentInvitationEmail
                    invitationURL
                }
            }
        `, {
        username,
        organization,
    }).pipe(map(({ data, errors }) => {
        const eventData = {
            organization: {
                invite: {
                    username,
                },
                org_id: organization,
            },
        };
        if (!data || !data.inviteUserToOrganization || (errors && errors.length > 0)) {
            eventLogger.log('InviteOrgMemberFailed', eventData);
            throw createAggregateError(errors);
        }
        eventLogger.log('OrgMemberInvited', eventData);
        return data.inviteUserToOrganization;
    }));
}
function addUserToOrganization(username, organization) {
    return mutateGraphQL(gql `
            mutation AddUserToOrganization($organization: ID!, $username: String!) {
                addUserToOrganization(organization: $organization, username: $username) {
                    alwaysNil
                }
            }
        `, {
        username,
        organization,
    }).pipe(map(({ data, errors }) => {
        if (!data || !data.addUserToOrganization || (errors && errors.length > 0)) {
            eventLogger.log('AddOrgMemberFailed');
            throw createAggregateError(errors);
        }
        eventLogger.log('OrgMemberAdded');
    }));
}
const emailInvitesEnabled = window.context.emailEnabled;
const InvitedNotification = ({ className, username, sentInvitationEmail, invitationURL, onDismiss }) => (React.createElement("div", { className: `${className} invited-notification` },
    React.createElement("div", { className: "invited-notification__message" },
        sentInvitationEmail ? (React.createElement(React.Fragment, null,
            "Invitation sent to ",
            username,
            ". You can also send ",
            username,
            " the invitation link directly:")) : (React.createElement(React.Fragment, null,
            "Generated invitation link. Copy and send it to ",
            username,
            ":")),
        React.createElement(CopyableText, { text: invitationURL, size: 40, className: "mt-2" })),
    React.createElement("button", { className: "btn btn-icon", title: "Dismiss", onClick: onDismiss },
        React.createElement(CloseIcon, { className: "icon-inline" }))));
export class InviteForm extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = { username: '' };
        this.submits = new Subject();
        this.inviteClicks = new Subject();
        this.usernameChanges = new Subject();
        this.componentUpdates = new Subject();
        this.subscriptions = new Subscription();
        this.onUsernameChange = e => this.usernameChanges.next(e.currentTarget.value);
        this.onSubmit = e => this.submits.next(e);
        this.onInviteClick = e => this.inviteClicks.next(e);
        this.dismissNotification = (i) => {
            this.setState(prevState => ({ invited: (prevState.invited || []).filter((_, j) => i !== j) }));
        };
    }
    componentDidMount() {
        const orgChanges = this.componentUpdates.pipe(distinctUntilChanged((a, b) => a.orgID !== b.orgID));
        this.subscriptions.add(this.usernameChanges.subscribe(username => this.setState({ username })));
        // Invite clicks.
        this.subscriptions.add(merge(this.submits.pipe(filter(() => !this.viewerCanAddUserToOrganization)), this.inviteClicks)
            .pipe(tap(e => e.preventDefault()), withLatestFrom(orgChanges, this.usernameChanges), tap(([, orgId, username]) => eventLogger.log('InviteOrgMemberClicked', {
            organization: {
                invite: {
                    username,
                },
                org_id: orgId,
            },
        })), mergeMap(([, { orgID }, username]) => inviteUserToOrganization(username, orgID).pipe(tap(() => this.props.onOrganizationUpdate()), tap(() => this.usernameChanges.next('')), mergeMap(({ sentInvitationEmail, invitationURL }) => 
        // Reset email, reenable submit button, flash "invited" text
        of((state) => (Object.assign({}, state, { loading: undefined, error: undefined, username: '', invited: [
                ...(state.invited || []),
                { username, sentInvitationEmail, invitationURL },
            ] })))), 
        // Disable button while loading
        startWith((state) => (Object.assign({}, state, { loading: 'inviteUserToOrganization' }))), catchError(error => [(state) => (Object.assign({}, state, { loading: undefined, error }))]))))
            .subscribe(stateUpdate => this.setState(stateUpdate), err => console.error(err)));
        // Adds.
        this.subscriptions.add(this.submits
            .pipe(filter(() => this.viewerCanAddUserToOrganization))
            .pipe(tap(e => e.preventDefault()), withLatestFrom(orgChanges, this.usernameChanges), mergeMap(([, { orgID }, username]) => addUserToOrganization(username, orgID).pipe(tap(() => this.props.onDidUpdateOrganizationMembers()), tap(() => this.usernameChanges.next('')), mergeMap(() => 
        // Reset email, reenable submit button, flash "invited" text
        of((state) => (Object.assign({}, state, { loading: undefined, error: undefined, username: '' })))), 
        // Disable button while loading
        startWith((state) => (Object.assign({}, state, { loading: 'addUserToOrganization' }))), catchError(error => [(state) => (Object.assign({}, state, { loading: undefined, error }))]))))
            .subscribe(stateUpdate => this.setState(stateUpdate), err => console.error(err)));
        this.componentUpdates.next(this.props);
    }
    componentWillReceiveProps(nextProps) {
        this.componentUpdates.next(nextProps);
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    get viewerCanAddUserToOrganization() {
        return !!this.props.authenticatedUser && this.props.authenticatedUser.siteAdmin;
    }
    render() {
        const viewerCanAddUserToOrganization = this.viewerCanAddUserToOrganization;
        return (React.createElement("div", { className: "invite-form" },
            React.createElement("div", { className: "card invite-form__container" },
                React.createElement("div", { className: "card-body" },
                    React.createElement("h4", { className: "card-title" }, this.viewerCanAddUserToOrganization ? 'Add or invite member' : 'Invite member'),
                    React.createElement(Form, { className: "form-inline align-items-start", onSubmit: this.onSubmit },
                        React.createElement("label", { className: "sr-only", htmlFor: "invite-form__username" }, "Username"),
                        React.createElement("input", { type: "text", className: "form-control mb-2 mr-sm-2", id: "invite-form__username", placeholder: "Username", onChange: this.onUsernameChange, value: this.state.username, autoComplete: "off", autoCapitalize: "off", autoCorrect: "off", required: true, spellCheck: false, size: 30 }),
                        viewerCanAddUserToOrganization && (React.createElement("button", { type: "submit", disabled: !!this.state.loading, className: "btn btn-primary mb-2 mr-sm-2", "data-tooltip": "Add immediately without sending invitation (site admins only)" },
                            this.state.loading === 'addUserToOrganization' ? (React.createElement(LoadingSpinner, { className: "icon-inline" })) : (React.createElement(AddIcon, { className: "icon-inline" })),
                            ' ',
                            "Add member")),
                        (emailInvitesEnabled || !this.viewerCanAddUserToOrganization) && (React.createElement("div", { className: "form-group flex-column mb-2 mr-sm-2" },
                            React.createElement("button", { type: viewerCanAddUserToOrganization ? 'button' : 'submit', disabled: !!this.state.loading, className: `btn ${viewerCanAddUserToOrganization ? 'btn-secondary' : 'btn-primary'}`, "data-tooltip": emailInvitesEnabled
                                    ? 'Send invitation email with link to join this organization'
                                    : 'Generate invitation link to manually send to user', onClick: viewerCanAddUserToOrganization ? this.onInviteClick : undefined },
                                this.state.loading === 'inviteUserToOrganization' ? (React.createElement(LoadingSpinner, { className: "icon-inline" })) : (React.createElement(EmailOpenOutlineIcon, { className: "icon-inline" })),
                                ' ',
                                emailInvitesEnabled
                                    ? this.viewerCanAddUserToOrganization
                                        ? 'Send invitation to join'
                                        : 'Send invitation'
                                    : 'Generate invitation link')))))),
            this.props.authenticatedUser &&
                this.props.authenticatedUser.siteAdmin &&
                !window.context.emailEnabled && (React.createElement(DismissibleAlert, { className: "alert-info", partialStorageKey: "org-invite-email-config" },
                React.createElement("p", { className: " mb-0" },
                    "Set ",
                    React.createElement("code", null, "email.smtp"),
                    " in",
                    ' ',
                    React.createElement(Link, { to: "/site-admin/configuration" }, "site configuration"),
                    " to send email notfications about invitations."))),
            this.state.invited &&
                this.state.invited.map(({ username, sentInvitationEmail, invitationURL }, i) => (React.createElement(InvitedNotification, { key: i, className: "alert alert-success invite-form__alert", username: username, sentInvitationEmail: sentInvitationEmail, invitationURL: invitationURL, 
                    // tslint:disable-next-line:jsx-no-lambda
                    onDismiss: () => this.dismissNotification(i) }))),
            this.state.error && (React.createElement("div", { className: "invite-form__alert alert alert-danger" },
                "Error: ",
                upperFirst(this.state.error.message)))));
    }
}
//# sourceMappingURL=InviteForm.js.map