Skip to content

Commit

Permalink
✨ Add catalog (summary and paginated)
Browse files Browse the repository at this point in the history
  • Loading branch information
toridoriv committed Oct 11, 2023
1 parent 1571dae commit 65a0df3
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 78 deletions.
57 changes: 57 additions & 0 deletions app/catalog/catalog-paginated.view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { EndpointView, LiteFanfictionOutput } from "@common";
import { z } from "@deps";

const CatalogItemSchema = z.custom<LiteFanfictionOutput>();
const CatalogPageSchema = z.object({
href: z.string(),
isActive: z.boolean().default(false),
index: z.number().int(),
});

const options = {
projection: {
_id: 0,
chapters: 0,
kind: 0,
},
limit: 5,
sort: { created_at: -1 as const },
};

export default EndpointView.init({
method: "get",
path: "/fanfiction-catalog/pages/:page",
view: "catalog-paginated",
payload: {
params: z.object({
page: z.coerce.number().int(),
}),
},
context: z.object({
fanfictions: z.array(CatalogItemSchema),
pages: z.array(CatalogPageSchema),
currentPage: z.number().int(),
}),
}).registerHandler(async function main(req, res) {
const total = await res.app.fanfics.countDocuments();
const totalPages = Math.ceil(total / options.limit);
const currentPage = req.params.page;
const pages = Array.from(
{ length: totalPages },
(_, index) =>
CatalogPageSchema.parse({
href: this.path.replace(":page", String(index + 1)),
isActive: currentPage === index + 1,
index: index + 1,
}),
);
const skip = currentPage === 1
? 0
: currentPage * options.limit - options.limit;
const fanfictions = await res.app.fanfics
.find({}, options)
.skip(skip)
.toArray();

return this.renderOk(res, { fanfictions, pages, currentPage });
});
48 changes: 48 additions & 0 deletions app/catalog/catalog-summary.view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { EndpointView, LiteFanfictionOutput } from "@common";
import { z } from "@deps";

const CatalogSummarySchema = z.custom<LiteFanfictionOutput>();

const limit = 3;
const projection = {
_id: 0,
chapters: 0,
kind: 0,
};

const recentlyAddedOptions = {
limit,
projection,
sort: { created_at: -1 as const },
};

const recentlyUpdatedQuery = {
$expr: { $gt: ["$updated_at", "$created_at"] },
};

const recentlyUpdatedOptions = {
limit,
projection,
sort: {
updated_at: -1 as const,
},
};

export default EndpointView.init({
method: "get",
path: "/",
view: "catalog-summary",
context: z.object({
recentlyAdded: z.array(CatalogSummarySchema),
recentlyUpdated: z.array(CatalogSummarySchema),
}),
}).registerHandler(async function main(_req, res) {
const recentlyAdded = await res.app.fanfics
.find({}, recentlyAddedOptions)
.toArray();
const recentlyUpdated = await res.app.fanfics
.find(recentlyUpdatedQuery, recentlyUpdatedOptions)
.toArray();

return this.renderOk(res, { recentlyAdded, recentlyUpdated });
});
54 changes: 54 additions & 0 deletions app/catalog/catalog.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
getAuthorTag,
getFandomTag,
getLanguageTag,
getOriginTag,
getRelationshipTag,
type LiteFanfictionOutput,
type Tag,
type TextOutput,
type TextWithTranslationsOutput,
} from "@common";

export function getTags(fanfiction: LiteFanfictionOutput) {
const tags: Tag[] = [getAuthorTag(fanfiction.author.name)];

if (fanfiction.fandom) {
tags.push(getFandomTag(fanfiction.fandom));
}

if (fanfiction.relationship) {
tags.push(getRelationshipTag(fanfiction.relationship));
}

tags.push(getLanguageTag(fanfiction.language_code));
tags.push(getOriginTag(fanfiction.source));

return tags;
}

export function getTextToDisplay(text: TextOutput | TextOutput[]) {
if (typeof text === "undefined") {
return;
}

if (Array.isArray(text)) {
if (text.length === 0) {
return;
}

return text[0].rich || text[0].raw;
}

return text.rich || text.raw;
}

export function getMainTranslation(localized: TextWithTranslationsOutput) {
return localized.translations[0];
}

