@@ -74,6 +74,7 @@ import { ensurePlusFeaturesEnabled } from './utils/-webview/plus.utils';
7474import { getConfiguredActiveOrganizationId , updateActiveOrganizationId } from './utils/-webview/subscription.utils' ;
7575import { getSubscriptionFromCheckIn } from './utils/checkin.utils' ;
7676import {
77+ AiAllAccessOptInPathPrefix ,
7778 assertSubscriptionState ,
7879 compareSubscriptionPlans ,
7980 computeSubscriptionState ,
@@ -140,6 +141,7 @@ export class SubscriptionService implements Disposable {
140141 }
141142 } ) ,
142143 container . uri . onDidReceiveSubscriptionUpdatedUri ( ( ) => this . checkUpdatedSubscription ( undefined ) , this ) ,
144+ container . uri . onDidReceiveAiAllAccessOptInUri ( this . onAiAllAccessOptInUri , this ) ,
143145 container . uri . onDidReceiveLoginUri ( this . onLoginUri , this ) ,
144146 ) ;
145147
@@ -348,6 +350,7 @@ export class SubscriptionService implements Disposable {
348350 registerCommand ( 'gitlens.plus.upgrade' , ( args ?: SubscriptionUpgradeCommandArgs ) =>
349351 this . upgrade ( args ?. plan , args ? { source : args . source , detail : args . detail } : undefined ) ,
350352 ) ,
353+ registerCommand ( 'gitlens.plus.aiAllAccess.optIn' , ( src ?: Source ) => this . aiAllAccessOptIn ( src ) ) ,
351354
352355 registerCommand ( 'gitlens.plus.hide' , ( src ?: Source ) => this . setProFeaturesVisibility ( false , src ) ) ,
353356 registerCommand ( 'gitlens.plus.restore' , ( src ?: Source ) => this . setProFeaturesVisibility ( true , src ) ) ,
@@ -1663,6 +1666,88 @@ export class SubscriptionService implements Disposable {
16631666
16641667 return this . _subscription . state ;
16651668 }
1669+
1670+ @log ( )
1671+ async aiAllAccessOptIn ( source : Source | undefined ) : Promise < boolean > {
1672+ const scope = getLogScope ( ) ;
1673+
1674+ if ( ! ( await ensurePlusFeaturesEnabled ( ) ) ) return false ;
1675+
1676+ const hasAccount = this . _session != null ;
1677+
1678+ const query = new URLSearchParams ( ) ;
1679+ query . set ( 'source' , 'gitlens' ) ;
1680+ query . set ( 'product' , 'gitlens' ) ;
1681+
1682+ try {
1683+ if ( hasAccount ) {
1684+ try {
1685+ const token = await this . container . accountAuthentication . getExchangeToken (
1686+ AiAllAccessOptInPathPrefix ,
1687+ ) ;
1688+ query . set ( 'token' , token ) ;
1689+ } catch ( ex ) {
1690+ Logger . error ( ex , scope ) ;
1691+ }
1692+ } else {
1693+ const callbackUri = await env . asExternalUri (
1694+ Uri . parse ( `${ env . uriScheme } ://${ this . container . context . extension . id } /${ AiAllAccessOptInPathPrefix } ` ) ,
1695+ ) ;
1696+ query . set ( 'redirect_uri' , callbackUri . toString ( true ) ) ;
1697+ }
1698+
1699+ if ( this . container . telemetry . enabled ) {
1700+ this . container . telemetry . sendEvent ( 'aiAllAccess/opened' , undefined , source ) ;
1701+ }
1702+
1703+ if ( ! ( await openUrl ( this . container . urls . getGkDevUrl ( 'all-access' , query ) ) ) ) {
1704+ return false ;
1705+ }
1706+ } catch ( ex ) {
1707+ Logger . error ( ex , scope ) ;
1708+ return false ;
1709+ }
1710+
1711+ const completionPromises = [
1712+ new Promise < string > ( resolve => setTimeout ( ( ) => resolve ( 'cancel' ) , 5 * 60 * 1000 ) ) ,
1713+ new Promise < string > ( resolve => once ( this . container . uri . onDidReceiveAiAllAccessOptInUri ) ( ( ) => resolve ( hasAccount ? 'update' : 'login' ) ) ) ,
1714+ ] ;
1715+
1716+ const action = await Promise . race ( completionPromises ) ;
1717+
1718+ if ( action === 'update' && hasAccount ) {
1719+ void this . checkUpdatedSubscription ( source ) ;
1720+ void this . container . storage . store ( `gk:promo:${ this . _session ?. account . id ?? '00000000' } :ai:allAccess:dismissed` , true ) . catch ( ) ;
1721+ void this . container . views . home . refresh ( ) ;
1722+ }
1723+
1724+ if ( action !== 'cancel' ) {
1725+ if ( this . container . telemetry . enabled ) {
1726+ this . container . telemetry . sendEvent ( 'aiAllAccess/optedIn' , undefined , source ) ;
1727+ }
1728+
1729+ return true ;
1730+ }
1731+
1732+ return false ;
1733+ }
1734+
1735+ private async onAiAllAccessOptInUri ( uri : Uri ) : Promise < void > {
1736+ const queryParams = new URLSearchParams ( uri . query ) ;
1737+ const code = queryParams . get ( 'code' ) ;
1738+
1739+ if ( code == null ) return ;
1740+
1741+ // If we don't have an account and received a code, login with the code
1742+ if ( this . _session == null ) {
1743+ await this . loginWithCode ( { code : code } , { source : 'subscription' } ) ;
1744+ const newSession = await this . getAuthenticationSession ( ) ;
1745+ if ( newSession ?. account ?. id != null ) {
1746+ await this . container . storage . store ( `gk:promo:${ newSession . account . id } :ai:allAccess:dismissed` , true ) . catch ( ) ;
1747+ void this . container . views . home . refresh ( ) ;
1748+ }
1749+ }
1750+ }
16661751}
16671752
16681753function flattenFeaturePreview ( preview : FeaturePreview ) : FeaturePreviewEventData {
0 commit comments