This commit is contained in:
Sergey Yarkov 2026-02-23 21:03:12 +03:00
parent 515302fed3
commit c41a54072b
8 changed files with 40 additions and 29 deletions

View File

@ -1,14 +1,15 @@
---
import { useTranslation, getLanguageFromURL } from "@i18n/utils";
import { filterArticlesByPageLang, formatToArticleBlocks } from "@utils/article";
import * as utils from "@utils/article";
import List from "@components/ArticlesList";
import { getArticleList } from "@root/queries/article";
import { ArticleQuery } from "../graphql/graphql";
const t = useTranslation(Astro);
const pageLang = getLanguageFromURL(Astro.url.pathname);
const data = await getArticleList();
const articles = filterArticlesByPageLang(data, pageLang);
const articleBlocks = formatToArticleBlocks(articles);
const articles = utils.filterArticlesByPageLang<ArticleQuery["article"][0]["translations"]>(data, pageLang);
const articleBlocks = utils.formatToArticleBlocks(articles);
---
<List client:load i18n={{ "articles.empty": t("articles.empty") }} articles={articleBlocks} pageLang={pageLang} />

View File

@ -6,7 +6,7 @@ import { ArticleQuery } from "@/src/graphql/graphql";
import "./ArticlesList.scss";
type UiStringsType = { "articles.empty": string };
type Articles = ArticleQuery["article_translations"];
type Articles = ArticleQuery["article"][0]["translations"];
type ArticlesBlockType = Record<string, Articles>;
type ArticlesListProps = {

View File

@ -6,12 +6,11 @@ import EmptyList from "@components/EmptyList.astro";
import UIString from "@components/UIString.astro";
import TranslatedLink from "@components/TranslatedLink.astro";
import { getRecentArticleList, getSiteSettings } from "../queries";
import { RecentArticleQuery } from "../graphql/graphql";
const pageLang = getLanguageFromURL(Astro.url.pathname);
const siteSettings = await getSiteSettings();
const articles = await getRecentArticleList(siteSettings?.recent_articles_limit || 5);
const filteredArticles = utils.filterArticlesByPageLang<RecentArticleQuery>(articles, pageLang);
const filteredArticles = utils.filterArticlesByPageLang(articles, pageLang);
const isEmpty = articles.length === 0;
---
@ -27,15 +26,18 @@ const isEmpty = articles.length === 0;
<div itemscope itemtype='http://schema.org/Blog'>
{
!isEmpty ? (
filteredArticles.map((a) => (
<ArticleItem
slug={a.slug}
title={a.title}
languages_code={a.languages_code}
pub_date={a.pub_date}
pageLang={pageLang}
/>
))
filteredArticles.map(
(a) =>
a && (
<ArticleItem
slug={a.slug}
title={a.title}
languages_code={a.languages_code}
pub_date={a.pub_date}
pageLang={pageLang}
/>
)
)
) : (
<EmptyList />
)

View File

@ -18,12 +18,12 @@ type Documents = {
"\n\t\tquery LatestAppealByClientIpQuery($client_ip: String!, $limit: Int!) {\n\t\t\tappeal(filter: { client_ip: { _eq: $client_ip } }, sort: [\"-date_created\"], limit: $limit) {\n\t\t\t\tclient_ip\n\t\t\t\tdate_created\n\t\t\t}\n\t\t}\n\t": typeof types.LatestAppealByClientIpQueryDocument,
"\n\t\tmutation CreateAppealQuery(\n\t\t\t$name: String!\n\t\t\t$email: String!\n\t\t\t$subject: String!\n\t\t\t$message: String!\n\t\t\t$client_ip: String!\n\t\t) {\n\t\t\tcreate_appeal_item(\n\t\t\t\tdata: { name: $name, email: $email, subject: $subject, message: $message, client_ip: $client_ip }\n\t\t\t) {\n\t\t\t\tid\n\t\t\t}\n\t\t}\n\t": typeof types.CreateAppealQueryDocument,
"\n\t\tquery Article {\n\t\t\tarticle {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\tid\n\t\t\t\t\ttitle\n\t\t\t\t\ttags {\n\t\t\t\t\t\ttag_id {\n\t\t\t\t\t\t\ttitle\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdescription\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t\tname\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": typeof types.ArticleDocument,
"\n\t\tquery RecentArticle($limit: Int!) {\n\t\t\tarticle(limit: $limit, sort: [\"-translations.pub_date\"]) {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\ttitle\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": typeof types.RecentArticleDocument,
"\n\t\tquery RecentArticle($limit: Int!) {\n\t\t\tarticle(limit: $limit, sort: [\"-translations.pub_date\"]) {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\ttitle\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t\tname\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": typeof types.RecentArticleDocument,
"\n\t\tquery Article_translations($slug: String!) {\n\t\t\tarticle_translations(filter: { slug: { _eq: $slug } }) {\n\t\t\t\tid\n\t\t\t\ttitle\n\t\t\t\ttags {\n\t\t\t\t\ttag_id {\n\t\t\t\t\t\ttitle\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdescription\n\t\t\t\tslug\n\t\t\t\tcontent\n\t\t\t\tauthor {\n\t\t\t\t\tfirst_name\n\t\t\t\t\tlast_name\n\t\t\t\t}\n\t\t\t\tpub_date\n\t\t\t\tviews\n\t\t\t\tlanguages_code {\n\t\t\t\t\tcode\n\t\t\t\t\tname\n\t\t\t\t}\n\t\t\t\tcover_image {\n\t\t\t\t\ttitle\n\t\t\t\t\ttype\n\t\t\t\t\tfilename_disk\n\t\t\t\t}\n\t\t\t\tarticle_id {\n\t\t\t\t\ttranslations {\n\t\t\t\t\t\tslug\n\t\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\t\tcode\n\t\t\t\t\t\t\tname\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpub_date\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": typeof types.Article_TranslationsDocument,
"\n\t\tmutation Update_article_translations_item($id: ID!, $views: Int!) {\n\t\t\tupdate_article_translations_item(id: $id, data: { views: $views }) {\n\t\t\t\tviews\n\t\t\t}\n\t\t}\n\t": typeof types.Update_Article_Translations_ItemDocument,
"\n\t\tquery AboutMeQuery($pageLang: String!) {\n\t\t\tglobal_translations(filter: { languages_code: { code: { _contains: $pageLang } } }) {\n\t\t\t\tabout\n\t\t\t}\n\t\t}\n\t": typeof types.AboutMeQueryDocument,
"\n\t\tquery ContactsQuery {\n\t\t\tglobal {\n\t\t\t\temail\n\t\t\t\tgithub\n\t\t\t\ttelegram\n\t\t\t}\n\t\t}\n\t": typeof types.ContactsQueryDocument,
"\n\t\tquery SiteSettingsQuery {\n\t\t\tglobal {\n\t\t\t\tym_counter\n\t\t\t\trecent_articles_limit\n\t\t\t}\n\t\t}\n\t": typeof types.SiteSettingsQueryDocument,
"\n\t\tquery SiteSettingsQuery {\n\t\t\tglobal {\n\t\t\t\tym_counter\n\t\t\t\trecent_articles_limit\n\t\t\t\tgithub\n\t\t\t}\n\t\t}\n\t": typeof types.SiteSettingsQueryDocument,
"\n\t\tquery Project_translations($pageLang: String!) {\n\t\t\tproject_translations(filter: { languages_code: { code: { _contains: $pageLang } } }, sort: [\"-year\"]) {\n\t\t\t\tid\n\t\t\t\ttitle\n\t\t\t\tyear\n\t\t\t\tdescription\n\t\t\t\tsource_url\n\t\t\t\tdemo_url\n\t\t\t\tarticle_url\n\t\t\t\ticon {\n\t\t\t\t\tfilename_disk\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": typeof types.Project_TranslationsDocument,
"\n\t\tquery Tag {\n\t\t\ttag {\n\t\t\t\tid\n\t\t\t\ttitle\n\t\t\t}\n\t\t}\n\t": typeof types.TagDocument,
};
@ -31,12 +31,12 @@ const documents: Documents = {
"\n\t\tquery LatestAppealByClientIpQuery($client_ip: String!, $limit: Int!) {\n\t\t\tappeal(filter: { client_ip: { _eq: $client_ip } }, sort: [\"-date_created\"], limit: $limit) {\n\t\t\t\tclient_ip\n\t\t\t\tdate_created\n\t\t\t}\n\t\t}\n\t": types.LatestAppealByClientIpQueryDocument,
"\n\t\tmutation CreateAppealQuery(\n\t\t\t$name: String!\n\t\t\t$email: String!\n\t\t\t$subject: String!\n\t\t\t$message: String!\n\t\t\t$client_ip: String!\n\t\t) {\n\t\t\tcreate_appeal_item(\n\t\t\t\tdata: { name: $name, email: $email, subject: $subject, message: $message, client_ip: $client_ip }\n\t\t\t) {\n\t\t\t\tid\n\t\t\t}\n\t\t}\n\t": types.CreateAppealQueryDocument,
"\n\t\tquery Article {\n\t\t\tarticle {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\tid\n\t\t\t\t\ttitle\n\t\t\t\t\ttags {\n\t\t\t\t\t\ttag_id {\n\t\t\t\t\t\t\ttitle\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdescription\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t\tname\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": types.ArticleDocument,
"\n\t\tquery RecentArticle($limit: Int!) {\n\t\t\tarticle(limit: $limit, sort: [\"-translations.pub_date\"]) {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\ttitle\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": types.RecentArticleDocument,
"\n\t\tquery RecentArticle($limit: Int!) {\n\t\t\tarticle(limit: $limit, sort: [\"-translations.pub_date\"]) {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\ttitle\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t\tname\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": types.RecentArticleDocument,
"\n\t\tquery Article_translations($slug: String!) {\n\t\t\tarticle_translations(filter: { slug: { _eq: $slug } }) {\n\t\t\t\tid\n\t\t\t\ttitle\n\t\t\t\ttags {\n\t\t\t\t\ttag_id {\n\t\t\t\t\t\ttitle\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdescription\n\t\t\t\tslug\n\t\t\t\tcontent\n\t\t\t\tauthor {\n\t\t\t\t\tfirst_name\n\t\t\t\t\tlast_name\n\t\t\t\t}\n\t\t\t\tpub_date\n\t\t\t\tviews\n\t\t\t\tlanguages_code {\n\t\t\t\t\tcode\n\t\t\t\t\tname\n\t\t\t\t}\n\t\t\t\tcover_image {\n\t\t\t\t\ttitle\n\t\t\t\t\ttype\n\t\t\t\t\tfilename_disk\n\t\t\t\t}\n\t\t\t\tarticle_id {\n\t\t\t\t\ttranslations {\n\t\t\t\t\t\tslug\n\t\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\t\tcode\n\t\t\t\t\t\t\tname\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpub_date\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": types.Article_TranslationsDocument,
"\n\t\tmutation Update_article_translations_item($id: ID!, $views: Int!) {\n\t\t\tupdate_article_translations_item(id: $id, data: { views: $views }) {\n\t\t\t\tviews\n\t\t\t}\n\t\t}\n\t": types.Update_Article_Translations_ItemDocument,
"\n\t\tquery AboutMeQuery($pageLang: String!) {\n\t\t\tglobal_translations(filter: { languages_code: { code: { _contains: $pageLang } } }) {\n\t\t\t\tabout\n\t\t\t}\n\t\t}\n\t": types.AboutMeQueryDocument,
"\n\t\tquery ContactsQuery {\n\t\t\tglobal {\n\t\t\t\temail\n\t\t\t\tgithub\n\t\t\t\ttelegram\n\t\t\t}\n\t\t}\n\t": types.ContactsQueryDocument,
"\n\t\tquery SiteSettingsQuery {\n\t\t\tglobal {\n\t\t\t\tym_counter\n\t\t\t\trecent_articles_limit\n\t\t\t}\n\t\t}\n\t": types.SiteSettingsQueryDocument,
"\n\t\tquery SiteSettingsQuery {\n\t\t\tglobal {\n\t\t\t\tym_counter\n\t\t\t\trecent_articles_limit\n\t\t\t\tgithub\n\t\t\t}\n\t\t}\n\t": types.SiteSettingsQueryDocument,
"\n\t\tquery Project_translations($pageLang: String!) {\n\t\t\tproject_translations(filter: { languages_code: { code: { _contains: $pageLang } } }, sort: [\"-year\"]) {\n\t\t\t\tid\n\t\t\t\ttitle\n\t\t\t\tyear\n\t\t\t\tdescription\n\t\t\t\tsource_url\n\t\t\t\tdemo_url\n\t\t\t\tarticle_url\n\t\t\t\ticon {\n\t\t\t\t\tfilename_disk\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t": types.Project_TranslationsDocument,
"\n\t\tquery Tag {\n\t\t\ttag {\n\t\t\t\tid\n\t\t\t\ttitle\n\t\t\t}\n\t\t}\n\t": types.TagDocument,
};
@ -56,7 +56,7 @@ export function graphql(source: "\n\t\tquery Article {\n\t\t\tarticle {\n\t\t\t\
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n\t\tquery RecentArticle($limit: Int!) {\n\t\t\tarticle(limit: $limit, sort: [\"-translations.pub_date\"]) {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\ttitle\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t"): typeof import('./graphql').RecentArticleDocument;
export function graphql(source: "\n\t\tquery RecentArticle($limit: Int!) {\n\t\t\tarticle(limit: $limit, sort: [\"-translations.pub_date\"]) {\n\t\t\t\ttranslations(filter: { status: { _eq: \"published\" } }) {\n\t\t\t\t\ttitle\n\t\t\t\t\tslug\n\t\t\t\t\tpub_date\n\t\t\t\t\tlanguages_code {\n\t\t\t\t\t\tcode\n\t\t\t\t\t\tname\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t"): typeof import('./graphql').RecentArticleDocument;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@ -76,7 +76,7 @@ export function graphql(source: "\n\t\tquery ContactsQuery {\n\t\t\tglobal {\n\t
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n\t\tquery SiteSettingsQuery {\n\t\t\tglobal {\n\t\t\t\tym_counter\n\t\t\t\trecent_articles_limit\n\t\t\t}\n\t\t}\n\t"): typeof import('./graphql').SiteSettingsQueryDocument;
export function graphql(source: "\n\t\tquery SiteSettingsQuery {\n\t\t\tglobal {\n\t\t\t\tym_counter\n\t\t\t\trecent_articles_limit\n\t\t\t\tgithub\n\t\t\t}\n\t\t}\n\t"): typeof import('./graphql').SiteSettingsQueryDocument;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

View File

@ -4363,7 +4363,7 @@ export type RecentArticleQueryVariables = Exact<{
}>;
export type RecentArticleQuery = { __typename?: 'Query', article: Array<{ __typename?: 'article', translations?: Array<{ __typename?: 'article_translations', title: string, slug: string, pub_date: any, languages_code?: { __typename?: 'languages', code: string } | null } | null> | null }> };
export type RecentArticleQuery = { __typename?: 'Query', article: Array<{ __typename?: 'article', translations?: Array<{ __typename?: 'article_translations', title: string, slug: string, pub_date: any, languages_code?: { __typename?: 'languages', code: string, name: string } | null } | null> | null }> };
export type Article_TranslationsQueryVariables = Exact<{
slug: Scalars['String']['input'];
@ -4395,7 +4395,7 @@ export type ContactsQueryQuery = { __typename?: 'Query', global?: { __typename?:
export type SiteSettingsQueryQueryVariables = Exact<{ [key: string]: never; }>;
export type SiteSettingsQueryQuery = { __typename?: 'Query', global?: { __typename?: 'global', ym_counter?: number | null, recent_articles_limit?: number | null } | null };
export type SiteSettingsQueryQuery = { __typename?: 'Query', global?: { __typename?: 'global', ym_counter?: number | null, recent_articles_limit?: number | null, github: string } | null };
export type Project_TranslationsQueryVariables = Exact<{
pageLang: Scalars['String']['input'];
@ -4480,6 +4480,7 @@ export const RecentArticleDocument = new TypedDocumentString(`
pub_date
languages_code {
code
name
}
}
}
@ -4554,6 +4555,7 @@ export const SiteSettingsQueryDocument = new TypedDocumentString(`
global {
ym_counter
recent_articles_limit
github
}
}
`) as unknown as TypedDocumentString<SiteSettingsQueryQuery, SiteSettingsQueryQueryVariables>;

View File

@ -43,6 +43,7 @@ export async function getRecentArticleList(limit: number = 5) {
pub_date
languages_code {
code
name
}
}
}

View File

@ -39,6 +39,7 @@ export async function getSiteSettings() {
global {
ym_counter
recent_articles_limit
github
}
}
`);

View File

@ -1,18 +1,22 @@
import { DEFAULT_LANGUAGE } from "@root/constants";
import { Article, Article_Translations, RecentArticleQuery } from "../graphql/graphql";
import { Article_Translations, ArticleQuery, RecentArticleQuery } from "../graphql/graphql";
/**
* If there is a translation of an article in the current language of the page,
* the remaining articles with other translations should be removed.
*/
export function filterArticlesByPageLang<QueryT extends RecentArticleQuery>(
articles: QueryT["article"][0],
export function filterArticlesByPageLang<T>(
articles: T extends RecentArticleQuery["article"][0]["translations"]
? RecentArticleQuery["article"]
: ArticleQuery["article"],
pageLang: LanguageKeys
) {
const filteredArticles = articles.flatMap((a) => {
if (!a.translations) return [];
// Filter by articles by page lang
if (a.translations.length >= 2) {
const pageLangTranslations = a.translations.filter((a) => a.languages_code?.code.split("-")[0] === pageLang);
const pageLangTranslations = a.translations.filter((a) => a && a.languages_code?.code.split("-")[0] === pageLang);
if (pageLangTranslations.length !== 0) return pageLangTranslations;
// No articles found by page lang. Return first translation.
@ -39,8 +43,8 @@ export function createRelativeArticleUrl(
return `${lang}/${prefix}/${date}/${slug}`;
}
export function formatToArticleBlocks<T>(articles: Array<T & Pick<Article_Translations, "pub_date">>) {
const blocks: Record<string, Array<T & Pick<Article_Translations, "pub_date">>> = {};
export function formatToArticleBlocks(articles: ArticleQuery["article"][0]["translations"]) {
const blocks: Record<string, ArticleQuery["article"][0]["translations"]> = {};
if (!articles) return {};