Skip to content

Commit

Permalink
Extension search fixes (#9231) (#9241)
Browse files Browse the repository at this point in the history
* fix wrong gh extension being downloadexd

* fix shareurl loading as extension

* small cleanup thumbless share scripts

* remove more specific types that were commented out

* remove comment on delay

* use addDepIfNoConflict to avoid duping loading logic

* make ExtensionMetaCard a fc

* actually just don't need className as it was just for key
  • Loading branch information
jwunderl authored Nov 18, 2022
1 parent b39a3d0 commit ba223d5
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 55 deletions.
9 changes: 5 additions & 4 deletions pxtlib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1666,8 +1666,9 @@ namespace ts.pxtc.service {


export enum ExtensionType {
Bundled,
Github
Bundled = 1,
Github = 2,
ShareScript = 3,
}

export interface ExtensionMeta {
Expand All @@ -1676,13 +1677,13 @@ namespace ts.pxtc.service {
description?: string,
imageUrl?: string,
type?: ExtensionType
learnMoreUrl?: string;

pkgConfig?: pxt.PackageConfig; // Added if the type is Bundled
repo?: pxt.github.GitRepo; //Added if the type is Github VVN TODO ADD THIS
learnMoreUrl?: string;
scriptInfo?: pxt.Cloud.JsonScript
}


export interface SearchInfo {
id: string;
name: string;
Expand Down
152 changes: 101 additions & 51 deletions webapp/src/extensionsBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Modal } from "../../react-common/components/controls/Modal";
import { classList } from "../../react-common/components/util";

type ExtensionMeta = pxtc.service.ExtensionMeta;
const ExtensionType = pxtc.service.ExtensionType;
type EmptyCard = { name: string, loading?: boolean }
const emptyCard: EmptyCard = { name: "", loading: true }

Expand Down Expand Up @@ -68,6 +69,12 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
parsedExt.unshift(e)
}
})

const shareUrlData = await fetchShareUrlDataAsync(searchFor);
if (shareUrlData) {
parsedExt.unshift(parseShareScript(shareUrlData));
}

addExtensionsToPool(parsedExt)
setExtensionsToShow(parsedExt)
setSearchComplete(true)
Expand All @@ -87,23 +94,25 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
}

function getExtensionFromFetched(extensionUrl: string) {
const parsedGithubRepo = pxt.github.parseRepoId(extensionUrl);
if (parsedGithubRepo)
return allExtensions.get(parsedGithubRepo.slug.toLowerCase());

const fullName = allExtensions.get(extensionUrl.toLowerCase())
if (fullName) {
if (fullName)
return fullName
}
const parsedGithubRepo = pxt.github.parseRepoId(extensionUrl)
if (!parsedGithubRepo) return undefined;
return allExtensions.get(parsedGithubRepo.slug.toLowerCase())

return undefined;
}

