feat: DEV-99 blog migration (#3870)

* feat: blog migration w/ fixes

Co-authored-by: Prospector <prospectordev@gmail.com>

* feat: add changelog button to news page

* fix: lint issues

* refactor: replace nuxt content with `@modrinth/blog`

* feat: shared public folder

* feat: try lazy loading html content

* feat: rss + hide newsletter btn + blog.config.ts

* feat: add new chapter modrinth servers post

* fix: lint issues

* fix: only generate RSS feed if changes detected

* fix: utils dep

* fix: lockfile dep

* feat: GET /email/subscribe + subscription button

* fix: lint issues

* feat: articles.json for app

* Made grid more responsive

* fix: changes

* Make margin slightly smaller in lists

* Fix footer link

* feat: latest news

* Fix responsiveness

* Remove old utm link

* Update changelog

* Lint

---------

Co-authored-by: Prospector <prospectordev@gmail.com>
This commit is contained in:
IMB11
2025-07-01 02:59:08 +01:00
committed by GitHub
parent fdb2b1195e
commit eef09e1ffe
220 changed files with 5094 additions and 681 deletions

View File

@@ -0,0 +1,50 @@
<template>
<div class="mx-2 p-4 !py-8 shadow-md sm:mx-8 sm:p-32">
<div class="my-8 flex items-center justify-between">
<h2 class="m-0 mx-auto text-3xl font-extrabold sm:text-4xl">Latest news from Modrinth</h2>
</div>
<div v-if="latestArticles" class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
<div
v-for="(article, index) in latestArticles"
:key="article.slug"
:class="{ 'max-xl:hidden': index === 2 }"
>
<NewsArticleCard :article="article" />
</div>
</div>
<div class="mx-2 my-8 flex w-full items-center justify-center">
<ButtonStyled color="brand" size="large">
<nuxt-link to="/news">
<NewspaperIcon />
View all news
</nuxt-link>
</ButtonStyled>
</div>
</div>
</template>
<script setup lang="ts">
import { NewspaperIcon } from "@modrinth/assets";
import { articles as rawArticles } from "@modrinth/blog";
import { ButtonStyled } from "@modrinth/ui";
import { ref, computed } from "vue";
import NewsArticleCard from "./NewsArticleCard.vue";
const articles = ref(
rawArticles
.map((article) => ({
...article,
path: `/news/article/${article.slug}`,
thumbnail: article.thumbnail
? `/news/article/${article.slug}/thumbnail.webp`
: `/news/default.jpg`,
title: article.title,
summary: article.summary,
date: article.date,
}))
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()),
);
const latestArticles = computed(() => articles.value.slice(0, 3));
</script>

View File

@@ -0,0 +1,40 @@
<script setup lang="ts">
import dayjs from "dayjs";
interface Article {
path: string;
thumbnail: string;
title: string;
summary: string;
date: string;
}
defineProps<{
article: Article;
}>();
</script>
<template>
<nuxt-link
:to="`${article.path}/`"
class="active:scale-[0.99]! group flex flex-col transition-all ease-in-out hover:brightness-125"
>
<article class="flex h-full grow flex-col gap-4">
<img
:src="article.thumbnail"
class="aspect-video w-full rounded-xl border-[1px] border-solid border-button-border object-cover"
/>
<div class="flex grow flex-col gap-2">
<h3 class="m-0 text-base leading-tight group-hover:underline">
{{ article.title }}
</h3>
<p v-if="article.summary" class="m-0 text-sm leading-tight">
{{ article.summary }}
</p>
<div class="mt-auto text-sm text-secondary">
{{ dayjs(article.date).format("MMMM D, YYYY") }}
</div>
</div>
</article>
</nuxt-link>
</template>