From 08accc75a43b91a9576aede15b8adb55e7ab493c Mon Sep 17 00:00:00 2001 From: Rachel Lowe <60523642+11BelowStudio@users.noreply.github.com> Date: Fri, 2 May 2025 19:25:59 +0100 Subject: [PATCH 1/2] Solving #197, front page 'recent uploads' listing is now sorted by upload timestamp. 'All packages' page functionality unchanged (still sorted alphabetically by mpackage field). Also the backend now allows the package list to be sorted by other fields, and for the sort order to be reversed, in case anyone feels like adding controls for that to the frontend. --- upload-site/app/components/PackageList.tsx | 40 +++++++++++++++++----- upload-site/app/lib/packages.ts | 4 +-- upload-site/app/lib/types.ts | 37 ++++++++++++++++++++ upload-site/app/page.tsx | 3 +- 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/upload-site/app/components/PackageList.tsx b/upload-site/app/components/PackageList.tsx index 8a3f337..fc680ef 100644 --- a/upload-site/app/components/PackageList.tsx +++ b/upload-site/app/components/PackageList.tsx @@ -1,25 +1,49 @@ 'use client' -import { PackageMetadata } from '@/app/lib/types' +import { UploadedPackageMetadata, UploadedPackageSortByOptions } from '@/app/lib/types' import { useState } from 'react' import ReactMarkdown from 'react-markdown' import Image from 'next/image' interface PackageListProps { - packages: PackageMetadata[]; + packages: UploadedPackageMetadata[]; limit?: number; + sortBy?: UploadedPackageSortByOptions; // will sort by mpackage by default + reverse?: boolean; } -export const PackageList = ({ packages, limit }: PackageListProps) => { + +export const PackageList = ({ packages, limit, sortBy, reverse }: PackageListProps) => { const [expandedPackage, setExpandedPackage] = useState(null); + // will sort by mpackage by default. + const _sortBy = sortBy ? sortBy : UploadedPackageSortByOptions.mpackage; + + // will not reverse the sort order by default. + const _reverse = reverse ? reverse : false; + // Sort packages alphabetically by mpackage, case-insensitive // This is not fixed in stone and might change in the future as we get a better understanding of how to handle this. - const sortedPackages = packages.slice().sort((a, b) => { - const aName = a.mpackage?.toLowerCase() || ''; - const bName = b.mpackage?.toLowerCase() || ''; - return aName.localeCompare(bName); - }); + const sortedPackages = packages.slice().sort( + + // If we're sorting by 'uploaded' - uploaded is a number (not a string), so we compare the 'uploaded' values mathematically. + (_sortBy == UploadedPackageSortByOptions.uploaded) + ? (a1, b1) => { + const aUpload = a1.uploaded; + const bUpload = b1.uploaded; + return bUpload - aUpload; + } + // otherwise, we sort them alphabetically (case-insensitive) by the appropriate field (as defined by _sortBy) + : (a2, b2) => { + const aName = a2[_sortBy]?.toLowerCase() || ''; + const bName = b2[_sortBy]?.toLowerCase() || ''; + return aName.localeCompare(bName); + } + ); + + if (_reverse){ + sortedPackages.reverse(); + } const displayPackages = limit ? sortedPackages.slice(0, limit) : sortedPackages; diff --git a/upload-site/app/lib/packages.ts b/upload-site/app/lib/packages.ts index bc602e4..54b276a 100644 --- a/upload-site/app/lib/packages.ts +++ b/upload-site/app/lib/packages.ts @@ -1,6 +1,6 @@ -import { PackageMetadata } from './types' +import { UploadedPackageMetadata } from './types' -export async function fetchRepositoryPackages(): Promise { +export async function fetchRepositoryPackages(): Promise { const response = await fetch('https://raw.githubusercontent.com/Mudlet/mudlet-package-repository/refs/heads/main/packages/mpkg.packages.json') const data = await response.json() return data.packages diff --git a/upload-site/app/lib/types.ts b/upload-site/app/lib/types.ts index 46eeb89..8503996 100644 --- a/upload-site/app/lib/types.ts +++ b/upload-site/app/lib/types.ts @@ -14,3 +14,40 @@ export interface PackageMetadata { icon: string | null; filename: string | null; } + +/** + * Extension of PackageMetadata, + * now exposing the 'uploaded' field within mpkg.packages.json + * (this is in a new interface because 'uploaded' is not known within + * regular PackageMetadata (which is also used for packages which + * have not yet been uploaded)) + */ +export interface UploadedPackageMetadata extends PackageMetadata { + uploaded: number; +} + + +/** + * Enum for the known fields in the UploadedPackageMetadata interface + * intended to allow the uploaded packages to be sorted by those fields + * (defining known fields via an enum allows some more robust type safety stuff + * via avoiding accidental use of invalid fields in hardcoded values later on) + * + * 'description', 'filename', and 'icon' intentionally omitted because why would anyone need to sort by those? + * + * please update the values of this enum if there's a change to UploadedPackageMetadata's fields. + */ +export enum UploadedPackageSortByOptions { + /** sort by the name of the .mpackage */ + mpackage = "mpackage", + /** sort by package name/title/subtitle */ + title = "title", + /** sort by package version number */ + version = "version", + /** sort by mpackage creation timestamp */ + created = "created", + /** sort by author name */ + author = "author", + /** sort by mpackage upload unix timestamp (number) */ + uploaded = "uploaded" +} \ No newline at end of file diff --git a/upload-site/app/page.tsx b/upload-site/app/page.tsx index e08582a..df71a7b 100644 --- a/upload-site/app/page.tsx +++ b/upload-site/app/page.tsx @@ -1,6 +1,7 @@ import { PackageList } from './components/PackageList'; import { IntroSection } from './components/IntroSection'; import { ProgressBar } from './components/ProgressBar'; +import { UploadedPackageSortByOptions } from './lib/types'; import { promises as fs } from 'fs'; const PACKAGE_GOAL = 100; @@ -34,7 +35,7 @@ export default async function Home() {

Recent uploads

- +
); From 7c7df097f234d8368ad189aed50b43cc9f7daea8 Mon Sep 17 00:00:00 2001 From: Rachel Lowe <60523642+11BelowStudio@users.noreply.github.com> Date: Fri, 2 May 2025 19:41:41 +0100 Subject: [PATCH 2/2] removing an extra newline which snuck in --- upload-site/app/components/PackageList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/upload-site/app/components/PackageList.tsx b/upload-site/app/components/PackageList.tsx index fc680ef..9d81a5e 100644 --- a/upload-site/app/components/PackageList.tsx +++ b/upload-site/app/components/PackageList.tsx @@ -12,7 +12,6 @@ interface PackageListProps { reverse?: boolean; } - export const PackageList = ({ packages, limit, sortBy, reverse }: PackageListProps) => { const [expandedPackage, setExpandedPackage] = useState(null);