async function addDepIfNoConflict(config: pxt.PackageConfig, version: string) {
try {
props.hideExtensions();
core.showLoading("installingextension", lf("Adding extension..."))
core.showLoading("installingextension", lf("Adding extension..."));
const added = await pkg.mainEditorPkg()
.addDependencyAsync({ ...config, isExtension: true }, version, false)
if (added) {
await pxt.Util.delay(1000)
await pxt.Util.delay(200);
await props.reloadHeaderAsync();
}
}
Expand Down Expand Up @@ -139,11 +148,11 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
let r: { version: string, config: pxt.PackageConfig };
try {
core.showLoading("downloadingpackage", lf("downloading extension..."));
const pkg = getExtensionFromFetched(scr.name);
const pkg = getExtensionFromFetched(scr.repo.slug);
if (pkg) {
r = await pxt.github.downloadLatestPackageAsync(pkg.repo);
} else {
const res = await fetchGithubDataAsync([scr.name]);
const res = await fetchGithubDataAsync([scr.repo.slug]);
if (res && res.length > 0) {
const parsed = parseGithubRepo(res[0])
addExtensionsToPool([parsed])
Expand All @@ -159,6 +168,34 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
return await addDepIfNoConflict(r.config, r.version)
}

async function fetchShareUrlDataAsync(potentialShareUrl: string): Promise<pxt.Cloud.JsonScript> {
const scriptId = pxt.Cloud.parseScriptId(potentialShareUrl);
if (!scriptId)
return undefined;

const scriptData = await data.getAsync<pxt.Cloud.JsonScript>(`cloud-search:${scriptId}`);

// TODO: fix typing on getAsync? it looks like it returns T or the failed network request
if ((scriptData as any).statusCode == 404) {
return undefined;
}
// unwrap array if returned as array
if (Array.isArray(scriptData)) {
return scriptData[0];
}

return scriptData;
}
async function addShareUrlExtension(scr: pxt.Cloud.JsonScript): Promise<void> {
// todo: we justed used name before but that's easy to lead to conflicts?
// should this be scr.id or something as pkgid?
// todo: how to handle persistent links? right now scr.id is the current version,
// we should probably persist the s id and make it updatable with a refresh.
const shareScript = await workspace.getPublishedScriptAsync(scr.id);
const config = pxt.Util.jsonTryParse(shareScript[pxt.CONFIG_NAME]);
addDepIfNoConflict({...config, version: scr.id }, `pub:${scr.id}`);
}

async function fetchGithubDataAsync(preferredRepos: string[]): Promise<pxt.github.GitRepo[]> {
// When searching multiple repos at the same time, use 'extension-search' which caches results
// for much longer than 'gh-search'
Expand Down Expand Up @@ -195,15 +232,19 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {

function installExtension(scr: ExtensionMeta) {
switch (scr.type) {
case pxtc.service.ExtensionType.Bundled:
case ExtensionType.Bundled:
pxt.tickEvent("packages.bundled", { name: scr.name });
props.hideExtensions();
addDepIfNoConflict(scr.pkgConfig, "*")
addDepIfNoConflict(scr.pkgConfig, "*");
break;
case pxtc.service.ExtensionType.Github:
case ExtensionType.Github:
props.hideExtensions();
addGithubPackage(scr);
break;
case ExtensionType.ShareScript:
props.hideExtensions();
addShareUrlExtension(scr.scriptInfo);
break;
}
}

Expand All @@ -221,14 +262,24 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
function parseGithubRepo(r: pxt.github.GitRepo): ExtensionMeta {
return {
name: ghName(r),
type: pxtc.service.ExtensionType.Github,
type: ExtensionType.Github,
imageUrl: pxt.github.repoIconUrl(r),
repo: r,
description: r.description,
fullName: r.fullName
}
}

function parseShareScript(s: pxt.Cloud.JsonScript): ExtensionMeta {
return {
name: s.name,
type: ExtensionType.ShareScript,
imageUrl: s.thumb ? `${pxt.Cloud.apiRoot}/${s.id}/thumb` : undefined,
description: s.description,
scriptInfo: s,
}
}


function getCategoryNames(): string[] {
if (!extensionTags) return [];
Expand Down Expand Up @@ -273,7 +324,7 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
return {
name: p.name,
imageUrl: p.icon,
type: pxtc.service.ExtensionType.Bundled,
type: ExtensionType.Bundled,
learnMoreUrl: `/reference/${p.name}`,
pkgConfig: p,
description: p.description
Expand Down Expand Up @@ -364,6 +415,34 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
}
}

function ExtensionMetaCard(props: {
extensionInfo: ExtensionMeta & EmptyCard,
}) {
const { extensionInfo } = props;
const {
description,
fullName,
imageUrl,
learnMoreUrl,
loading,
name,
repo,
type,
} = extensionInfo;

return <ExtensionCard
title={name || fullName}
description={description}
imageUrl={imageUrl}
extension={extensionInfo}
onClick={installExtension}
learnMoreUrl={learnMoreUrl || (fullName ? `/pkg/${fullName}` : undefined)}
loading={loading}
label={pxt.isPkgBeta(extensionInfo) ? lf("Beta") : undefined}
showDisclaimer={type != ExtensionType.Bundled && repo?.status != pxt.github.GitRepoStatus.Approved}
/>;
}

enum ExtensionView {
Tabbed,
Search,
Expand Down Expand Up @@ -479,19 +558,9 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
{displayMode == ExtensionView.Search &&
<>
<div className="extension-cards">
{extensionsToShow?.map((scr, index) =>
<ExtensionCard
key={classList("searched", index + "", scr.loading && "loading")}
title={scr.name ?? `${index}`}
description={scr.description}
imageUrl={scr.imageUrl}
extension={scr}
onClick={installExtension}
learnMoreUrl={scr.learnMoreUrl || (scr.fullName ? `/pkg/${scr.fullName}` : undefined)}
loading={scr.loading}
label={pxt.isPkgBeta(scr) ? lf("Beta") : undefined}
showDisclaimer={scr.type != pxtc.service.ExtensionType.Bundled && scr.repo?.status != pxt.github.GitRepoStatus.Approved}
/>)}
{extensionsToShow?.map(
(scr, index) => <ExtensionMetaCard extensionInfo={scr} key={index} />
)}
</div>
{searchComplete && extensionsToShow.length == 0 &&
<div aria-label="Extension search results">
Expand All @@ -501,33 +570,14 @@ export const ExtensionsBrowser = (props: ExtensionsProps) => {
</>}
{displayMode == ExtensionView.Tags &&
<div className="extension-cards">
{extensionsToShow?.map((scr, index) =>
<ExtensionCard
key={classList("tagged", index + "", scr.loading && "loading")}
title={scr.name ?? `${index}`}
description={scr.description}
imageUrl={scr.imageUrl}
extension={scr}
onClick={installExtension}
learnMoreUrl={scr.learnMoreUrl || (scr.fullName ? `/pkg/${scr.fullName}` : undefined)}
loading={scr.loading}
label={pxt.isPkgBeta(scr) ? lf("Beta") : undefined}
/>)}
{extensionsToShow?.map(
(scr, index) => <ExtensionMetaCard extensionInfo={scr} key={index} />
)}
</div>}
{displayMode == ExtensionView.Tabbed &&
<div className="extension-cards">
{currentTab == TabState.Recommended && preferredExts.map((scr, index) =>
<ExtensionCard
key={classList("preferred", index + "", scr.loading && "loading")}
extension={scr}
title={scr.name ?? `${index}`}
onClick={installExtension}
imageUrl={scr.imageUrl}
description={scr.description}
learnMoreUrl={scr.learnMoreUrl || (scr.fullName ? `/pkg/${scr.fullName}` : undefined)}
loading={scr.loading}
label={pxt.isPkgBeta(scr) ? lf("Beta") : undefined}
/>
{currentTab == TabState.Recommended && preferredExts.map(
(scr, index) => <ExtensionMetaCard extensionInfo={scr} key={index} />
)}
{currentTab == TabState.InDevelopment && extensionsInDevelopment.map((p, index) =>
<ExtensionCard
Expand Down

0 comments on commit ba223d5

Please sign in to comment.