export function isCurrentPage(page: number) {
const currentPage = window.location.pathname.split("/").reverse()[0];

return Number(currentPage) === page;
}
2 changes: 1 addition & 1 deletion app/core/fanfiction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export const LiteFanfictionSchema = z.object({
created_at: z.coerce.date().default(createTimestamp),
updated_at: z.coerce.date().default(createTimestamp),
author: AuthorSchema,
kind: z.literal("fanfiction").default("fanfiction").catch("fanfiction"),
origin_id: z.coerce.string().min(1),
origin_url: z.string().url(),
source: z.string().default(""),
Expand All @@ -84,6 +83,7 @@ export const LiteFanfictionSchema = z.object({
});

export const RawFanfictionSchema = LiteFanfictionSchema.extend({
kind: z.literal("fanfiction").default("fanfiction").catch("fanfiction"),
chapters: z.array(ChapterSchema).default([]),
});

Expand Down
44 changes: 14 additions & 30 deletions app/core/tag.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FanfictionOutput } from "./fanfiction.ts";
import { z } from "@deps";
import { getLanguageName, LanguageCode } from "./localization.ts";

/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -36,13 +36,19 @@ export const ICON_BY_TAG_NAME = Object.freeze({
[TagName.Author]: "user",
});

export type Tag = {
href: string;
icon: typeof ICON_BY_TAG_NAME[TagName];
name: TagName;
value: string;
total?: number;
};
export type Tag = z.output<typeof TagSchema>;

export const TagSchema = z.object({
href: z.string().default(""),
icon: z.nativeEnum(ICON_BY_TAG_NAME),
name: z.nativeEnum(TagName),
value: z.string(),
total: z.number().optional(),
}).transform((tag) => {
tag.href = getTagHref(tag.name, tag.value);

return tag;
});

export function getTagHref(name: TagName, ...values: string[]) {
return `/fanfictions?${QUERY_BY_TAG_NAME[name]}${
Expand Down Expand Up @@ -109,28 +115,6 @@ export function getAuthorTag(
};
}

export function getAllAvailableTagsForFanfiction(
fanfiction: FanfictionOutput,
): Tag[] {
const tags: Tag[] = [getAuthorTag(fanfiction.author.name)];

if (fanfiction.fandom) {
tags.push(getFandomTag(fanfiction.fandom));
}

if (fanfiction.relationship) {
tags.push(getRelationshipTag(fanfiction.relationship));
}

tags.push(getLanguageTag(fanfiction.language_code));

const url = new URL(fanfiction.origin_url);

tags.push(getOriginTag(url.hostname));

return tags;
}

export function sortTagsDescending(tags: Tag[]) {
return (tags as Required<Tag>[]).sort(isMoreFrequent);
}
Expand Down
43 changes: 0 additions & 43 deletions app/home/home.endpoint.ts

This file was deleted.

12 changes: 12 additions & 0 deletions app/views/catalog-paginated.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{#each fanfictions}}
{{> fanfiction-card . }}
{{/each}}
<nav aria-label="Navigation">
<ul class="pagination">
{{#each pages}}
<li class="page-item{{#if isActive}} active{{/if}}">
<a href="{{href}}" class="page-link">{{index}}</a>
</li>
{{/each}}
</ul>
</nav>
File renamed without changes.
5 changes: 3 additions & 2 deletions app/views/layouts/main.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="icon" type="image/x-icon" sizes="any" href="/favicon.ico">
<link rel="manifest" href="/site.webmanifest">
<link rel="stylesheet"
<link rel="stylesheet" rel="preload"
href="https://cdn.jsdelivr.net/npm/halfmoon@2.0.1/css/halfmoon.min.css">
<link rel="preload"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
Expand All @@ -41,7 +41,8 @@
<a class="nav-link" href="/">🏠 Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/fanfictions/pages/1">📚 Fanfictions</a>
<a class="nav-link" href="/fanfiction-catalog/pages/1">📚
Catalog</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/tags">🏷️ Tags</a>
Expand Down
6 changes: 4 additions & 2 deletions app/views/public/styles/main.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* @import "https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;600;800;900&display=swap"; */

:root {
--bs-success: var(--bs-emerald-700);
}

.pagination {
--bs-pagination-active-bg: var(--bs-emerald-700);
}

nav.navbar {
margin-bottom: 2em;
}
Expand Down

0 comments on commit 65a0df3

Please sign in to comment.