diff --git a/src/components/footer.tsx b/src/components/footer.tsx index 6f0e1ca..24ad6f8 100644 --- a/src/components/footer.tsx +++ b/src/components/footer.tsx @@ -2,6 +2,19 @@ import { Code, RssIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import formatDate from "@/utils/format-date"; +const locales = { + en: { + developed_by: "Developed by", + build_handcrafted: "Built handcrafted with", + last_build: "Last build", + }, + es: { + developed_by: "Desarrollado por", + build_handcrafted: "Construido a mano con", + last_build: "Última build", + }, +}; + type Props = { lang: "en" | "es"; }; @@ -11,11 +24,11 @@ export default function Footer(props: Props) { <footer className="border-t border-secondary px-4 py-12 text-center text-sm md:px-16 prose prose-invert min-w-full"> <section> <p> - Developed by{" "} + {locales[props.lang].developed_by}{" "} <strong className="font-bold text-primary">juancmandev</strong> </p> <p> - Built handcrafted with{" "} + {locales[props.lang].build_handcrafted}{" "} <Button asChild size={null} @@ -27,7 +40,10 @@ export default function Footer(props: Props) { </a> </Button> </p> - <p>Last built {formatDate(new Date())}.</p> + <p> + {locales[props.lang].last_build}: {formatDate(new Date(), props.lang)} + . + </p> </section> <section className="w-max mx-auto flex items-center gap-12"> <Button diff --git a/src/components/microblog-item.astro b/src/components/microblog-item.astro index 5b66993..5f7287b 100644 --- a/src/components/microblog-item.astro +++ b/src/components/microblog-item.astro @@ -1,31 +1,33 @@ --- import { marked } from "marked"; import formatDate from "@/utils/format-date"; +import { getLangFromUrl } from "@/i18n/utils"; const props = Astro.props; const content = marked.parse(props.content); + +const lang = getLangFromUrl(Astro.url); --- <article class="rounded-md border px-4 py-2"> - <header class="mb-2"> - <section class="flex items-center justify-between text-sm"> - <span class="font-light"> - {formatDate(new Date(props.published))}{" "} - </span> - <span class="text-sm font-thin"> - {new Date(props.published).toLocaleTimeString()} - </span> - </section> - <section class="mt-1"> - { - props && - props.expand.tags && - props?.expand.tags.map( - (tag: any) => - tag && <span class="text-sm">#{tag.name} </span>, - ) - } - </section> - </header> - <main set:html={content} /> + <header class="mb-2"> + <section class="flex items-center justify-between text-sm"> + <span class="font-light"> + {formatDate(new Date(props.published), lang)}{" "} + </span> + <span class="text-sm font-thin"> + {new Date(props.published).toLocaleTimeString()} + </span> + </section> + <section class="mt-1"> + { + props && + props.expand.tags && + props?.expand.tags.map( + (tag: any) => tag && <span class="text-sm">#{tag.name} </span>, + ) + } + </section> + </header> + <main set:html={content} /> </article> diff --git a/src/content/blog/en/a-better-way-for-consuming-content.mdx b/src/content/blog/en/a-better-way-for-consuming-content.mdx index 66347c2..c638bc4 100644 --- a/src/content/blog/en/a-better-way-for-consuming-content.mdx +++ b/src/content/blog/en/a-better-way-for-consuming-content.mdx @@ -53,8 +53,7 @@ you don't open the post link in your RSS Reader. The good thing is that almost every RSS Reader shows you content sorted by date, not by a creepy algorithm that wants you to be mad. -[For my website](https://github.com/juancmandev/website/blob/main/scripts/rss.ts), -I use a Node.js script that takes the `.mdx` files inside `content/blog` and +For my website I use a Node.js script that takes the `.mdx` files inside `content/blog` and `content/portfolio`, then generates the RSS Items, those with the `rss: true` in the metadata. @@ -115,6 +114,7 @@ https://youtube.com/feeds/videos.xml?channel_id=[CHANNEL ID] - [Astronomic Picture of the Day (apod)](https://apod.com/feed.rss) - [Earth Science Picture of the Day (epod)](https://feeds2.feedburner.com/EarthSciencePictureoftheDay) - [Erick Murphy (cool guy)](https://ericmurphy.xyz/index.xml) +- [Luke Smith](https://lukesmith.xyz/index.xml) ## More About RSS diff --git a/src/content/blog/es/una-mejor-forma-de-consumir-contenido.mdx b/src/content/blog/es/una-mejor-forma-de-consumir-contenido.mdx index 3432226..5acac5c 100644 --- a/src/content/blog/es/una-mejor-forma-de-consumir-contenido.mdx +++ b/src/content/blog/es/una-mejor-forma-de-consumir-contenido.mdx @@ -1,14 +1,114 @@ --- title: Una Mejor Forma de Consumir Contenido -description: - Get your news without visiting websites with algorithms that shows content - that you don't want to see. +description: Obtén tus noticias sin visitar websites con algoritmos que muestran contenido que no quieres ver. tags: [Tech] image: /blog/a-better-way-for-consuming-content/banner.webp -imageCaption: Newspapers. Photo by Ashni on Unsplash +imageCaption: Periódicos. Foto de Ashni en Unsplash date: 2024-4-11 author: Juan Manzanero rss: true --- -Hola + _Foto de +[Ashni](https://unsplash.com/@ashni_ahlawat?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) +en +[Unsplash](https://unsplash.com/photos/text-ePWaAwUn80k?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash)_ + +Obtén tus noticias sin visitar websites con algoritmos que muestran contenido que no quieres ver. + +## Algoritmos que dictan lo que ves + +Las redes sociales no están diseñadas para mostrarte las noticias más novedosas e importantes, +sino para mostrarte contenido dictado por un algoritmo. + +Y este contenido es normalmente, viral; y viral no quiere decir interesante. + +Normalmente estos algoritmos priorizan contenido que te hacen enojar. + +Contenido que promueve la negatividad obtiene más "clicks" que aquel que promueve la positividad. + +Esa es la razón por la cual Twitter y Facebook están llenos de posts estúpidos e irrelevantes (por lo regular). + +Por supuesto, es genial cuando el algoritmo te muestra contenido que te gusta, +descubriendo nuevas personas y páginas, pero eso no es lo usual. + +Sin mencionar los molestos anuncios y más cosas que quieren que les hagas click. + +Meta (anteriormente Facebook) sabe sobre esto, y promueve en sus productos como Instagram y Facebook, +lo mismo con Twitter. + +## La Solución: News Aggregators (RSS) + +RSS es un acrónimo de "Really Simple Syndication". + +Es una tecnología antigua, no muy promovida por las compañías. + +Esto es debido a que cuando lees un post en un RSS Reader, no debes de visitar el website, +y el website no puede mostrarte anuncios usando Google Ads (por ejemplo). No generas tráfico, +tus visitas no cuentan, al menos no si no abres el link del post en tu RSS Reader. + +Lo bueno que casi cualquier RSS Reader te muestra el contenido organizado por fechas, +no por un algoritmo raro que quiere hacerte enojar. + +Para mi website utilizo un script en Node.js que toma todos los archivos `.mdx` dentro de `content/blog` +y `content/portfolio`, entonces genera los RSS Items, aquellos con `rss: true` en sus metadatos. + +## Cómo usar un RSS Reader + +Primero, debes descargar uno. + +Hay muchas opciones: + +- [NetNewsWire](https://netnewswire.com/): un RSS Reader nativo para macOS y iOS, gratis y Open Source, +mi opción favorita como ~~Pecador~~ usuario de Apple +- [Akregator](https://apps.kde.org/akregator): del proyecto KDE para Linux +- [Feeder](https://play.google.com/store/apps/details?id=com.nononsenseapps.feeder.play): para Android +- [Raven Reader](https://ravenreader.app/): aplicación de escritorio multiplataforma + +### Agregando Feeds + +Ahora debes buscar por el link RSS de tu website favorito, ¡[como este](https://juancman.dev/es/rss.xml)! + +``` +https://juancman.dev/es/rss.xml +``` + +Si lo abres, verás una página rara con código similar a HTML. + +Una vez copiado, ve a tu RSS app y busca "Agregar feed" o algo similar, y pega el link, ¡y listo!, +ahora obtendrás los últimos posts de mi website. + +### Agregando Feeds de Redes Sociales + +Puedes agregar incluso feeds de sitios como Reddit o YouTube. + +#### Reddit + +Solo cambia `[SUBREDDIT]` por el nombre del subreddit a añadir: + +``` +https://reddit.com/r/[SUBREDDIT]/new/.rss +``` + +#### YouTube + +Ve a el canal por añadir, luego ve a la pestaña "Acerca de", da click en **Compartir > Copiar ID del Canal**. + +Ahora solo cambia `[CHANNEL ID]` por el copiado: + +``` +https://youtube.com/feeds/videos.xml?channel_id=[CHANNEL ID] +``` + +### Mis Feeds Favoritos + +- [juancman.dev (¡obviamente!)](https://www.juancman.dev/es/rss.xml) +- [Astronomic Picture of the Day (apod)](https://apod.com/feed.rss) +- [Earth Science Picture of the Day (epod)](https://feeds2.feedburner.com/EarthSciencePictureoftheDay) +- [Erick Murphy](https://ericmurphy.xyz/index.xml) +- [Luke Smith](https://lukesmith.xyz/index.xml) + +## Más Sobre RSS + +- [Privacy Tools - RSS Feed Readers](https://www.privacytools.io/privacy-rss-feed-readers) +- [Privacy Guides - News Aggregators](https://www.privacyguides.org/en/news-aggregators/) diff --git a/src/pages/es/index.astro b/src/pages/es/index.astro index f1b953d..459de73 100644 --- a/src/pages/es/index.astro +++ b/src/pages/es/index.astro @@ -6,33 +6,39 @@ import Layout from "@/layouts/Layout.astro"; import { sortContentByDate } from "@/utils/sorts"; import { getCollection } from "astro:content"; +const pageData = { + title: "juancmandev", + description: + "Bienvenido a mi dominio, extraño. Soy juancmandev; Desarrollador Web, entusiasta de Linux, y defensor de la privacidad.", +}; + const allPosts = await getCollection("blog", ({ data }) => data.draft !== true); const allEsPosts = allPosts.map((post) => { - const [lang, ...slug] = post.slug.split("/"); + const [lang, ...slug] = post.slug.split("/"); - if (lang === "es") - return { - ...post, - slug: slug.toString(), - }; - else null; + if (lang === "es") + return { + ...post, + slug: slug.toString(), + }; + else null; }); sortContentByDate(allEsPosts); const last3Blogs = allEsPosts.slice(0, 3); const allProjects = await getCollection( - "portfolio", - ({ data }) => data.draft !== true, + "portfolio", + ({ data }) => data.draft !== true, ); const allEnProjects = allProjects.map((project) => { - const [lang, ...slug] = project.slug.split("/"); + const [lang, ...slug] = project.slug.split("/"); - if (lang === "es") - return { - ...project, - slug: slug.toString(), - }; - else null; + if (lang === "es") + return { + ...project, + slug: slug.toString(), + }; + else null; }); sortContentByDate(allEnProjects); const last3Projects = allEnProjects.slice(0, 3); @@ -40,75 +46,68 @@ const last3Projects = allEnProjects.slice(0, 3); const lang = getLangFromUrl(Astro.url); --- -<Layout - title="juancmandev" - description="Bienvenido a mi dominio, extraño. Soy juancmandev; Desarrollador Web, entusiasta de Linux, y defensor de la privacidad." -> - <div class="prose prose-invert"> - <h1 class="text-primary">Bienvenido a mi dominio, extraño.</h1> - <p> - Soy <strong class="text-primary">juancmandev</strong>; <strong - >Desarrollador Web</strong - >, entusiasta de <strong>Linux</strong> y defensor de la <strong - >privacidad.</strong - > - </p> - <p> - Este es mi <strong>website</strong>, un pedazo de Internet al que - puedo llamar <strong>hogar</strong>. Aquí comparto mi pasión por - proyectos open source y otros temas. - </p> - <section> - <h2>Últimos posts</h2> - <ul class="mt-0 p-0 list-none"> - { - last3Blogs.map( - (blogpost) => - blogpost && ( - <li class="p-0"> - <PostItem - type="blog" - lang={lang} - slug={blogpost?.slug} - date={blogpost.data.date} - title={blogpost.data.title} - /> - </li> - ), - ) - } - </ul> - <LinkButton - variant="secondary" - href="/blog" - className="no-underline">Más posts</LinkButton - > - </section> - <section> - <h2>Últimos proyectos</h2> - <ul class="mt-0 p-0 list-none"> - { - last3Projects.map( - (project) => - project && ( - <li class="p-0"> - <PostItem - lang={lang} - type="portfolio" - slug={project.slug} - date={project.data.date!} - title={project.data.title!} - /> - </li> - ), - ) - } - </ul> - <LinkButton - variant="secondary" - href="/portfolio" - className="no-underline">More projects</LinkButton - > - </section> - </div> +<Layout {...pageData}> + <div class="prose prose-invert"> + <h1 class="text-primary">Bienvenido a mi dominio, extraño.</h1> + <p> + Soy <strong class="text-primary">juancmandev</strong>; <strong + >Desarrollador Web</strong + >, entusiasta de <strong>Linux</strong> y defensor de la <strong + >privacidad.</strong + > + </p> + <p> + Este es mi <strong>website</strong>, un pedazo de Internet al que puedo + llamar <strong>hogar</strong>. Aquí comparto mi pasión por proyectos open + source y otros temas. + </p> + <section> + <h2>Últimos posts</h2> + <ul class="mt-0 p-0 list-none"> + { + last3Blogs.map( + (blogpost) => + blogpost && ( + <li class="p-0"> + <PostItem + type="blog" + lang={lang} + slug={blogpost?.slug} + date={blogpost.data.date} + title={blogpost.data.title} + /> + </li> + ), + ) + } + </ul> + <LinkButton variant="secondary" href="/blog" className="no-underline" + >Más posts</LinkButton + > + </section> + <section> + <h2>Últimos proyectos</h2> + <ul class="mt-0 p-0 list-none"> + { + last3Projects.map( + (project) => + project && ( + <li class="p-0"> + <PostItem + lang={lang} + type="portfolio" + slug={project.slug} + date={project.data.date!} + title={project.data.title!} + /> + </li> + ), + ) + } + </ul> + <LinkButton variant="secondary" href="/portfolio" className="no-underline" + >Más proyectos</LinkButton + > + </section> + </div> </Layout> diff --git a/src/pages/es/videos/[...slug].astro b/src/pages/es/videos/[...slug].astro index 6609c9c..c392d86 100644 --- a/src/pages/es/videos/[...slug].astro +++ b/src/pages/es/videos/[...slug].astro @@ -4,6 +4,7 @@ import { getCollection } from "astro:content"; import components from "@/components/mdx/wrapper"; import formatDate from "@/utils/format-date"; import type { CollectionEntry } from "astro:content"; +import { getLangFromUrl } from "@/i18n/utils"; interface Props { project: CollectionEntry<"videos">; @@ -12,7 +13,7 @@ interface Props { export async function getStaticPaths() { const allProjects = await getCollection( "videos", - ({ data }) => data.draft !== true + ({ data }) => data.draft !== true, ); return allProjects.map((project) => ({ @@ -23,6 +24,8 @@ export async function getStaticPaths() { const { project } = Astro.props; const { Content } = await project.render(); + +const lang = getLangFromUrl(Astro.url); --- <Layout title={project.data.title} description={project.data.description}> @@ -32,7 +35,7 @@ const { Content } = await project.render(); <hr /> <p> <strong>Posted: </strong> - {project.data.date && formatDate(new Date(project.data.date))} + {project.data.date && formatDate(new Date(project.data.date), lang)} </p> </article> </Layout> diff --git a/src/pages/es/videos/index.astro b/src/pages/es/videos/index.astro index 4bbc2e4..7fddf09 100644 --- a/src/pages/es/videos/index.astro +++ b/src/pages/es/videos/index.astro @@ -1,5 +1,6 @@ --- import PostItem from "@/components/post-item"; +import { getLangFromUrl } from "@/i18n/utils"; import Layout from "@/layouts/Layout.astro"; import { sortContentByDate } from "@/utils/sorts"; import { getCollection } from "astro:content"; @@ -11,9 +12,11 @@ const pageData = { const allVideos = await getCollection( "videos", - ({ data }) => data.draft !== true + ({ data }) => data.draft !== true, ); sortContentByDate(allVideos); + +const lang = getLangFromUrl(Astro.url); --- <Layout {...pageData}> @@ -26,6 +29,7 @@ sortContentByDate(allVideos); allVideos.map((video: any) => ( <li> <PostItem + lang={lang} type="es/videos" slug={video.slug} date={video.data.date!} diff --git a/src/pages/index.astro b/src/pages/index.astro index f40dd22..33d5151 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -6,33 +6,39 @@ import PostItem from "@/components/post-item"; import { sortContentByDate } from "@/utils/sorts"; import { getLangFromUrl } from "@/i18n/utils"; +const pageData = { + title: "juancmandev", + description: + "Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender.", +}; + const allPosts = await getCollection("blog", ({ data }) => data.draft !== true); const allEnPosts = allPosts.map((post) => { - const [lang, ...slug] = post.slug.split("/"); + const [lang, ...slug] = post.slug.split("/"); - if (lang !== "es") - return { - ...post, - slug: slug.toString(), - }; - else null; + if (lang !== "es") + return { + ...post, + slug: slug.toString(), + }; + else null; }); sortContentByDate(allEnPosts); const last3Blogs = allEnPosts.slice(0, 3); const allProjects = await getCollection( - "portfolio", - ({ data }) => data.draft !== true, + "portfolio", + ({ data }) => data.draft !== true, ); const allEnProjects = allProjects.map((project) => { - const [lang, ...slug] = project.slug.split("/"); + const [lang, ...slug] = project.slug.split("/"); - if (lang !== "es") - return { - ...project, - slug: slug.toString(), - }; - else null; + if (lang !== "es") + return { + ...project, + slug: slug.toString(), + }; + else null; }); sortContentByDate(allEnProjects); const last3Projects = allEnProjects.slice(0, 3); @@ -40,70 +46,63 @@ const last3Projects = allEnProjects.slice(0, 3); const lang = getLangFromUrl(Astro.url); --- -<Layout - title="juancmandev" - description="Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender." -> - <div class="prose prose-invert"> - <h1 class="text-primary">Welcome to my domain, stranger.</h1> - <p> - I am <strong class="text-primary">juancmandev</strong>; <strong - >Web Developer</strong - >, <strong>Linux</strong> enthusiast, and <strong>privacy</strong> defender. - </p> - <p> - This is my <strong>website</strong>, a piece of the Internet that I - could call my <strong>home base</strong>. Here, I share my passion - about open source projects and other topics. - </p> - <section> - <h2>Latest posts</h2> - <ul class="mt-0 p-0 list-none"> - { - last3Blogs.map((blogpost: any) => ( - <li class="p-0"> - <PostItem - type="blog" - lang={lang} - slug={blogpost.slug} - date={blogpost.data.date!} - title={blogpost.data.title!} - /> - </li> - )) - } - </ul> - <LinkButton - variant="secondary" - href="/blog" - className="no-underline">More posts</LinkButton - > - </section> - <section> - <h2>Latest projects</h2> - <ul class="mt-0 p-0 list-none"> - { - last3Projects.map( - (project) => - project && ( - <li class="p-0"> - <PostItem - lang={lang} - type="portfolio" - slug={project.slug} - date={project.data.date!} - title={project.data.title!} - /> - </li> - ), - ) - } - </ul> - <LinkButton - variant="secondary" - href="/portfolio" - className="no-underline">More projects</LinkButton - > - </section> - </div> +<Layout {...pageData}> + <div class="prose prose-invert"> + <h1 class="text-primary">Welcome to my domain, stranger.</h1> + <p> + I am <strong class="text-primary">juancmandev</strong>; <strong + >Web Developer</strong + >, <strong>Linux</strong> enthusiast, and <strong>privacy</strong> defender. + </p> + <p> + This is my <strong>website</strong>, a piece of the Internet that I could + call my <strong>home base</strong>. Here, I share my passion about open + source projects and other topics. + </p> + <section> + <h2>Latest posts</h2> + <ul class="mt-0 p-0 list-none"> + { + last3Blogs.map((blogpost: any) => ( + <li class="p-0"> + <PostItem + type="blog" + lang={lang} + slug={blogpost.slug} + date={blogpost.data.date!} + title={blogpost.data.title!} + /> + </li> + )) + } + </ul> + <LinkButton variant="secondary" href="/blog" className="no-underline" + >More posts</LinkButton + > + </section> + <section> + <h2>Latest projects</h2> + <ul class="mt-0 p-0 list-none"> + { + last3Projects.map( + (project) => + project && ( + <li class="p-0"> + <PostItem + lang={lang} + type="portfolio" + slug={project.slug} + date={project.data.date!} + title={project.data.title!} + /> + </li> + ), + ) + } + </ul> + <LinkButton variant="secondary" href="/portfolio" className="no-underline" + >More projects</LinkButton + > + </section> + </div> </Layout> diff --git a/src/pages/portfolio/index.astro b/src/pages/portfolio/index.astro index 6ca966e..1d1d601 100644 --- a/src/pages/portfolio/index.astro +++ b/src/pages/portfolio/index.astro @@ -6,23 +6,23 @@ import { sortContentByDate } from "@/utils/sorts"; import { getCollection } from "astro:content"; const pageData = { - title: "Portfolio", - description: "Check my projects.", + title: "Portfolio", + description: "Check my projects.", }; const allProjects = await getCollection( - "portfolio", - ({ data }) => data.draft !== true, + "portfolio", + ({ data }) => data.draft !== true, ); const allEnProjects = allProjects.map((project) => { - const [lang, ...slug] = project.slug.split("/"); + const [lang, ...slug] = project.slug.split("/"); - if (lang === "en") - return { - ...project, - slug: slug, - }; - else null; + if (lang === "en") + return { + ...project, + slug: slug.toString(), + }; + else null; }); sortContentByDate(allEnProjects); @@ -30,26 +30,26 @@ const lang = getLangFromUrl(Astro.url); --- <Layout {...pageData}> - <section class="prose prose-invert"> - <h1>{pageData.title}</h1> - <p>{pageData.description}</p> - </section> - <ul class="mt-4 flex flex-col gap-4"> - { - allEnProjects.map( - (project) => - project && ( - <li> - <PostItem - lang={lang} - type="portfolio" - slug={project.slug} - date={project.data.date!} - title={project.data.title!} - /> - </li> - ), - ) - } - </ul> + <section class="prose prose-invert"> + <h1>{pageData.title}</h1> + <p>{pageData.description}</p> + </section> + <ul class="mt-4 flex flex-col gap-4"> + { + allEnProjects.map( + (project) => + project && ( + <li> + <PostItem + lang={lang} + type="portfolio" + slug={project.slug} + date={project.data.date!} + title={project.data.title!} + /> + </li> + ), + ) + } + </ul> </Layout>