From a5d73e75d384a5107cdcec2d948353bff0550fbb Mon Sep 17 00:00:00 2001 From: Mark Rapson Date: Sat, 17 Oct 2026 20:15:13 +0100 Subject: [PATCH] Fix media library date ordering --- CHANGELOG.md | 1 + src/web/routes/settings/media-library.js | 6 ++-- test/media-library.test.js | 39 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03827ce..d5f0a33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. ### Fixed +- Media Library newest-first and oldest-first sorting now uses actual creation timestamps. - Browser requests now show the shared formatted error page for plain-text HTTP errors. - Template region position and stacking fields now update the preview immediately. - Region inputs no longer submit the template form when Enter is pressed. diff --git a/src/web/routes/settings/media-library.js b/src/web/routes/settings/media-library.js index 339b18b..6e6aed6 100644 --- a/src/web/routes/settings/media-library.js +++ b/src/web/routes/settings/media-library.js @@ -102,15 +102,15 @@ function filterAndSortAssets(assets, query) { filtered.sort(function (left, right) { const leftName = String(left.original_name || '').toLowerCase(); const rightName = String(right.original_name || '').toLowerCase(); - const leftDate = String(left.created_at || ''); - const rightDate = String(right.created_at || ''); if (query.sort === 'name-asc' || query.sort === 'name-desc') { return (query.sort === 'name-asc' ? 1 : -1) * leftName.localeCompare(rightName); } if (query.sort === 'size-asc' || query.sort === 'size-desc') { return (query.sort === 'size-asc' ? 1 : -1) * (Number(left.file_size) - Number(right.file_size)); } - return (query.sort === 'date-asc' ? 1 : -1) * leftDate.localeCompare(rightDate); + const leftDate = left.created_at instanceof Date ? left.created_at.getTime() : Date.parse(String(left.created_at || '')) || 0; + const rightDate = right.created_at instanceof Date ? right.created_at.getTime() : Date.parse(String(right.created_at || '')) || 0; + return (query.sort === 'date-asc' ? 1 : -1) * (leftDate - rightDate); }); return filtered; } diff --git a/test/media-library.test.js b/test/media-library.test.js index 2ae1158..83f41b5 100644 --- a/test/media-library.test.js +++ b/test/media-library.test.js @@ -25,6 +25,45 @@ test('media library normalizes only media URLs', () => { assert.equal(normalizeMediaPath('/assets/photo.png'), null); }); +test('media library sorts Date-valued creation timestamps chronologically', async () => { + const originalSyncMediaAssets = mediaLibrary.syncMediaAssetsFromDirectory; + const originalCountReferences = mediaLibrary.countMediaAssetReferences; + const assets = [ + { id: 1, original_name: 'October 14', media_type: 'image', file_size: 1, created_at: new Date('2026-10-14T12:00:00Z') }, + { id: 2, original_name: 'October 15', media_type: 'image', file_size: 1, created_at: new Date('2026-10-15T12:00:00Z') }, + { id: 3, original_name: 'October 17', media_type: 'image', file_size: 1, created_at: new Date('2026-10-17T12:00:00Z') } + ]; + const handlers = {}; + mediaLibrary.syncMediaAssetsFromDirectory = async function () { return 0; }; + mediaLibrary.countMediaAssetReferences = async function () { return 0; }; + + try { + registerMediaLibraryRoutes({ + get(route, ...middleware) { handlers[route] = middleware.at(-1); }, + post() {} + }, { + pool: { async query() { return [assets]; } }, + pages: {}, + upload: { single() { return function () {}; } }, + uploadDir: os.tmpdir(), + requirePermission() { return function () {}; } + }); + + const response = { json(data) { this.data = data; } }; + const next = function (error) { if (error) throw error; }; + const request = { currentUser: { permissions: [] }, query: { offset: '0', limit: '24', sort: 'date-desc' } }; + await handlers['/media-library/assets'](request, response, next); + assert.deepEqual(response.data.assets.map(asset => asset.original_name), ['October 17', 'October 15', 'October 14']); + + request.query.sort = 'date-asc'; + await handlers['/media-library/assets'](request, response, next); + assert.deepEqual(response.data.assets.map(asset => asset.original_name), ['October 14', 'October 15', 'October 17']); + } finally { + mediaLibrary.syncMediaAssetsFromDirectory = originalSyncMediaAssets; + mediaLibrary.countMediaAssetReferences = originalCountReferences; + } +}); + test('media library optimizes large raster uploads in place', async () => { const uploadDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pulse-media-optimize-')); const filePath = path.join(uploadDir, 'large.jpg');