feat: add changelog page with localized release notes
- Implement releases collection in content-collections.ts for tracking version updates - Create changelog page with dynamic release rendering for multiple locales - Add ReleaseCard component to display version details and changes - Implement getReleases utility function to fetch and sort release notes - Update internationalization messages for changelog page - Add English and Chinese release notes for v1.0.0, v1.1.0, and v1.2.0
This commit is contained in:
parent
8da4488106
commit
bc4b5527eb
@ -232,6 +232,74 @@ export const pages = defineCollection({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases collection for changelog
|
||||||
|
*
|
||||||
|
* 1. For a release at content/en/release/v1-0-0.md:
|
||||||
|
* locale: en
|
||||||
|
* slug: /release/v1-0-0
|
||||||
|
* slugAsParams: v1-0-0
|
||||||
|
*
|
||||||
|
* 2. For a release at content/zh/release/v1-0-0.md:
|
||||||
|
* locale: zh
|
||||||
|
* slug: /release/v1-0-0
|
||||||
|
* slugAsParams: v1-0-0
|
||||||
|
*/
|
||||||
|
export const releases = defineCollection({
|
||||||
|
name: 'release',
|
||||||
|
directory: 'content',
|
||||||
|
include: '**/release/**/*.{md,mdx}',
|
||||||
|
schema: (z) => ({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
date: z.string().datetime(),
|
||||||
|
version: z.string(),
|
||||||
|
published: z.boolean().default(true),
|
||||||
|
locale: z.enum(LOCALES as [string, ...string[]]).optional()
|
||||||
|
}),
|
||||||
|
transform: async (data, context) => {
|
||||||
|
const body = await compileMDX(context, data, {
|
||||||
|
remarkPlugins: [
|
||||||
|
remarkGfm,
|
||||||
|
codeImport
|
||||||
|
],
|
||||||
|
rehypePlugins: [
|
||||||
|
rehypeSlug,
|
||||||
|
rehypeAutolinkHeadings,
|
||||||
|
[rehypePrettyCode, prettyCodeOptions]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine the locale from the file path or use the provided locale
|
||||||
|
const pathParts = data._meta.path.split(path.sep);
|
||||||
|
const localeFromPath = LOCALES.includes(pathParts[0]) ? pathParts[0] : null;
|
||||||
|
const locale = data.locale || localeFromPath || DEFAULT_LOCALE;
|
||||||
|
|
||||||
|
// Create a slug without the locale in the path
|
||||||
|
let slugPath = data._meta.path;
|
||||||
|
if (localeFromPath) {
|
||||||
|
// Remove the locale from the path for the slug
|
||||||
|
const pathWithoutLocale = pathParts.slice(1).join(path.sep);
|
||||||
|
slugPath = pathWithoutLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create slugAsParams without the locale
|
||||||
|
const slugParamsParts = slugPath.split(path.sep).slice(1);
|
||||||
|
const slugAsParams = slugParamsParts.join('/');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
locale,
|
||||||
|
slug: `/${slugPath}`,
|
||||||
|
slugAsParams,
|
||||||
|
body: {
|
||||||
|
raw: data.content,
|
||||||
|
code: body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const prettyCodeOptions: Options = {
|
const prettyCodeOptions: Options = {
|
||||||
theme: 'github-dark',
|
theme: 'github-dark',
|
||||||
getHighlighter: (options) =>
|
getHighlighter: (options) =>
|
||||||
@ -260,5 +328,5 @@ const prettyCodeOptions: Options = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
collections: [authors, categories, posts, pages]
|
collections: [authors, categories, posts, pages, releases]
|
||||||
});
|
});
|
30
content/en/release/v1-0-0.md
Normal file
30
content/en/release/v1-0-0.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: "Initial Release"
|
||||||
|
description: "Our first official release with core features and functionality"
|
||||||
|
date: "2024-03-01T00:00:00Z"
|
||||||
|
version: "v1.0.0"
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
### Core Features
|
||||||
|
|
||||||
|
We're excited to announce the initial release of our platform with the following core features:
|
||||||
|
|
||||||
|
- **User Authentication**: Secure login and registration with email verification
|
||||||
|
- **Dashboard**: Intuitive dashboard for managing your projects and resources
|
||||||
|
- **Project Management**: Create, edit, and organize your projects with ease
|
||||||
|
- **Team Collaboration**: Invite team members and collaborate on projects
|
||||||
|
- **API Integration**: Connect with third-party services through our API
|
||||||
|
|
||||||
|
### Technical Improvements
|
||||||
|
|
||||||
|
- Built with Next.js 14 and React Server Components for optimal performance
|
||||||
|
- Implemented Drizzle ORM for type-safe database operations
|
||||||
|
- Added comprehensive error handling and logging
|
||||||
|
- Optimized for mobile and desktop experiences
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fixed issues with user registration flow
|
||||||
|
- Resolved authentication token expiration handling
|
||||||
|
- Improved form validation and error messages
|
30
content/en/release/v1-1-0.md
Normal file
30
content/en/release/v1-1-0.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: "Feature Update"
|
||||||
|
description: "New features and improvements to enhance your experience"
|
||||||
|
date: "2024-03-15T00:00:00Z"
|
||||||
|
version: "v1.1.0"
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
We've added several new features to improve your experience:
|
||||||
|
|
||||||
|
- **Dark Mode**: Toggle between light and dark themes based on your preference
|
||||||
|
- **Export Options**: Export your data in multiple formats (CSV, JSON, PDF)
|
||||||
|
- **Advanced Search**: Find what you need faster with our improved search functionality
|
||||||
|
- **Custom Templates**: Create and save templates for recurring projects
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- **Performance Optimization**: Reduced page load times by 40%
|
||||||
|
- **UI Improvements**: Refreshed design with better accessibility
|
||||||
|
- **Mobile Experience**: Enhanced responsive design for all device sizes
|
||||||
|
- **Notification System**: Improved notification delivery and management
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fixed issue with project duplication
|
||||||
|
- Resolved calendar sync problems
|
||||||
|
- Fixed data import validation errors
|
||||||
|
- Improved error handling for API requests
|
37
content/en/release/v1-2-0.md
Normal file
37
content/en/release/v1-2-0.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "AI Integration"
|
||||||
|
description: "Introducing AI-powered features to boost productivity"
|
||||||
|
date: "2024-03-30T00:00:00Z"
|
||||||
|
version: "v1.2.0"
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
### AI-Powered Features
|
||||||
|
|
||||||
|
We're thrilled to introduce our new AI capabilities:
|
||||||
|
|
||||||
|
- **Smart Suggestions**: Get intelligent recommendations based on your usage patterns
|
||||||
|
- **Content Generation**: Generate high-quality content with our AI assistant
|
||||||
|
- **Automated Tagging**: Let AI organize your content with smart tagging
|
||||||
|
- **Predictive Analytics**: Forecast trends and outcomes with AI-powered insights
|
||||||
|
|
||||||
|
### Platform Improvements
|
||||||
|
|
||||||
|
- **Webhooks**: Set up custom webhooks to integrate with your workflow
|
||||||
|
- **Enhanced Security**: Added two-factor authentication and improved password policies
|
||||||
|
- **Custom Domains**: Use your own domain for your workspace
|
||||||
|
- **Audit Logs**: Track all activities with comprehensive audit logging
|
||||||
|
|
||||||
|
### Developer Tools
|
||||||
|
|
||||||
|
- **Expanded API**: New endpoints for advanced integrations
|
||||||
|
- **SDK Updates**: Updated SDKs for all major programming languages
|
||||||
|
- **Developer Console**: Improved developer experience with better documentation
|
||||||
|
- **Rate Limiting Controls**: Manage API usage with flexible rate limiting
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fixed issues with file uploads on certain browsers
|
||||||
|
- Resolved synchronization issues between devices
|
||||||
|
- Improved error handling for third-party integrations
|
||||||
|
- Fixed accessibility issues in the dashboard
|
30
content/zh/release/v1-0-0.md
Normal file
30
content/zh/release/v1-0-0.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: "初始版本"
|
||||||
|
description: "我们的第一个正式版本,包含核心功能"
|
||||||
|
date: "2024-03-01T00:00:00Z"
|
||||||
|
version: "v1.0.0"
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
### 核心功能
|
||||||
|
|
||||||
|
我们很高兴宣布平台的初始版本,包含以下核心功能:
|
||||||
|
|
||||||
|
- **用户认证**:安全的登录和注册,带有电子邮件验证
|
||||||
|
- **仪表盘**:直观的仪表盘,用于管理您的项目和资源
|
||||||
|
- **项目管理**:轻松创建、编辑和组织您的项目
|
||||||
|
- **团队协作**:邀请团队成员并在项目上协作
|
||||||
|
- **API集成**:通过我们的API连接第三方服务
|
||||||
|
|
||||||
|
### 技术改进
|
||||||
|
|
||||||
|
- 使用Next.js 14和React服务器组件构建,以获得最佳性能
|
||||||
|
- 实现Drizzle ORM进行类型安全的数据库操作
|
||||||
|
- 添加全面的错误处理和日志记录
|
||||||
|
- 针对移动和桌面体验进行优化
|
||||||
|
|
||||||
|
### 错误修复
|
||||||
|
|
||||||
|
- 修复了用户注册流程中的问题
|
||||||
|
- 解决了身份验证令牌过期处理
|
||||||
|
- 改进了表单验证和错误消息
|
30
content/zh/release/v1-1-0.md
Normal file
30
content/zh/release/v1-1-0.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: "功能更新"
|
||||||
|
description: "新功能和改进,提升您的使用体验"
|
||||||
|
date: "2024-03-15T00:00:00Z"
|
||||||
|
version: "v1.1.0"
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
### 新功能
|
||||||
|
|
||||||
|
我们添加了几个新功能来改善您的体验:
|
||||||
|
|
||||||
|
- **深色模式**:根据您的偏好在浅色和深色主题之间切换
|
||||||
|
- **导出选项**:以多种格式(CSV、JSON、PDF)导出您的数据
|
||||||
|
- **高级搜索**:通过我们改进的搜索功能更快地找到您需要的内容
|
||||||
|
- **自定义模板**:为重复项目创建和保存模板
|
||||||
|
|
||||||
|
### 增强功能
|
||||||
|
|
||||||
|
- **性能优化**:页面加载时间减少40%
|
||||||
|
- **UI改进**:更新设计,提高可访问性
|
||||||
|
- **移动体验**:为所有设备尺寸增强响应式设计
|
||||||
|
- **通知系统**:改进通知传递和管理
|
||||||
|
|
||||||
|
### 错误修复
|
||||||
|
|
||||||
|
- 修复了项目复制问题
|
||||||
|
- 解决了日历同步问题
|
||||||
|
- 修复了数据导入验证错误
|
||||||
|
- 改进了API请求的错误处理
|
37
content/zh/release/v1-2-0.md
Normal file
37
content/zh/release/v1-2-0.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "AI集成"
|
||||||
|
description: "引入AI驱动的功能,提高生产力"
|
||||||
|
date: "2024-03-30T00:00:00Z"
|
||||||
|
version: "v1.2.0"
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
### AI驱动功能
|
||||||
|
|
||||||
|
我们很高兴介绍我们的新AI功能:
|
||||||
|
|
||||||
|
- **智能建议**:根据您的使用模式获取智能推荐
|
||||||
|
- **内容生成**:使用我们的AI助手生成高质量内容
|
||||||
|
- **自动标记**:让AI通过智能标记组织您的内容
|
||||||
|
- **预测分析**:使用AI驱动的洞察力预测趋势和结果
|
||||||
|
|
||||||
|
### 平台改进
|
||||||
|
|
||||||
|
- **Webhooks**:设置自定义webhooks以集成到您的工作流程中
|
||||||
|
- **增强安全性**:添加双因素认证和改进的密码策略
|
||||||
|
- **自定义域名**:为您的工作空间使用自己的域名
|
||||||
|
- **审计日志**:通过全面的审计日志跟踪所有活动
|
||||||
|
|
||||||
|
### 开发者工具
|
||||||
|
|
||||||
|
- **扩展API**:用于高级集成的新端点
|
||||||
|
- **SDK更新**:更新了所有主要编程语言的SDK
|
||||||
|
- **开发者控制台**:通过更好的文档改善开发者体验
|
||||||
|
- **速率限制控制**:通过灵活的速率限制管理API使用
|
||||||
|
|
||||||
|
### 错误修复
|
||||||
|
|
||||||
|
- 修复了某些浏览器上文件上传的问题
|
||||||
|
- 解决了设备之间的同步问题
|
||||||
|
- 改进了第三方集成的错误处理
|
||||||
|
- 修复了仪表板中的可访问性问题
|
@ -20,6 +20,10 @@
|
|||||||
"tryAgain": "Try again",
|
"tryAgain": "Try again",
|
||||||
"backToHome": "Back to home"
|
"backToHome": "Back to home"
|
||||||
},
|
},
|
||||||
|
"ChangelogPage": {
|
||||||
|
"title": "Changelog",
|
||||||
|
"subtitle": "Stay up to date with the latest changes in our product."
|
||||||
|
},
|
||||||
"AuthPage": {
|
"AuthPage": {
|
||||||
"login": {
|
"login": {
|
||||||
"title": "Login",
|
"title": "Login",
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
"tryAgain": "重试",
|
"tryAgain": "重试",
|
||||||
"backToHome": "返回首页"
|
"backToHome": "返回首页"
|
||||||
},
|
},
|
||||||
|
"ChangelogPage": {
|
||||||
|
"title": "更新日志",
|
||||||
|
"subtitle": "查看我们产品的最新动态"
|
||||||
|
},
|
||||||
"AuthPage": {
|
"AuthPage": {
|
||||||
"login": {
|
"login": {
|
||||||
"title": "登录",
|
"title": "登录",
|
||||||
|
75
src/app/[locale]/(marketing)/(pages)/changelog/page.tsx
Normal file
75
src/app/[locale]/(marketing)/(pages)/changelog/page.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { ReleaseCard } from '@/components/release/release-card';
|
||||||
|
import { getReleases } from '@/lib/release/get-releases';
|
||||||
|
import { getBaseUrl } from '@/lib/urls/get-base-url';
|
||||||
|
import type { NextPageProps } from '@/types/next-page-props';
|
||||||
|
import type { Metadata } from 'next';
|
||||||
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
|
import '@/styles/mdx.css';
|
||||||
|
|
||||||
|
export async function generateMetadata(
|
||||||
|
props: NextPageProps
|
||||||
|
): Promise<Metadata> {
|
||||||
|
const params = await props.params;
|
||||||
|
if (!params) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const locale = params.locale as string;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: 'Changelog',
|
||||||
|
description: 'Track all updates and improvements to our platform',
|
||||||
|
openGraph: {
|
||||||
|
title: 'Changelog',
|
||||||
|
description: 'Track all updates and improvements to our platform',
|
||||||
|
type: 'article',
|
||||||
|
url: `${getBaseUrl()}/changelog`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function ChangelogPage(props: NextPageProps) {
|
||||||
|
const params = await props.params;
|
||||||
|
if (!params) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const locale = params.locale as string;
|
||||||
|
const releases = await getReleases(locale);
|
||||||
|
|
||||||
|
if (!releases || releases.length === 0) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = await getTranslations('ChangelogPage');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-4xl mx-auto space-y-8">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="text-center text-3xl font-bold tracking-tight">
|
||||||
|
{t('title')}
|
||||||
|
</h1>
|
||||||
|
<p className="text-center text-lg text-muted-foreground">
|
||||||
|
{t('subtitle')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Releases */}
|
||||||
|
<div className="mt-8">
|
||||||
|
{releases.map((release) => (
|
||||||
|
<ReleaseCard
|
||||||
|
key={release.slug}
|
||||||
|
title={release.title}
|
||||||
|
description={release.description}
|
||||||
|
date={release.date}
|
||||||
|
version={release.version}
|
||||||
|
content={release.body.code}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -13,22 +13,24 @@ export function CustomPage({ title, description, date, content }: CustomPageProp
|
|||||||
const formattedDate = getLocaleDate(date);
|
const formattedDate = getLocaleDate(date);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8">
|
<div className="max-w-4xl mx-auto space-y-8">
|
||||||
<div className="space-y-8">
|
{/* Header */}
|
||||||
{/* Header */}
|
<div className="space-y-4">
|
||||||
<div className="space-y-4">
|
<h1 className="text-center text-3xl font-bold tracking-tight">
|
||||||
<h1 className="text-3xl font-bold tracking-tight">{title}</h1>
|
{title}
|
||||||
<p className="text-lg text-muted-foreground">{description}</p>
|
</h1>
|
||||||
<div className="flex items-center gap-2">
|
<p className="text-center text-lg text-muted-foreground">
|
||||||
<CalendarIcon className="size-4 text-muted-foreground" />
|
{description}
|
||||||
<p className="text-sm text-muted-foreground">{formattedDate}</p>
|
</p>
|
||||||
</div>
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<CalendarIcon className="size-4 text-muted-foreground" />
|
||||||
|
<p className="text-sm text-muted-foreground">{formattedDate}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="prose prose-gray dark:prose-invert max-w-none">
|
<div className="prose prose-gray dark:prose-invert max-w-none">
|
||||||
<Mdx code={content} />
|
<Mdx code={content} />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
43
src/components/release/release-card.tsx
Normal file
43
src/components/release/release-card.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Mdx } from '@/components/shared/mdx-component';
|
||||||
|
import { getLocaleDate } from '@/lib/utils';
|
||||||
|
import { CalendarIcon, TagIcon } from 'lucide-react';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
|
||||||
|
interface ReleaseCardProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
date: string;
|
||||||
|
version: string;
|
||||||
|
content: any; // MDX content
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReleaseCard({ title, description, date, version, content }: ReleaseCardProps) {
|
||||||
|
const formattedDate = getLocaleDate(date);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="mb-8">
|
||||||
|
<CardHeader className="space-y-4">
|
||||||
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<h2 className="text-2xl font-bold tracking-tight">{title}</h2>
|
||||||
|
<Badge variant="default" className="w-fit">
|
||||||
|
<TagIcon className="mr-1 size-3" />
|
||||||
|
{version}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground">{description}</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<CalendarIcon className="size-4 text-muted-foreground" />
|
||||||
|
<p className="text-sm text-muted-foreground">{formattedDate}</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="prose prose-gray dark:prose-invert max-w-none">
|
||||||
|
<Mdx code={content} />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
32
src/lib/release/get-releases.ts
Normal file
32
src/lib/release/get-releases.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { allReleases } from 'content-collections';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all releases for the changelog page
|
||||||
|
* @param locale The locale to get releases for
|
||||||
|
* @returns An array of releases sorted by date (newest first)
|
||||||
|
*/
|
||||||
|
export async function getReleases(locale: string) {
|
||||||
|
// Find all published releases with matching locale
|
||||||
|
const releases = allReleases.filter(
|
||||||
|
(release) =>
|
||||||
|
release.published &&
|
||||||
|
release.locale === locale
|
||||||
|
);
|
||||||
|
|
||||||
|
// If no releases found with the current locale, try to find ones with any locale
|
||||||
|
if (releases.length === 0) {
|
||||||
|
const defaultReleases = allReleases.filter(
|
||||||
|
(release) => release.published
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sort by date (newest first)
|
||||||
|
return defaultReleases.sort(
|
||||||
|
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by date (newest first)
|
||||||
|
return releases.sort(
|
||||||
|
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user