Skip to content

Commit

Permalink
feat: ✅ add search for release & caching on CF workers
Browse files Browse the repository at this point in the history
  • Loading branch information
abhagsain committed Sep 14, 2022
1 parent 416f41b commit b98f72b
Show file tree
Hide file tree
Showing 24 changed files with 2,911 additions and 924 deletions.
15 changes: 13 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
{
"extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node"]
}
"extends": [
"@remix-run/eslint-config",
"@remix-run/eslint-config/node"
],
"settings": {
"files": [
"**/*.js",
"**/*.jsx",
"**/*.ts",
"**/*.tsx"
]
}
}
2 changes: 1 addition & 1 deletion app/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type Props = {

function Layout({ children }: Props) {
return (
<div className="prose max-w-full prose-img:rounded-xl prose-headings:underline prose-a:text-cyan-500 dark:prose-invert">
<div className="max-w-full prose prose-img:rounded-xl prose-headings:underline prose-a:text-cyan-500 dark:prose-invert">
{children}
</div>
);
Expand Down
72 changes: 72 additions & 0 deletions app/api.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { marked } from "marked";
import type { IChangeLog, SearchFormData } from "./types";
import { getJSON } from "./utils";
const WORKER_ENDPOINT = process.env.WORKER_URL || "http://localhost:8787/api";

export const getNameAndURLFromPackageName = async (name: string) => {
const API_ENDPOINT = `${WORKER_ENDPOINT}/package/${name}`;
const res = await fetch(API_ENDPOINT).then((res) => res.json());
const { ownerName, repoName } = res;
return { ownerName, repoName };
};

export const getReleaseTags = async (
ownerName: string,
repoName: string,
perPage = 100,
page = 1
): Promise<IChangeLog[] | Response> => {
const URL = `${WORKER_ENDPOINT}/repos/${ownerName}/${repoName}/tags?per_page=${perPage}&page=${page}`;
const data = await getJSON(URL);
return data;
};

export const getReleaseInfo = async (
name: string,
versions: SearchFormData["versions"]
) => {
const { ownerName, repoName } = (await getNameAndURLFromPackageName(
name
)) as { ownerName: string; repoName: string };
const versionPromise = versions.map((version) =>
getTagByVersion(ownerName, repoName, version)
);
const result = await Promise.allSettled(versionPromise).then((res) => {
return res
.filter((r) => r.status === "fulfilled")
.map((r) =>
r.status === "fulfilled"
? {
...(r.value as IChangeLog),
html: marked((r.value as IChangeLog).body),
}
: undefined
);
});
const releaseTagsList = (await getReleaseTags(
ownerName,
repoName
)) as IChangeLog[];
return {
ownerName,
repoName,
releaseTagsList,
changeLogs: result,
};
};

export const getChangelogList = async (values: SearchFormData[]) => {
const releases = await Promise.all(
values.map(({ name, versions }) => getReleaseInfo(name, versions))
);
return releases;
};

export const getTagByVersion = async (
authorName: string,
repoName: string,
tagName: string
): Promise<IChangeLog> => {
const API_URL = `${WORKER_ENDPOINT}/repos/${authorName}/${repoName}/releases/tags/${tagName}`;
return getJSON(API_URL);
};
65 changes: 0 additions & 65 deletions app/api.ts

This file was deleted.

91 changes: 0 additions & 91 deletions app/components/Autcomplete.tsx

This file was deleted.

31 changes: 31 additions & 0 deletions app/components/Changelog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { IChangeLog } from "../types";

interface IChangelogProps {
changeLog?: IChangeLog;
}

const Changelog = ({ changeLog }: IChangelogProps) => {
if (!changeLog) {
return (
<div className="flex items-center justify-center w-full max-w-3xl">
<h3>Couldn't find info about this release </h3>
</div>
);
}

const { author, name, tag_name, published_at } = changeLog;
const formattedDate = new Date(published_at).toLocaleDateString();
return (
<article>
<h3>{name || tag_name} </h3>
<div className="flex items-center space-x-1 text-white/60">
<img src={author.avatar_url} alt="" className="w-5 h-5 m-0" />
<p className="my-1">{author.login}</p>
<p className="my-1">released this {formattedDate}</p>
</div>
<div dangerouslySetInnerHTML={{ __html: changeLog?.html }} />
</article>
);
};

export default Changelog;
89 changes: 89 additions & 0 deletions app/components/ChangelogList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import useChangeLogState from "../hooks/useChangelogState";
import type { AutocompleteOption, LoaderData } from "../types";
import Changelog from "./Changelog";
import MultiSelect from "./MultiSelect/MultiSelect";

const ChangeLogList = ({ changeLogList }: { changeLogList: LoaderData }) => {
const { setSelectedReleases, updateURLSearchParams } = useChangeLogState();

const scrollToItem = (name: string) => {
const item = document.getElementById(`#${name.toLowerCase()}`);
item?.scrollIntoView(true);
};

const renderNav = () => {
if (changeLogList.length <= 1) return null;
return (
<nav className="sticky top-0 z-10 flex flex-wrap px-8 space-x-4 backdrop-blur">
{changeLogList.map((release) => (
<div
key={`nav-${release.repoName}`}
onClick={() => scrollToItem(release.repoName)}
className="py-2 text-lg cursor-pointer hover:text-cyan-400"
>
{release.repoName}
</div>
))}
</nav>
);
};

return (
<div className="space-y-4">
{renderNav()}
<section className="flex flex-col justify-start space-y-10">
{changeLogList?.map((changeLog) => {
const repoName = changeLog.repoName;
const options = changeLog.releaseTagsList.map((tag) => ({
label: tag.name,
value: tag.name,
}));
const defaultChangeLogVersions = [
...changeLog.changeLogs.map((log) => ({
label: log?.tag_name,
value: log?.tag_name,
})),
];

return changeLog.changeLogs ? (
<div id={`#${changeLog.repoName.toLowerCase()}`}>
<div className="flex items-center justify-between px-8 space-x-4">
<div className="flex items-center">
<h2 className="my-4">{repoName}</h2>
</div>
<div className="flex items-center space-x-3">
<MultiSelect
isMulti
name="versions"
id="versions"
options={options}
onChange={(values) => {
setSelectedReleases(values as AutocompleteOption[]);
}}
defaultValue={defaultChangeLogVersions}
isSearchable
/>
<button
type="submit"
onClick={() => {
updateURLSearchParams(repoName);
}}
>
Search
</button>
</div>
</div>
<div className="grid grid-cols-1 gap-4 px-8 lg:grid-cols-2">
{changeLog.changeLogs.map((release) => (
<Changelog changeLog={release} key={release?.node_id} />
))}
</div>
</div>
) : null;
})}
</section>
</div>
);
};

export default ChangeLogList;
18 changes: 18 additions & 0 deletions app/components/Label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Label = ({
children,
htmlFor,
...args
}: {
children: string;
htmlFor: string;
}) => (
<label
htmlFor={htmlFor}
className="block text-sm font-medium text-gray-700 dark:text-white/95"
{...args}
>
{children}
</label>
);

export default Label;
Loading

0 comments on commit b98f72b

Please sign in to comment.