Skip to content

Commit 19d0ec1

Browse files
committed
Handles Home view state load failures gracefully
- Prevents the Home view from breaking if subscription data fails to load - Falls back to a default community subscription state to maintain functionality - Adds telemetry to track and diagnose when state loading fails Refs #4766
1 parent 8b0ec28 commit 19d0ec1

File tree

4 files changed

+59
-13
lines changed

4 files changed

+59
-13
lines changed

docs/telemetry-events.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,6 +2613,18 @@ void
26132613
void
26142614
```
26152615

2616+
### home/failed
2617+
2618+
> Sent when Home fails to load some state
2619+
2620+
```typescript
2621+
{
2622+
'error': string,
2623+
'error.detail': string,
2624+
'reason': 'subscription'
2625+
}
2626+
```
2627+
26162628
### home/preview/toggled
26172629

26182630
> Sent when the new Home view preview is toggled on/off

src/constants.telemetry.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ export interface TelemetryEvents extends WebviewShowAbortedEvents, WebviewShownE
227227
'home/changeBranchMergeTarget': void;
228228
/** Sent when the user chooses to enable AI from the integrations menu */
229229
'home/enableAi': void;
230+
/** Sent when Home fails to load some state */
231+
'home/failed': HomeFailedEvent;
230232

231233
/** Sent when the user takes an action on the Launchpad title bar */
232234
'launchpad/title/action': LaunchpadTitleActionEvent;
@@ -786,6 +788,12 @@ interface HomePreviewToggledEvent {
786788
version: string;
787789
}
788790

791+
interface HomeFailedEvent {
792+
reason: 'subscription';
793+
error: string;
794+
'error.detail'?: string;
795+
}
796+
789797
type InspectWipContextEventData = {
790798
'context.mode': 'wip';
791799
'context.attachedTo': 'graph' | 'default';

src/webviews/home/homeWebview.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import type { Subscription } from '../../plus/gk/models/subscription';
6060
import type { SubscriptionChangeEvent } from '../../plus/gk/subscriptionService';
6161
import { isMcpBannerEnabled, mcpExtensionRegistrationAllowed } from '../../plus/gk/utils/-webview/mcp.utils';
6262
import { isAiAllAccessPromotionActive } from '../../plus/gk/utils/-webview/promo.utils';
63-
import { isSubscriptionTrialOrPaidFromState } from '../../plus/gk/utils/subscription.utils';
63+
import { getCommunitySubscription, isSubscriptionTrialOrPaidFromState } from '../../plus/gk/utils/subscription.utils';
6464
import type { ConfiguredIntegrationsChangeEvent } from '../../plus/integrations/authentication/configuredIntegrationService';
6565
import type { ConnectionStateChangeEvent } from '../../plus/integrations/integrationService';
6666
import { providersMetadata } from '../../plus/integrations/providers/models';
@@ -84,6 +84,8 @@ import { debug, log } from '../../system/decorators/log';
8484
import type { Deferrable } from '../../system/function/debounce';
8585
import { debounce } from '../../system/function/debounce';
8686
import { filterMap } from '../../system/iterable';
87+
import { getLoggableName, Logger } from '../../system/logger';
88+
import { startLogScope } from '../../system/logger.scope';
8789
import { getSettledValue } from '../../system/promise';
8890
import { SubscriptionManager } from '../../system/subscriptionManager';
8991
import type { UriTypes } from '../../uris/deepLinks/deepLink';
@@ -113,6 +115,7 @@ import type {
113115
OverviewRepository,
114116
OverviewStaleThreshold,
115117
State,
118+
SubscriptionState,
116119
} from './protocol';
117120
import {
118121
ChangeOverviewRepositoryCommand,
@@ -884,15 +887,32 @@ export class HomeWebviewProvider implements WebviewProvider<State, State, HomeWe
884887
}
885888

886889
private async getState(subscription?: Subscription): Promise<State> {
887-
const [subResult, integrationResult, aiModelResult, aiAllAccessBannerCollapsed] = await Promise.allSettled([
888-
this.getSubscriptionState(subscription),
889-
this.getIntegrationStates(true),
890-
this.container.ai.getModel({ silent: true }, { source: 'home' }),
891-
this.getAiAllAccessBannerCollapsed(),
892-
]);
890+
const [subscriptionResult, integrationResult, aiModelResult, aiAllAccessBannerCollapsed] =
891+
await Promise.allSettled([
892+
this.getSubscriptionState(subscription),
893+
this.getIntegrationStates(true),
894+
this.container.ai.getModel({ silent: true }, { source: 'home' }),
895+
this.getAiAllAccessBannerCollapsed(),
896+
]);
893897

894-
if (subResult.status === 'rejected') {
895-
throw subResult.reason;
898+
// Handle subscription rejection gracefully by falling back to community subscription
899+
let subscriptionState: SubscriptionState;
900+
if (subscriptionResult.status === 'fulfilled') {
901+
subscriptionState = subscriptionResult.value;
902+
} else {
903+
using scope = startLogScope(`${getLoggableName(this)}.getState(${Logger.toLoggable(subscription)})`, false);
904+
Logger.error(subscriptionResult.reason, scope, 'Failed to get subscription state');
905+
906+
this.container.telemetry.sendEvent('home/failed', {
907+
reason: 'subscription',
908+
error: String(subscriptionResult.reason),
909+
});
910+
911+
subscriptionState = {
912+
subscription: getCommunitySubscription(),
913+
avatar: `${this.host.getWebRoot() ?? ''}/media/gitlens-logo.webp`,
914+
organizationsCount: 0,
915+
};
896916
}
897917

898918
const integrations = getSettledValue(integrationResult) ?? [];
@@ -904,9 +924,9 @@ export class HomeWebviewProvider implements WebviewProvider<State, State, HomeWe
904924
discovering: this._discovering != null,
905925
repositories: this.getRepositoriesState(),
906926
webroot: this.host.getWebRoot(),
907-
subscription: subResult.value.subscription,
908-
avatar: subResult.value.avatar,
909-
organizationsCount: subResult.value.organizationsCount,
927+
subscription: subscriptionState.subscription,
928+
avatar: subscriptionState.avatar,
929+
organizationsCount: subscriptionState.organizationsCount,
910930
orgSettings: this.getOrgSettings(),
911931
aiEnabled: this.getAiEnabled(),
912932
experimentalComposerEnabled: this.getExperimentalComposerEnabled(),
@@ -1281,7 +1301,7 @@ export class HomeWebviewProvider implements WebviewProvider<State, State, HomeWe
12811301
return isSubscriptionTrialOrPaidFromState(subscription.state);
12821302
}
12831303

1284-
private async getSubscriptionState(subscription?: Subscription) {
1304+
private async getSubscriptionState(subscription?: Subscription): Promise<SubscriptionState> {
12851305
subscription = await this.getSubscription(subscription);
12861306
this._etagSubscription = this.container.subscription.etag;
12871307

src/webviews/home/protocol.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ export interface State extends WebviewState<'gitlens.views.home'> {
5454
amaBannerCollapsed: boolean;
5555
}
5656

57+
export interface SubscriptionState {
58+
subscription: Subscription;
59+
avatar: string;
60+
organizationsCount: number;
61+
}
62+
5763
export interface IntegrationState extends IntegrationDescriptor {
5864
connected: boolean;
5965
}

0 commit comments

Comments
 (0)