Skip to content

Instantly share code, notes, and snippets.

@vojtaholik
Last active November 5, 2021 14:05
Show Gist options
  • Save vojtaholik/36795499ad007e324660352d02217cd8 to your computer and use it in GitHub Desktop.
Save vojtaholik/36795499ad007e324660352d02217cd8 to your computer and use it in GitHub Desktop.
// Menu: Search egghead.io
// Description: Browse egghead.io's lesson library
// Shortcut: cmd option e
// Author: egghead.io
// Twitter: @eggheadio
/** @type {import("@johnlindquist/kit")} */
let algoliasearch = await npm("algoliasearch");
let app = await env("EGGHEAD_PUBLIC_ALGOLIA_APP");
let key = await env("EGGHEAD_PUBLIC_ALGOLIA_KEY");
let client = algoliasearch(app, key);
let index = client.initIndex("content_production");
let hit = await arg(
{
placeholder: "Search egghead:",
ignoreBlur: true,
input: "react",
},
async (input) => {
if (input) {
let { hits } = await index.search(input);
return hits.map((hit) => {
return {
name: hit.title,
value: hit,
// tag: '', // TODO: tag could be number of lessons, but it's not available here
img: hit.image,
description: hit.instructor_name,
preview: async () => {
// We _could_ query the egghead api here, but I think we have enough info already? πŸ€”
// TODO: Delay fetching individual lessons to avoid lagginess?
let lessons = await fetch(
`https://app.egghead.io/api/v1/playlists/${hit.slug}/items?flatten=true`
).then((r) => r.json());
// This doesn't seem to be working properly.
if (lessons) {
setFlags({
instructor: {
name: "View instructor profile",
description: "Open the instructor profile in your browser",
shortcut: "cmd+i",
url: `https://egghead.io/${hit.instructor_path}`,
},
twitter: {
name: "View instructor's twitter",
description: "Open the instructor twitter account",
shortcut: "cmd+t",
url: `https://twitter.com/${hit.instructor.twitter}`,
},
website: {
name: "View instructor's website",
description: "Open the instructor website",
shortcut: "cmd+w",
},
...lessons.map((lesson, i) => ({
name: `${i + 1}: ${lesson.title}`,
description: convertTimeWithTitles(lesson.duration),
shortcut: `cmd+${i + 1}`,
url: `https://egghead.io${lesson.path}`,
})),
});
}
return `
<div class="border-l dark:border-white dark:border-opacity-5 border-black border-opacity-5 h-full min-h-screen">
<div class="grid grid-cols-3 border-b dark:border-white dark:border-opacity-5 border-black border-opacity-5">
<div class="col-span-2 border-r dark:border-white dark:border-opacity-5 border-black border-opacity-5 flex flex-col">
<h1 class="p-5 h-full text-xl leading-tight font-semibold m-0">${
hit.title
}</h1>
<div class="pl-5 pr-2 py-2 flex items-center space-x-3 text-xs border-t dark:border-white dark:border-opacity-5 border-black border-opacity-5">
<div class="flex items-center">
<div class="rounded-full overflow-hidden w-8 h-8 flex items-center justify-center">
<img src="${hit.instructor_avatar_url}" />
</div>
<span class="pl-2">${hit.instructor_name}</span>
</div>
<div class="${
hit.access_state === "pro"
? "bg-yellow-500 text-white border border-transparent"
: "text-yellow-500 border border-yellow-500"
} rounded-full leading-none px-2 py-1 text-xs uppercase">
${hit.access_state}
</div>
<div>
${convertTimeWithTitles(hit.duration)}
</div>
</div>
</div>
<div class="p-5">
<img src="${hit.image}" />
</div>
</div>
<div class="p-5 border-b dark:border-white dark:border-opacity-5 border-black border-opacity-5">
<article class="prose prose-sm dark:prose-dark">
${md(hit.description)}
</article>
</div>
<ul class="text-sm list-none list-inside m-0">
<div class="uppercase text-xs pb-2 tracking-wide opacity-60 px-5 pt-5">Lessons</div>
${lessons
?.map((lesson, i) => {
return `
<li class="list-none">
<a class="dark:hover:bg-white dark:hover:bg-opacity-5 flex items-baseline py-3 px-5 border-b dark:border-white dark:border-opacity-5 border-black border-opacity-5 font-medium"
href="https://egghead.io${lesson.path}">
<span class="opacity-50 pr-2 text-xs">${i + 1}</span>
${lesson.title}
</a>
</li>
`;
})
.join("")}
</ul>
</div>
`;
},
};
});
}
}
);
let url = hit.url;
if (flag?.instructor) url = `https://egghead.io/${hit.instructor_path}`;
if (flag?.twitter) url = `https://twitter.com/${hit.instructor.twitter}`;
if (flag?.website) url = `${hit.instructor.twitter}`;
// TODO: Make this work for lessons inside flags menu
await $`open ${url}`;
// β€”β€”β€”β€” UTILS β€”β€”β€”β€”
function convertTimeWithTitles(seconds) {
if (seconds < 0) seconds = 0;
const hours = ~~(seconds / 3600);
const mins = ~~((seconds - hours * 3600) / 60);
const secs = (seconds - hours * 3600 - mins * 60) % 60;
let result = "";
if (hours) result += hours + "h ";
if (mins) result += mins + "m ";
if (mins < 1) result += secs + "s ";
return result.trim();
}
// TODO:
// - Lazy load more results (results from algolia are paginated)
// - Sometimes using space in search term errors out. No results fail - something to do with fetching lessons?
// - Fix flags for lessons, they don't work properly
// - Flags URL not working
// - Still issues with tailwind classes not being available
// - Script "frontmatter" parser takes all instances of `// shortcut: "cmd+w"` from the file, so one must remember to be careful
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment