Skip to content

Solving issue #197, front page 'recent uploads' listing is now sorted by upload timestamp. #452

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions upload-site/app/components/PackageList.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
'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<string | null>(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;

Expand Down
4 changes: 2 additions & 2 deletions upload-site/app/lib/packages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PackageMetadata } from './types'
import { UploadedPackageMetadata } from './types'

export async function fetchRepositoryPackages(): Promise<PackageMetadata[]> {
export async function fetchRepositoryPackages(): Promise<UploadedPackageMetadata[]> {
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
Expand Down
37 changes: 37 additions & 0 deletions upload-site/app/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
3 changes: 2 additions & 1 deletion upload-site/app/page.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,7 +35,7 @@ export default async function Home() {
</div>
<div className="border-t pt-8">
<h2 className="text-2xl font-bold mb-8">Recent uploads</h2>
<PackageList packages={packages} limit={5} />
<PackageList packages={packages} limit={5} sortBy={UploadedPackageSortByOptions.uploaded} />
</div>
</main>
);
Expand Down