import {Component} from 'react'; import {fetchOrganizationDetails} from 'sentry/actionCreators/organization'; import type {Client} from 'sentry/api'; import type {Organization} from 'sentry/types/organization'; import withApi from 'sentry/utils/withApi'; import withSubscription from 'getsentry/components/withSubscription'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import type {Subscription} from 'getsentry/types'; import trackMarketingEvent from 'getsentry/utils/trackMarketingEvent'; type ChildProps = { startTrial: () => Promise; subscription: Subscription; trialFailed: boolean; trialStarted: boolean; trialStarting: boolean; }; type Props = { api: Client; children: (args: ChildProps) => React.ReactNode; organization: Organization; source: string; subscription: Subscription; onTrialFailed?: (err: Error) => void; // Can't use default prop typings because of HoC wrappers. onTrialStarted?: () => void; requestData?: Record; }; type State = { trialFailed: boolean; trialStarted: boolean; trialStarting: boolean; }; class TrialStarter extends Component { static defaultProps = { onTrialFailed: () => {}, onTrialStarted: () => {}, }; state: State = { trialStarting: false, trialStarted: false, trialFailed: false, }; handleStartTrial = async () => { const {organization, source, onTrialStarted, onTrialFailed, requestData} = this.props; this.setState({trialStarting: true}); let data: any; let url: any; if (requestData) { data = {referrer: source, ...requestData}; url = `/customers/${organization.slug}/product-trial/`; } else { data = {trial: true, referrer: source}; url = `/customers/${organization.slug}/`; } try { await this.props.api.requestPromise(url, { method: 'PUT', data, }); } catch (err) { onTrialFailed?.(err); this.setState({trialStarting: false, trialFailed: true}); return; } this.setState({trialStarting: false, trialStarted: true}); trackMarketingEvent('Start Trial'); onTrialStarted?.(); // Refresh organization and subscription state SubscriptionStore.loadData(organization.slug, null, {markStartedTrial: true}); fetchOrganizationDetails(this.props.api, organization.slug); // we showed the "new" icon for the upsell that wasn't the actual dashboard // we should clear this so folks can see "new" for the actual dashboard localStorage.removeItem('sidebar-new-seen:customizable-dashboards'); }; render() { const {trialStarted, trialStarting, trialFailed} = this.state; const {subscription, children} = this.props; return children({ startTrial: this.handleStartTrial, trialStarting, trialStarted, trialFailed, subscription, }); } } // We enable the persistInFlight on the withApi wrapper to ensure that we don't // cancel the in-flight requests to reload the organization details after the // trial has been started. Otherwise if this component is unmounted as a result // of starting the trial. export default withSubscription(withApi(TrialStarter, {persistInFlight: true}));