Skip to content

Commit 88ec6a9

Browse files
[feat] Gitpod samples templates now in Raycast (#24)
* added gitpod samples' templates * added better error handling and readability * loading all default templates * improved speed by changing schema and fetching all repos as sample queries
1 parent 2081b58 commit 88ec6a9

File tree

6 files changed

+13569
-17453
lines changed

6 files changed

+13569
-17453
lines changed

package.json

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@
2727
"Open in Gitpod"
2828
]
2929
},
30+
{
31+
"name": "open_template",
32+
"title": "Find Gitpod Templates",
33+
"subtitle": "Gitpod Templates",
34+
"description": "Start your next app with our pre-configured templates",
35+
"mode": "view",
36+
"keywords": [
37+
"gitpod",
38+
"gp",
39+
"template"
40+
]
41+
},
3042
{
3143
"name": "menubar",
3244
"title": "Recent Repositories",

src/api/repository.graphql

+24
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,30 @@ query searchRepositories($query: String!, $numberOfItems: Int!) {
7777
}
7878
}
7979

80+
query searchTemplateRepositories($query: String!, $numberOfItems: Int!) {
81+
search(query: $query, first: $numberOfItems, type: REPOSITORY) {
82+
repos: edges {
83+
repo: node {
84+
... on Repository {
85+
name
86+
url
87+
id
88+
issues(states: OPEN) {
89+
totalCount
90+
}
91+
pullRequests(states: OPEN) {
92+
totalCount
93+
}
94+
owner {
95+
login
96+
avatarUrl(size: 64)
97+
}
98+
}
99+
}
100+
}
101+
}
102+
}
103+
80104
query getExistingRepoBranches(
81105
$orgName: String!
82106
$repoName: String!
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { List } from "@raycast/api";
2+
import { random } from "lodash";
3+
import { useEffect, useMemo, useState } from "react";
4+
5+
type TemplateRepositoryFieldsFragment = {
6+
name: string;
7+
url: string;
8+
id: string;
9+
stargazerCount: number;
10+
owner: { name?: string | null; login?: string; avatarUrl: string }
11+
issues: {totalCount: number}
12+
pullRequests: {totalCount: number}
13+
};
14+
15+
type TemplateListEmptyViewProps = {
16+
searchText: string;
17+
isLoading: boolean;
18+
sampleRepositories: TemplateRepositoryFieldsFragment[] | undefined;
19+
};
20+
21+
function searchStringInArray(str: string, strArray: string[] | undefined) {
22+
if (strArray) {
23+
for (let j = 0; j < strArray.length; j++) {
24+
if (strArray[j].match(str)) {
25+
return strArray[j];
26+
}
27+
}
28+
}
29+
return "";
30+
}
31+
32+
export default function TemplateListEmptyView({
33+
searchText,
34+
isLoading,
35+
sampleRepositories,
36+
}: TemplateListEmptyViewProps) {
37+
const example = useMemo(
38+
() =>
39+
sampleRepositories?.map((repository) => {
40+
return repository.name;
41+
})[random(0, sampleRepositories?.length - 1)],
42+
[]
43+
);
44+
45+
const sampleRepositoriesList = useMemo(() => sampleRepositories?.map((repository) => repository.name), []);
46+
47+
const [exampleSearch, setexampleSearchExampleSearch] = useState<string>(example as string);
48+
49+
useEffect(() => {
50+
if (sampleRepositoriesList && example) {
51+
setexampleSearchExampleSearch(searchStringInArray(searchText[0], sampleRepositoriesList) as string);
52+
}
53+
}, [searchText, sampleRepositoriesList, example]);
54+
55+
if (isLoading) {
56+
return <List.EmptyView title={`Type query e.g "${exampleSearch ?? example}"`} />;
57+
}
58+
59+
// If a search has been performed and returned no results, show a message.
60+
if (searchText.length > 0) {
61+
return <List.EmptyView title={`No templates found, try searching "${exampleSearch ?? example}"`} />;
62+
}
63+
64+
// Unreachable, but required by TypeScript.
65+
return null;
66+
}

src/components/TemplateListItem.tsx

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Color, List, ActionPanel, Action, showToast, Toast, open } from "@raycast/api";
2+
import { MutatePromise } from "@raycast/utils";
3+
4+
import { GitpodIcons } from "../../constants";
5+
import { ExtendedRepositoryFieldsFragment } from "../generated/graphql";
6+
import { getGitHubUser } from "../helpers/users";
7+
8+
type RepositoryListItemProps = {
9+
repository: {
10+
name: string;
11+
url: string;
12+
id: string;
13+
stargazerCount: number;
14+
owner: { name?: string | null; login?: string; avatarUrl: string }
15+
issues: {totalCount: number}
16+
pullRequests: {totalCount: number}
17+
};
18+
};
19+
20+
export default function TemplateListItem({ repository }: RepositoryListItemProps) {
21+
const owner = getGitHubUser(repository.owner);
22+
const numberOfStars = repository.stargazerCount;
23+
24+
const accessories: List.Item.Accessory[] = [];
25+
26+
const showLaunchToast = async () => {
27+
try {
28+
await showToast({
29+
title: "Launching your workspace",
30+
style: Toast.Style.Success,
31+
});
32+
setTimeout(() => {
33+
open(`https://gitpod.io/#${repository.url}`);
34+
}, 1500);
35+
} catch (error) {
36+
await showToast({
37+
title: "Error launching workspace",
38+
style: Toast.Style.Failure,
39+
});
40+
}
41+
};
42+
43+
accessories.unshift(
44+
{
45+
text: {
46+
value: repository.issues?.totalCount.toString(),
47+
},
48+
icon: GitpodIcons.issues_icon,
49+
},
50+
{
51+
text: {
52+
value: repository.pullRequests?.totalCount.toString(),
53+
},
54+
icon: GitpodIcons.pulls_icon,
55+
}
56+
);
57+
58+
return (
59+
<List.Item
60+
icon={owner.icon}
61+
title={repository.name}
62+
{...(numberOfStars > 0
63+
? {
64+
subtitle: {
65+
value: `${numberOfStars}`,
66+
tooltip: `Number of Stars: ${numberOfStars}`,
67+
},
68+
}
69+
: {})}
70+
accessories={accessories}
71+
actions={
72+
<ActionPanel>
73+
<Action title="Start with Gitpod" onAction={showLaunchToast} />
74+
<Action
75+
title="View template in GitHub"
76+
onAction={() => {
77+
open(repository.url);
78+
}}
79+
/>
80+
<Action
81+
title="Learn more about Gitpod templates"
82+
onAction={() => {
83+
open("https://www.gitpod.io/docs/introduction/getting-started/quickstart");
84+
}}
85+
/>
86+
<Action
87+
title="Add a template"
88+
onAction={() => {
89+
open("https://github.com/gitpod-samples/.github/blob/HEAD/CONTRIBUTING.md");
90+
}}
91+
/>
92+
</ActionPanel>
93+
}
94+
/>
95+
);
96+
}

0 commit comments

Comments
 (0)