Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-pick: Revert changes to render featured extensions when available #184747

Merged
merged 1 commit into from Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -14,6 +14,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { localize } from 'vs/nls';
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { raceTimeout } from 'vs/base/common/async';

type FeaturedExtensionTreatment = { extensions: string[]; showAsList?: string };
type FeaturedExtensionStorageData = { title: string; description: string; imagePath: string; date: number };
Expand Down Expand Up @@ -84,15 +85,8 @@ export class FeaturedExtensionsService extends Disposable implements IFeaturedEx
return;
}

const extensions = await Promise.race([
this.tasExperimentService?.getTreatment<string>('welcome.featured.item'),
new Promise<string | undefined>(resolve => setTimeout(() => resolve(''), 2000))
]);

const extensionListTitle = await Promise.race([
this.tasExperimentService?.getTreatment<string>('welcome.featured.title'),
new Promise<string | undefined>(resolve => setTimeout(() => resolve(''), 2000))
]);
const [extensions, extensionListTitle] = await Promise.all([raceTimeout(this.tasExperimentService?.getTreatment<string>('welcome.featured.item'), 2000),
raceTimeout(this.tasExperimentService?.getTreatment<string>('welcome.featured.title'), 2000)]);

try {
this.treatment = extensions ? JSON.parse(extensions) : { extensions: [] };
Expand Down
Expand Up @@ -73,6 +73,7 @@ import { IFeaturedExtensionsService } from 'vs/workbench/contrib/welcomeGettingS
import { IFeaturedExtension } from 'vs/base/common/product';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { onUnexpectedError } from 'vs/base/common/errors';

const SLIDE_TRANSITION_TIME_MS = 250;
const configurationKey = 'workbench.startupEditor';
Expand Down Expand Up @@ -130,9 +131,9 @@ export class GettingStartedPage extends EditorPane {

// Ensure that the these are initialized before use.
// Currently initialized before use in buildCategoriesSlide and scrollToCategory
private recentlyOpened!: IRecentlyOpened;
private recentlyOpened!: Promise<IRecentlyOpened>;
private gettingStartedCategories!: IResolvedWalkthrough[];
private featuredExtensions!: IFeaturedExtension[];
private featuredExtensions!: Promise<IFeaturedExtension[]>;

private currentWalkthrough: IResolvedWalkthrough | undefined;

Expand Down Expand Up @@ -209,10 +210,16 @@ export class GettingStartedPage extends EditorPane {
this.contextService = this._register(contextService.createScoped(this.container));
inWelcomeContext.bindTo(this.contextService).set(true);

this.gettingStartedCategories = this.gettingStartedService.getWalkthroughs();
this.featuredExtensions = this.featuredExtensionService.getExtensions();

this._register(this.dispatchListeners);
this.buildSlideThrottle = new Throttler();

const rerender = () => {
this.gettingStartedCategories = this.gettingStartedService.getWalkthroughs();
this.featuredExtensions = this.featuredExtensionService.getExtensions();

this.buildSlideThrottle.queue(async () => await this.buildCategoriesSlide());
};

Expand All @@ -227,7 +234,12 @@ export class GettingStartedPage extends EditorPane {

this._register(this.gettingStartedService.onDidAddWalkthrough(rerender));
this._register(this.gettingStartedService.onDidRemoveWalkthrough(rerender));
this._register(workspacesService.onDidChangeRecentlyOpened(rerender));

this.recentlyOpened = this.workspacesService.getRecentlyOpened();
this._register(workspacesService.onDidChangeRecentlyOpened(() => {
this.recentlyOpened = workspacesService.getRecentlyOpened();
rerender();
}));

this._register(this.gettingStartedService.onDidChangeWalkthrough(category => {
const ourCategory = this.gettingStartedCategories.find(c => c.id === category.id);
Expand Down Expand Up @@ -725,13 +737,6 @@ export class GettingStartedPage extends EditorPane {

private async buildCategoriesSlide() {

// Delay fetching welcome page content on startup until all extensions are ready.
await this.extensionService.whenInstalledExtensionsRegistered();

this.recentlyOpened = await this.workspacesService.getRecentlyOpened();
this.gettingStartedCategories = await this.gettingStartedService.getWalkthroughs();
this.featuredExtensions = await this.featuredExtensionService.getExtensions();

this.categoriesSlideDisposables.clear();
const showOnStartupCheckbox = new Toggle({
icon: Codicon.check,
Expand Down Expand Up @@ -831,6 +836,7 @@ export class GettingStartedPage extends EditorPane {
this.currentWalkthrough = this.gettingStartedCategories.find(category => category.id === this.editorInput.selectedCategory);

if (!this.currentWalkthrough) {
this.gettingStartedCategories = this.gettingStartedService.getWalkthroughs();
this.currentWalkthrough = this.gettingStartedCategories.find(category => category.id === this.editorInput.selectedCategory);
}

Expand Down Expand Up @@ -935,11 +941,19 @@ export class GettingStartedPage extends EditorPane {
});

recentlyOpenedList.onDidChange(() => this.registerDispatchListeners());
this.recentlyOpened.then(({ workspaces }) => {
// Filter out the current workspace
const workspacesWithID = workspaces
.filter(recent => !this.workspaceContextService.isCurrentWorkspace(isRecentWorkspace(recent) ? recent.workspace : recent.folderUri))
.map(recent => ({ ...recent, id: isRecentWorkspace(recent) ? recent.workspace.id : recent.folderUri.toString() }));

const updateEntries = () => {
recentlyOpenedList.setEntries(workspacesWithID);
};

const entries = this.recentlyOpened.workspaces.filter(recent => !this.workspaceContextService.isCurrentWorkspace(isRecentWorkspace(recent) ? recent.workspace : recent.folderUri))
.map(recent => ({ ...recent, id: isRecentWorkspace(recent) ? recent.workspace.id : recent.folderUri.toString() }));

recentlyOpenedList.setEntries(entries);
updateEntries();
recentlyOpenedList.register(this.labelService.onDidChangeFormatters(() => updateEntries()));
}).catch(onUnexpectedError);

return recentlyOpenedList;
}
Expand Down Expand Up @@ -1103,7 +1117,9 @@ export class GettingStartedPage extends EditorPane {
contextService: this.contextService,
});

featuredExtensionsList.setEntries(this.featuredExtensions);
this.featuredExtensions?.then(extensions => {
featuredExtensionsList.setEntries(extensions);
});

this.featuredExtensionsList?.onDidChange(() => {
this.registerDispatchListeners();
Expand Down Expand Up @@ -1159,7 +1175,7 @@ export class GettingStartedPage extends EditorPane {
private async scrollToCategory(categoryID: string, stepId?: string) {

if (!this.gettingStartedCategories.some(c => c.id === categoryID)) {
this.gettingStartedCategories = await this.gettingStartedService.getWalkthroughs();
this.gettingStartedCategories = this.gettingStartedService.getWalkthroughs();
}

const ourCategory = this.gettingStartedCategories.find(c => c.id === categoryID);
Expand Down
Expand Up @@ -18,7 +18,7 @@ import { joinPath } from 'vs/base/common/resources';
import { FileAccess } from 'vs/base/common/network';
import { EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ThemeIcon } from 'vs/base/common/themables';
import { BuiltinGettingStartedCategory, BuiltinGettingStartedStep, walkthroughs } from 'vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent';
import { walkthroughs } from 'vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent';
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
Expand All @@ -34,8 +34,6 @@ import { checkGlobFileExists } from 'vs/workbench/services/extensions/common/wor
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { DefaultIconPath } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IProductService } from 'vs/platform/product/common/productService';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';

export const HasMultipleNewFileEntries = new RawContextKey<boolean>('hasMultipleNewFileEntries', false);

Expand Down Expand Up @@ -98,8 +96,8 @@ export interface IWalkthroughsService {
readonly onDidChangeWalkthrough: Event<IResolvedWalkthrough>;
readonly onDidProgressStep: Event<IResolvedWalkthroughStep>;

getWalkthroughs(): Promise<IResolvedWalkthrough[]>;
getWalkthrough(id: string): Promise<IResolvedWalkthrough>;
getWalkthroughs(): IResolvedWalkthrough[];
getWalkthrough(id: string): IResolvedWalkthrough;

registerWalkthrough(descriptor: IWalkthroughLoose): void;

Expand All @@ -114,12 +112,6 @@ export interface IWalkthroughsService {
const DAYS = 24 * 60 * 60 * 1000;
const NEW_WALKTHROUGH_TIME = 7 * DAYS;

type WalkthroughTreatment = {
walkthroughId: string;
walkthroughStepIds?: string[];
stepOrder: number[];
};

export class WalkthroughsService extends Disposable implements IWalkthroughsService {
declare readonly _serviceBrand: undefined;

Expand Down Expand Up @@ -149,7 +141,6 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ

private metadata: WalkthroughMetaDataType;

private registeredWalkthroughs: boolean = false;
constructor(
@IStorageService private readonly storageService: IStorageService,
@ICommandService private readonly commandService: ICommandService,
Expand All @@ -162,9 +153,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ
@IHostService private readonly hostService: IHostService,
@IViewsService private readonly viewsService: IViewsService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IWorkbenchAssignmentService private readonly tasExperimentService: IWorkbenchAssignmentService,
@IProductService private readonly productService: IProductService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IWorkbenchAssignmentService private readonly tasExperimentService: IWorkbenchAssignmentService
) {
super();

Expand All @@ -178,28 +167,13 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ
this.initCompletionEventListeners();

HasMultipleNewFileEntries.bindTo(this.contextService).set(false);
}
this.registerWalkthroughs();

private async registerWalkthroughs() {

const treatmentString = await Promise.race([
this.tasExperimentService?.getTreatment<string>('welcome.walkthrough.content'),
new Promise<string | undefined>(resolve => setTimeout(() => resolve(''), 2000))
]);
}

const treatment: WalkthroughTreatment = treatmentString ? JSON.parse(treatmentString) : { walkthroughId: '' };
private registerWalkthroughs() {

walkthroughs.forEach(async (category, index) => {
let shouldReorder = false;
if (category.id === treatment?.walkthroughId) {
category = this.updateWalkthroughContent(category, treatment);

shouldReorder = (treatment?.stepOrder !== undefined && category.content.steps.length === treatment.stepOrder.length);
if (shouldReorder) {
category.content.steps = category.content.steps.filter((_step, index) => treatment.stepOrder[index] >= 0);
treatment.stepOrder = treatment.stepOrder.filter(value => value >= 0);
}
}

this._registerWalkthrough({
...category,
Expand All @@ -214,7 +188,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ
completionEvents: step.completionEvents ?? [],
description: parseDescription(step.description),
category: category.id,
order: shouldReorder ? (treatment?.stepOrder ? treatment.stepOrder[index] : index) : index,
order: index,
when: ContextKeyExpr.deserialize(step.when) ?? ContextKeyExpr.true(),
media: step.media.type === 'image'
? {
Expand All @@ -239,38 +213,12 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ
});
});

await this.lifecycleService.when(LifecyclePhase.Restored);

walkthroughsExtensionPoint.setHandler((_, { added, removed }) => {
added.map(e => this.registerExtensionWalkthroughContributions(e.description));
removed.map(e => this.unregisterExtensionWalkthroughContributions(e.description));
});
}

private updateWalkthroughContent(walkthrough: BuiltinGettingStartedCategory, experimentTreatment: WalkthroughTreatment): BuiltinGettingStartedCategory {

if (!experimentTreatment?.walkthroughStepIds) {
return walkthrough;
}

const walkthroughMetadata = this.productService.walkthroughMetadata?.find(value => value.id === walkthrough.id);
for (const step of experimentTreatment.walkthroughStepIds) {
const stepMetadata = walkthroughMetadata?.steps.find(value => value.id === step);
if (stepMetadata) {

const newStep: BuiltinGettingStartedStep = {
id: step,
title: stepMetadata.title,
description: stepMetadata.description,
when: stepMetadata.when,
media: stepMetadata.media
};
walkthrough.content.steps.push(newStep);
}
}
return walkthrough;
}

private initCompletionEventListeners() {
this._register(this.commandService.onDidExecuteCommand(command => this.progressByEvent(`onCommand:${command.commandId}`)));

Expand Down Expand Up @@ -493,22 +441,14 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ
});
}

async getWalkthrough(id: string): Promise<IResolvedWalkthrough> {
if (!this.registeredWalkthroughs) {
await this.registerWalkthroughs();
this.registeredWalkthroughs = true;
}
getWalkthrough(id: string): IResolvedWalkthrough {

const walkthrough = this.gettingStartedContributions.get(id);
if (!walkthrough) { throw Error('Trying to get unknown walkthrough: ' + id); }
return this.resolveWalkthrough(walkthrough);
}

async getWalkthroughs(): Promise<IResolvedWalkthrough[]> {
if (!this.registeredWalkthroughs) {
await this.registerWalkthroughs();
this.registeredWalkthroughs = true;
}
getWalkthroughs(): IResolvedWalkthrough[] {

const registeredCategories = [...this.gettingStartedContributions.values()];
const categoriesWithCompletion = registeredCategories
Expand Down