Add video listing on homepage

astro
Kamran Ahmed 2 years ago
parent 9e010f10b9
commit cbc53a66d8
  1. 16
      src/components/FeaturedGuides.astro
  2. 35
      src/components/FeaturedVideos.astro
  3. 40
      src/components/VideoListItem.astro
  4. 2
      src/guides/http-basic-authentication.md
  5. 61
      src/lib/video.ts
  6. 6
      src/pages/index.astro
  7. 23
      src/pages/videos/index.astro

@ -10,24 +10,26 @@ export interface Props {
const { heading, guides } = Astro.props; const { heading, guides } = Astro.props;
--- ---
<h1 class='text-2xl sm:text-3xl font-bold block'>{heading}</h1> <div class='container'>
<h1 class='text-2xl sm:text-3xl font-bold block'>{heading}</h1>
<div class='mt-3 sm:my-5'> <div class='mt-3 sm:my-5'>
{guides.map((guide) => <GuideListItem guide={guide} />)} {guides.map((guide) => <GuideListItem guide={guide} />)}
</div> </div>
<a <a
href='/guides' href='/guides'
class='hidden sm:inline transition-colors py-2 px-3 text-xs font-medium rounded-full bg-gradient-to-r from-slate-600 to-black hover:from-blue-600 hover:to-blue-800 text-white' class='hidden sm:inline transition-colors py-2 px-3 text-xs font-medium rounded-full bg-gradient-to-r from-slate-600 to-black hover:from-blue-600 hover:to-blue-800 text-white'
> >
View All Guides &rarr; View All Guides &rarr;
</a> </a>
<div class='block sm:hidden mt-3'> <div class='block sm:hidden mt-3'>
<a <a
href='/guides' href='/guides'
class='text-sm font-regular block p-2 border border-black text-black rounded-md text-center hover:bg-black hover:text-gray-50' class='text-sm font-regular block p-2 border border-black text-black rounded-md text-center hover:bg-black hover:text-gray-50'
> >
View All Guides &nbsp;&rarr; View All Guides &nbsp;&rarr;
</a> </a>
</div>
</div> </div>

@ -0,0 +1,35 @@
---
import type { VideoFileType } from '../lib/video';
import VideoListItem from './VideoListItem.astro';
export interface Props {
heading: string;
videos: VideoFileType[];
}
const { heading, videos } = Astro.props;
---
<div class='container'>
<h1 class='text-2xl sm:text-3xl font-bold block'>{heading}</h1>
<div class='mt-3 sm:my-5'>
{videos.map((video) => <VideoListItem video={video} />)}
</div>
<a
href='/videos'
class='hidden sm:inline transition-colors py-2 px-3 text-xs font-medium rounded-full bg-gradient-to-r from-slate-600 to-black hover:from-blue-600 hover:to-blue-800 text-white'
>
View All Videos &rarr;
</a>
<div class='block sm:hidden mt-3'>
<a
href='/videos'
class='text-sm font-regular block p-2 border border-black text-black rounded-md text-center hover:bg-black hover:text-gray-50'
>
View All Videos &nbsp;&rarr;
</a>
</div>
</div>

@ -0,0 +1,40 @@
---
import type { VideoFileType } from "../lib/video";
export interface Props {
video: VideoFileType;
}
const { video } = Astro.props;
const { frontmatter, id } = video;
---
<a
class:list={[
"block no-underline py-2 group text-md items-center text-gray-600 hover:text-blue-600 flex justify-between border-b",
]}
href={`/videos/${id}`}
>
<span class="group-hover:translate-x-2 transition-transform">
{frontmatter.title}
{
frontmatter.isNew && (
<span class="bg-green-300 text-green-900 text-xs font-medium px-1.5 py-0.5 rounded-sm uppercase ml-1.5">
New
<span class="hidden sm:inline">
&middot;
{new Date(frontmatter.date).toLocaleString("default", {
month: "long",
})}
</span>
</span>
)
}
</span>
<span class="capitalize text-gray-500 text-xs hidden sm:block">
{frontmatter.duration}
</span>
<span class="text-gray-400 text-xs block sm:hidden"> &raquo;</span>
</a>

@ -40,7 +40,7 @@ Now that we know what basic authentication is, the question is, how does it work
### Step 1 ### Step 1
When the browser first requests the server, the server tries to check the availability of the `Authorization` header in the request. Because it is the first request, no `Authorization` header is found in the request. So the server responds with the `401 Unauthorized` response code and also sends the `WWW-Authenticate` header with the value set to `Basic`, which tells the browser that it needs to trigger the basic authentication flow. When the browser first requests the server, the server tries to check the availability of the `Authorization` header in the request. Because it is the first request, no `Authorization` header is found in the request. So the server responds with the `401 Unauthorized` response code and also sends the `WWW-Authenticate` header with the value set to `Basic`, which tells the browser that it needs to trigger the basic authentication flow.
```text ```plaintext
401 Unauthorized 401 Unauthorized
WWW-Authenticate: Basic realm='user_pages' WWW-Authenticate: Basic realm='user_pages'
``` ```

@ -0,0 +1,61 @@
import type { MarkdownFileType } from './file';
export interface VideoFrontmatter {
title: string;
description: string;
author: {
name: string;
url: string;
imageUrl: string;
};
seo: {
title: string;
description: string;
};
isNew: boolean;
duration: string;
date: string;
sitemap: {
priority: number;
changefreq: 'daily' | 'weekly' | 'monthly' | 'yealry';
};
tags: string[];
}
export type VideoFileType = MarkdownFileType<VideoFrontmatter> & {
id: string;
};
/**
* Generates id from the given video file
* @param filePath Markdown file path
*
* @returns unique video identifier
*/
function videoPathToId(filePath: string): string {
const fileName = filePath.split('/').pop() || '';
return fileName.replace('.md', '');
}
/**
* Gets all the videos sorted by the publishing date
* @returns Promisifed video files
*/
export async function getAllVideos(): Promise<VideoFileType[]> {
const videos = await import.meta.glob<VideoFileType>('/src/videos/*.md', {
eager: true,
});
const videoFiles = Object.values(videos);
const enrichedVideos = videoFiles.map((videoFile) => ({
...videoFile,
id: videoPathToId(videoFile.file),
}));
return enrichedVideos.sort(
(a, b) =>
new Date(b.frontmatter.date).valueOf() -
new Date(a.frontmatter.date).valueOf()
);
}

@ -1,13 +1,16 @@
--- ---
import FeaturedGuides from '../components/FeaturedGuides.astro'; import FeaturedGuides from '../components/FeaturedGuides.astro';
import FeaturedRoadmaps from '../components/FeaturedRoadmaps/FeaturedRoadmaps.astro'; import FeaturedRoadmaps from '../components/FeaturedRoadmaps/FeaturedRoadmaps.astro';
import FeaturedVideos from '../components/FeaturedVideos.astro';
import BaseLayout from '../layouts/BaseLayout.astro'; import BaseLayout from '../layouts/BaseLayout.astro';
import { getAllGuides } from '../lib/guide'; import { getAllGuides } from '../lib/guide';
import { getRoadmapsByTag } from '../lib/roadmap'; import { getRoadmapsByTag } from '../lib/roadmap';
import { getAllVideos } from '../lib/video';
const roleRoadmaps = await getRoadmapsByTag('role-roadmap'); const roleRoadmaps = await getRoadmapsByTag('role-roadmap');
const skillRoadmaps = await getRoadmapsByTag('skill-roadmap'); const skillRoadmaps = await getRoadmapsByTag('skill-roadmap');
const guides = await getAllGuides(); const guides = await getAllGuides();
const videos = await getAllVideos();
--- ---
<BaseLayout title='Developer Roadmaps'> <BaseLayout title='Developer Roadmaps'>
@ -39,9 +42,8 @@ const guides = await getAllGuides();
<FeaturedRoadmaps heading='Skill based Roadmaps' roadmaps={skillRoadmaps} /> <FeaturedRoadmaps heading='Skill based Roadmaps' roadmaps={skillRoadmaps} />
<div class='grid grid-cols-1 gap-7 sm:gap-16 bg-gray-50 py-7 sm:py-16'> <div class='grid grid-cols-1 gap-7 sm:gap-16 bg-gray-50 py-7 sm:py-16'>
<div class='container'>
<FeaturedGuides heading='Guides' guides={guides.slice(0, 7)} /> <FeaturedGuides heading='Guides' guides={guides.slice(0, 7)} />
</div> <FeaturedVideos heading='Videos' videos={videos.slice(0, 7)} />
</div> </div>
</div> </div>
</BaseLayout> </BaseLayout>

@ -0,0 +1,23 @@
---
import VideoListItem from '../../components/VideoListItem.astro';
import SimplePageHeader from '../../components/SimplePageHeader.astro';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { getAllVideos } from '../../lib/video';
const guides = await getAllGuides();
---
<BaseLayout title='Guides'>
<SimplePageHeader
title='Guides'
description='Succinct graphical explanations to engineering topics.'
/>
<div class='pb-20 pt-2 bg-gray-50'>
<div class='container'>
<div class='mt-3 sm:my-5'>
{guides.map((guide) => <GuideListItem guide={guide} />)}
</div>
</div>
</div>
</BaseLayout>
Loading…
Cancel
Save