@@ -0,0 +1,174 @@
|
||||
import {capitalize, sendApiGet, TesoftComponent} from "../scripts/main.ts";
|
||||
import {TesoftLoaderSection} from "./loader-section.ts";
|
||||
import {TesoftPagination} from "./pagination.ts";
|
||||
import {TesoftError} from "./error.ts";
|
||||
import {TesoftTag} from "./tag.ts";
|
||||
import {formatArticleDate, getBadgeText} from "./blog-article.ts";
|
||||
import {TesoftBadge} from "./badge.ts";
|
||||
|
||||
export class TesoftBlogArticleList extends TesoftComponent {
|
||||
private readonly listItemTemplate: HTMLTemplateElement;
|
||||
private readonly loaderSection: TesoftLoaderSection;
|
||||
private readonly listDiv: HTMLDivElement;
|
||||
private readonly tagsDiv: HTMLDivElement;
|
||||
private readonly pagination: TesoftPagination;
|
||||
private readonly error: TesoftError;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const template = document.getElementById("tesoft-blog-article-list-template") as HTMLTemplateElement;
|
||||
const templateContent = template.content;
|
||||
|
||||
const shadowRoot = this.attachShadow({mode: "open"});
|
||||
shadowRoot.appendChild(templateContent.cloneNode(true));
|
||||
|
||||
this.listItemTemplate = document.getElementById("tesoft-blog-article-list-item-template") as HTMLTemplateElement;
|
||||
this.loaderSection = shadowRoot.querySelector<TesoftLoaderSection>("tesoft-loader-section")!;
|
||||
this.listDiv = shadowRoot.querySelector<HTMLDivElement>(".list")!;
|
||||
this.tagsDiv = shadowRoot.querySelector<HTMLDivElement>(".tags")!;
|
||||
this.pagination = shadowRoot.querySelector<TesoftPagination>("tesoft-pagination")!;
|
||||
this.error = shadowRoot.querySelector<TesoftError>("tesoft-error")!;
|
||||
}
|
||||
|
||||
async load(page: number, tags: string[], onParametersChange: (page: number, tags: string[]) => void) {
|
||||
try {
|
||||
this.tagsDiv.innerHTML = "";
|
||||
for (const tagName of await this.fetchTags()) {
|
||||
const tag = document.createElement("tesoft-tag") as TesoftTag;
|
||||
tag.textContent = tagName;
|
||||
this.tagsDiv.appendChild(tag);
|
||||
}
|
||||
} catch (error: any) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
this.pagination.addEventListener("update-page", (e) => {
|
||||
const page = (e as CustomEvent).detail.page;
|
||||
onParametersChange(page, tags);
|
||||
this.reload(page, tags, onParametersChange);
|
||||
});
|
||||
|
||||
await this.reload(page, tags, onParametersChange);
|
||||
}
|
||||
|
||||
private async reload(page: number, tags: string[], onParametersChange: (page: number, tags: string[]) => void) {
|
||||
const itemsPerPage = 20;
|
||||
|
||||
for (const tag of this.tagsDiv.children as any as TesoftTag[]) {
|
||||
const tagName = tag.textContent.trim();
|
||||
|
||||
tag.onclick = () => {
|
||||
const tagSet = new Set(tags);
|
||||
if (tagSet.has(tagName)) {
|
||||
tagSet.delete(tagName);
|
||||
} else {
|
||||
tagSet.add(tagName);
|
||||
}
|
||||
const newTags = Array.from(tagSet);
|
||||
|
||||
onParametersChange(page, newTags);
|
||||
this.reload(page, newTags, onParametersChange);
|
||||
};
|
||||
|
||||
if (tags.includes(tagName)) {
|
||||
tag.selected = "";
|
||||
} else {
|
||||
tag.selected = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.loaderSection.reset();
|
||||
this.listDiv.innerHTML = "";
|
||||
this.pagination.classList.add("hidden");
|
||||
|
||||
const parameters: Record<string, any> = {
|
||||
offset: (page - 1) * itemsPerPage,
|
||||
limit: itemsPerPage,
|
||||
};
|
||||
if (tags) {
|
||||
parameters.tags = tags.join(",");
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await sendApiGet("blog", parameters);
|
||||
if (response.ok) {
|
||||
const body = await response.json();
|
||||
|
||||
if (body.articles.length === 0) {
|
||||
throw "No articles found.";
|
||||
}
|
||||
|
||||
for (const article of body.articles) {
|
||||
const anchor = this.listItemTemplate.content.children[0].cloneNode(true) as HTMLAnchorElement;
|
||||
anchor.href = `/blog?id=${article.id}`;
|
||||
|
||||
const heading = anchor.querySelector<HTMLHeadingElement>("h3")!;
|
||||
heading.textContent = article.title;
|
||||
const badgeText = getBadgeText(body.status);
|
||||
if (badgeText) {
|
||||
const badge = document.createElement("tesoft-badge") as TesoftBadge;
|
||||
badge.textContent = badgeText;
|
||||
heading.appendChild(badge);
|
||||
}
|
||||
|
||||
anchor.querySelector<HTMLDivElement>(".dates")!.textContent = formatArticleDate(new Date(article.date), article['mod-date'] ? new Date(article['mod-date']) : null);
|
||||
|
||||
const tags = anchor.querySelector<HTMLDivElement>(".tags")!;
|
||||
for (const tagName of article.tags) {
|
||||
const badge = document.createElement("tesoft-badge") as TesoftBadge;
|
||||
badge.textContent = tagName;
|
||||
tags.appendChild(badge);
|
||||
}
|
||||
|
||||
this.listDiv.appendChild(anchor);
|
||||
}
|
||||
|
||||
this.pagination.curPage = page;
|
||||
this.pagination.lastPage = Math.ceil(body.total / itemsPerPage);
|
||||
this.pagination.classList.remove("hidden");
|
||||
} else {
|
||||
const body = await response.json();
|
||||
throw `${capitalize(body.message)}.`;
|
||||
}
|
||||
} catch (error: any) {
|
||||
this.error.textContent = error;
|
||||
this.error.classList.remove("hidden");
|
||||
this.listDiv.classList.add("hidden");
|
||||
}
|
||||
|
||||
this.loaderSection.finish();
|
||||
}
|
||||
|
||||
private async fetchTags(): Promise<string[]> {
|
||||
const tags: string[] = [];
|
||||
|
||||
let offset = 0;
|
||||
const limit = 50;
|
||||
|
||||
while (true) {
|
||||
const response = await sendApiGet("blog/tags", {
|
||||
offset: offset,
|
||||
limit: limit,
|
||||
});
|
||||
if (!response.ok) {
|
||||
const body = await response.json();
|
||||
throw `${capitalize(body.message)}.`;
|
||||
}
|
||||
|
||||
const body = await response.json();
|
||||
|
||||
for (const tag of body.tags) {
|
||||
tags.push(tag);
|
||||
}
|
||||
|
||||
if (body.tags.length < limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("tesoft-blog-article-list", TesoftBlogArticleList);
|
||||
Reference in New Issue
Block a user