Skip to content

Commit

Permalink
Feat/add language on sources (#62)
Browse files Browse the repository at this point in the history
* feat: add language set up

* chore: fix build error for image types and source breadcrumbs

* fix: remove language slug from breadcrumbs, fix route definition

* fix: remove duplicated declared constants

* fix: remove unused type declaration

* grammatical fixes and code style
  • Loading branch information
IgboPharaoh authored Dec 8, 2024
1 parent de54cad commit b217503
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 115 deletions.
207 changes: 122 additions & 85 deletions src/app/(explore)/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import { ContentTreeArray } from "@/utils/data";
import LinkIcon from "/public/svgs/link-icon.svg";
import WorldIcon from "/public/svgs/world-icon.svg";
import allSources from "@/public/sources-data.json";
import { constructSlugPaths, fetchTranscriptDetails, loopArrOrObject } from "@/utils";
import { constructSlugPaths, deriveSourcesList, fetchTranscriptDetails, loopArrOrObject } from "@/utils";
import { ArrowLinkRight } from "@bitcoin-dev-project/bdp-ui/icons";
import { allSources as allContentSources, allTranscripts } from "contentlayer/generated";
import TranscriptDetailsCard from "@/components/common/TranscriptDetailsCard";
import { SourcesBreadCrumbs } from "@/components/explore/SourcesBreadCrumbs";
import TranscriptContentPage from "@/components/explore/TranscriptContentPage";
import { LanguageCodes } from "@/config";

// forces 404 for paths not generated from `generateStaticParams` function.
export const dynamicParams = false;

export function generateStaticParams() {
const languageSlugs = LanguageCodes.map((lang) => ({ slug: [lang, "sources"] }));

const allSlugs = allContentSources.map(({ language, slugAsParams }) => {
const isEnglish = language === "en";
if (isEnglish) {
Expand All @@ -28,105 +32,138 @@ export function generateStaticParams() {
};
});

return allSlugs;
const combineSlugs = [...languageSlugs, ...allSlugs];

return combineSlugs;
}

const page = ({ params }: { params: { slug: string[] } }) => {
const slug = params.slug ?? [];
const contentTree = allSources;
let current: any = contentTree;
const { slugPaths } = constructSlugPaths(slug);
let current: any = contentTree;
let currentLanguageSource: any = allSources;
let languageTreeArray: { slug: string; name: string; count: number }[] = [];

const isRouteForLanguage = slug.length === 2 && LanguageCodes.includes(slug[0]) && slug[1] === "sources";

if (isRouteForLanguage) {
const language = slug[0];
const sourcesAndLanguage = Object.keys(allSources);
const languageTree: any = {};

for (const part of slugPaths) {
if (typeof current === "object" && !Array.isArray(current) && part in current) {
current = current[part];
} else {
notFound();
sourcesAndLanguage.forEach((source) => {
const getSource = currentLanguageSource[source];
if (getSource[language]) {
languageTree[source] = getSource[language];
}
});

languageTreeArray = deriveSourcesList(languageTree);

return (
<div className='flex flex-col text-black'>
<TranscriptContentPage
header='Sources'
data={languageTreeArray}
description='Sources help you find transcripts within a specific talk, meetup, conference, and the likes.'
type='alphabet'
linkName={language}
/>
</div>
);
} else {
for (const part of slugPaths) {
if (typeof current === "object" && !Array.isArray(current) && part in current) {
current = current[part];
} else {
notFound();
}
}
}

const metadata = current.metadata;
const data = loopArrOrObject(current?.data);
const isRoot = Array.isArray(current.data);
const { transcripts } = fetchTranscriptDetails(allTranscripts, data, isRoot);

return (
<div className='flex items-start lg:gap-[50px]'>
<div className='flex flex-col w-full gap-6 md:gap-8 2xl:gap-10 no-scrollbar'>
<div
className={`flex flex-col ${
isRoot ? "border-b border-b-[#9B9B9B] pb-6 md:border-b-0 md:pb-0" : "border-b border-b-[#9B9B9B] pb-6 lg:pb-10"
} gap-5 2xl:gap-6`}
>
<>
<SourcesBreadCrumbs slugPaths={slugPaths} current={contentTree} />
</>
<div className='flex flex-col'>
<Link href={slug.slice(0, -1).join("/") === "" ? `/sources` : `/${slug.slice(0, -1).join("/")}`} className='flex gap-1 items-center'>
<ArrowLinkRight className='rotate-180 w-5 md:w-6' />
<p>Back</p>
</Link>

<h3 className='text-xl 2xl:text-2xl font-medium pt-6 md:pt-3'>{metadata?.title ?? slug[slug.length - 1]}</h3>
{isRoot && metadata?.website ? (
<div className='flex gap-1 items-center pt-3 md:pt-6'>
<Image src={WorldIcon} alt='world icon' className='w-[18px] md:w-[20px]' />
<Link
href={metadata?.website ?? ""}
target='_blank'
className='text-xs md:text-sm xl:text-base leading-[17.6px] font-medium text-black underline text-wrap break-words line-clamp-1'
>
{metadata.website ?? ""}
</Link>
</div>
) : null}

{isRoot && metadata?.additional_resources ? (
<div className='flex gap-1 items-center pt-3 md:pt-6'>
<Image src={LinkIcon} alt='link icon' className='w-[18px] md:w-[20px]' />
<div className='flex gap-1 flex-wrap'>
{metadata.additional_resources.map((resource: any, index: number) => (
<Link
href={resource.url ?? ""}
key={`${resource.title}-${index}`}
target='_blank'
className='py-[2px] px-4 rounded-[5px] bg-custom-white-custom-100 text-base leading-[21.86px] font-medium max-md:px-3 lg:py-1 max-2xl:text-sm max-md:text-sm border border-gray-custom-1500 max-md:leading-[100%] cursor-pointer'
>
{resource.title}
</Link>
))}
const metadata = current.metadata;
const data = loopArrOrObject(current?.data);
const isRoot = Array.isArray(current.data);
const { transcripts } = fetchTranscriptDetails(allTranscripts, data, isRoot);

return (
<div className='flex items-start lg:gap-[50px]'>
<div className='flex flex-col w-full gap-6 md:gap-8 2xl:gap-10 no-scrollbar'>
<div
className={`flex flex-col ${
isRoot ? "border-b border-b-[#9B9B9B] pb-6 md:border-b-0 md:pb-0" : "border-b border-b-[#9B9B9B] pb-6 lg:pb-10"
} gap-5 2xl:gap-6`}
>
<>
<SourcesBreadCrumbs slugPaths={slugPaths} current={contentTree} />
</>
<div className='flex flex-col'>
<Link href={slug.slice(0, -1).join("/") === "" ? `/sources` : `/${slug.slice(0, -1).join("/")}`} className='flex gap-1 items-center'>
<ArrowLinkRight className='rotate-180 w-5 md:w-6' />
<p>Back</p>
</Link>

<h3 className='text-xl 2xl:text-2xl font-medium pt-6 md:pt-3'>{metadata?.title ?? slug[slug.length - 1]}</h3>
{isRoot && metadata?.website ? (
<div className='flex gap-1 items-center pt-3 md:pt-6'>
<Image src={WorldIcon} alt='world icon' className='w-[18px] md:w-[20px]' />
<Link
href={metadata?.website ?? ""}
target='_blank'
className='text-xs md:text-sm xl:text-base leading-[17.6px] font-medium text-black underline text-wrap break-words line-clamp-1'
>
{metadata.website ?? ""}
</Link>
</div>
</div>
) : null}
</div>
</div>
) : null}

{isRoot ? (
<div className='flex flex-col gap-6 h-full pb-8 overflow-scroll'>
{(transcripts as ContentTreeArray[]).map((item, i) => (
<TranscriptDetailsCard key={i} slug={slug} data={item} />
))}
{isRoot && metadata?.additional_resources ? (
<div className='flex gap-1 items-center pt-3 md:pt-6'>
<Image src={LinkIcon} alt='link icon' className='w-[18px] md:w-[20px]' />
<div className='flex gap-1 flex-wrap'>
{metadata.additional_resources.map((resource: any, index: number) => (
<Link
href={resource.url ?? ""}
key={`${resource.title}-${index}`}
target='_blank'
className='py-[2px] px-4 rounded-[5px] bg-custom-white-custom-100 text-base leading-[21.86px] font-medium max-md:px-3 lg:py-1 max-2xl:text-sm max-md:text-sm border border-gray-custom-1500 max-md:leading-[100%] cursor-pointer'
>
{resource.title}
</Link>
))}
</div>
</div>
) : null}
</div>
</div>
) : (
<div className='flex-col flex gap-10 overflow-scroll pb-8'>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-2.5 '>
{(data as any[]).map((value, i) => (
<Link
key={`${value.route}-${i}}`}
href={`/${[...slug, value.route].join("/")}`}
className='flex capitalize cursor-pointer border max-w-[100%] border-gray-custom-1200 rounded-[5px] justify-between items-center text-sm py-5 px-4 lg:py-7 2xl:px-6 2xl:text-lg font-semibold text-gray-custom-1100'
>
<span className='text-wrap break-words max-w-[80%]'>{value.title}</span>
<span>{value.count}</span>
</Link>

{isRoot ? (
<div className='flex flex-col gap-6 h-full pb-8 overflow-scroll'>
{(transcripts as ContentTreeArray[]).map((item, i) => (
<TranscriptDetailsCard key={i} slug={slug} data={item} />
))}
</div>
</div>
)}
) : (
<div className='flex-col flex gap-10 overflow-scroll pb-8'>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-2.5 '>
{(data as any[]).map((value, i) => (
<Link
key={`${value.route}-${i}}`}
href={`/${[...slug, value.route].join("/")}`}
className='flex capitalize cursor-pointer border max-w-[100%] border-gray-custom-1200 rounded-[5px] justify-between items-center text-sm py-5 px-4 lg:py-7 2xl:px-6 2xl:text-lg font-semibold text-gray-custom-1100'
>
<span className='text-wrap break-words max-w-[80%]'>{value.title}</span>
<span>{value.count}</span>
</Link>
))}
</div>
</div>
)}
</div>
<div className='hidden lg:flex sticky top-0 flex-shrink-0 w-fit lg:justify-center 2xl:min-w-[100px] xl:min-w-[200px]'></div>
</div>
<div className='hidden lg:flex sticky top-0 flex-shrink-0 w-fit lg:justify-center 2xl:min-w-[100px] xl:min-w-[200px]'></div>
</div>
);
);
}
};

export default page;
7 changes: 3 additions & 4 deletions src/components/explore/GroupedTranscriptContent.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
"use client";
import React, { useEffect } from "react";
import React from "react";
import SingleTranscriptContent from "./SingleTranscriptContent";
import { createSlug, DepreciatedCategories, TopicsData } from "@/utils";
import { createSlug, TopicsData } from "@/utils";
import { useInView } from "react-intersection-observer";
import { root } from "postcss";

interface IGroupedTranscriptContent {
topicsByAlphabet: [string, TopicsData[]];
setCurrentGroup: React.Dispatch<React.SetStateAction<string>>;
linkName: DepreciatedCategories;
linkName: string;
type: "alphabet" | "words";
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/explore/SingleTranscriptContent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DepreciatedCategories, getDoubleDigits, TopicsData } from "@/utils";
import { getDoubleDigits, TopicsData } from "@/utils";
import Link from "next/link";
import React from "react";

type SingleContent = {
linkName: DepreciatedCategories;
linkName: string;
} & TopicsData;
const SingleTranscriptContent = ({ count, slug, name, linkName }: SingleContent) => {
const url = linkName === "sources" ? `/${slug}` : `/${linkName}/${slug}`;
Expand Down
9 changes: 6 additions & 3 deletions src/components/explore/SourcesBreadCrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ export const SourcesBreadCrumbs = ({ slugPaths, current }: { slugPaths: string[]
const pathnameArray = pathname.replace(`/${language}`, "").split("/");
const isNotSourcesPage = navListWithoutSources.includes(pathnameArray[1]);

const isEnglishSlug = language === "en";
const allRoutes = pathnameArray.map((path, idx) => {
const route = pathname
.split("/")
.slice(0, idx + 1)
// we use the isEnglishSlug to determine the number of slugs to be sliced to appropriately create the path route.
.slice(0, idx + (isEnglishSlug ? 1 : 2))
.join("/");
return { name: path || "home", link: route || "/" };

return { name: path || "home", link: idx === 0 ? "/" : route || "/" };
});

if (!isNotSourcesPage && pathnameArray[1] !== "sources") {
allRoutes.splice(1, 0, { name: "Sources", link: "/sources" });
allRoutes.splice(1, 0, { name: "Sources", link: `${isEnglishSlug ? "/sources" : `/${language}/sources`}` });
}

const breadCrumbData = () => {
Expand Down
11 changes: 8 additions & 3 deletions src/components/explore/TranscriptContentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import GroupedTranscriptContent from "./GroupedTranscriptContent";
import AlphabetGrouping from "./AlphabetGrouping";
import {
createSlug,
DepreciatedCategories,
groupDataByAlphabet,
GroupedData,
sortKeysAlphabetically,
Expand All @@ -14,14 +13,15 @@ import MobileAlphabetGrouping from "./MobileAlphabetGrouping";
import ContentGrouping from "./ContentGrouping";
import BaseCrumbLists from "../common/BaseCrumbLists";
import { usePathname } from "next/navigation";
import { LanguageCodes } from '@/config';

interface ITranscriptContentPage {
header: string;
description?: string;
mobileDescription?: string;
data: any /* A Json that changes could be topics, speakers, categories */;
type: "alphabet" | "words";
linkName: DepreciatedCategories;
linkName: string;
}

const TranscriptContentPage: FC<ITranscriptContentPage> = ({
Expand All @@ -43,7 +43,8 @@ const TranscriptContentPage: FC<ITranscriptContentPage> = ({

const pathname = usePathname();
const pathnameArray = pathname.split("/");
const allRoutes = pathnameArray.map((path, idx) => {

const routes = pathnameArray.map((path, idx) => {
const route = pathname
.split("/")
.slice(0, idx + 1)
Expand All @@ -55,6 +56,10 @@ const TranscriptContentPage: FC<ITranscriptContentPage> = ({
isActive: idx === pathnameArray.length - 1,
};
});

// remove language codes from paths passed to breadcrumbs
const allRoutes = routes.filter((route) => !LanguageCodes.includes(route.name))

return (
<div className="flex items-start relative lg:gap-[50px]">
<div className="flex flex-col w-full gap-6 lg:gap-10 no-scrollbar">
Expand Down
Loading

0 comments on commit b217503

Please sign in to comment.