From e13f48376b365b2239ab7c5439c7a6028272edcb Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Thu, 31 Jul 2025 16:15:01 +0200 Subject: [PATCH] Extend my likes list with last published information about the package. --- app/lib/frontend/handlers/account.dart | 15 +++++++++-- app/lib/frontend/templates/admin.dart | 4 +-- .../views/pkg/liked_package_list.dart | 27 +++++++++++++++---- app/lib/package/name_tracker.dart | 10 +++++++ .../frontend/golden/my_liked_packages.html | 6 +++++ app/test/frontend/templates_test.dart | 18 ++++++++++--- 6 files changed, 68 insertions(+), 12 deletions(-) diff --git a/app/lib/frontend/handlers/account.dart b/app/lib/frontend/handlers/account.dart index c55470a174..dad9e05040 100644 --- a/app/lib/frontend/handlers/account.dart +++ b/app/lib/frontend/handlers/account.dart @@ -4,7 +4,6 @@ import 'package:_pub_shared/data/account_api.dart'; import 'package:clock/clock.dart'; -import 'package:pub_dev/frontend/handlers/cache_control.dart'; import 'package:shelf/shelf.dart' as shelf; import '../../account/backend.dart'; @@ -15,6 +14,7 @@ import '../../account/session_cookie.dart' as session_cookie; import '../../audit/backend.dart'; import '../../frontend/request_context.dart'; import '../../package/backend.dart'; +import '../../package/name_tracker.dart'; import '../../publisher/backend.dart'; import '../../publisher/models.dart'; import '../../scorecard/backend.dart'; @@ -26,6 +26,9 @@ import '../../shared/urls.dart' as urls; import '../templates/admin.dart'; import '../templates/consent.dart'; import '../templates/misc.dart' show renderUnauthenticatedPage; +import '../templates/views/pkg/liked_package_list.dart'; + +import 'cache_control.dart'; /// Handles requests for /authorized shelf.Response authorizedHandler(_) => htmlResponse(renderAuthorizedPage()); @@ -295,10 +298,18 @@ Future accountMyLikedPackagesPageHandler( final user = (await accountBackend .lookupUserById(requestContext.authenticatedUserId!))!; final likes = await likeBackend.listPackageLikes(user); + // extending the like data with cached last-published timestamp + final combined = likes + .map((d) => LikeAndPackageData( + package: d.package!, + likeCreated: d.created!, + lastPublished: nameTracker.getPackage(d.package!)?.lastPublished, + )) + .toList(); final html = renderMyLikedPackagesPage( user: user, userSessionData: requestContext.sessionData!, - likes: likes, + likes: combined, ); return htmlResponse(html); } diff --git a/app/lib/frontend/templates/admin.dart b/app/lib/frontend/templates/admin.dart index 8c7d81e1ac..29a631db8f 100644 --- a/app/lib/frontend/templates/admin.dart +++ b/app/lib/frontend/templates/admin.dart @@ -4,7 +4,7 @@ import 'package:_pub_shared/data/page_data.dart'; -import '../../account/models.dart' show LikeData, User, SessionData; +import '../../account/models.dart' show User, SessionData; import '../../audit/models.dart'; import '../../frontend/templates/views/account/activity_log_table.dart'; import '../../package/models.dart'; @@ -96,7 +96,7 @@ String renderAccountPackagesPage({ String renderMyLikedPackagesPage({ required User user, required SessionData userSessionData, - required List likes, + required List likes, }) { final resultCount = likes.isNotEmpty ? d.p( diff --git a/app/lib/frontend/templates/views/pkg/liked_package_list.dart b/app/lib/frontend/templates/views/pkg/liked_package_list.dart index 83c6cc8668..3fa5d17545 100644 --- a/app/lib/frontend/templates/views/pkg/liked_package_list.dart +++ b/app/lib/frontend/templates/views/pkg/liked_package_list.dart @@ -2,15 +2,27 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import '../../../../account/models.dart'; import '../../../../shared/urls.dart' as urls; import '../../../dom/dom.dart' as d; import '../../../dom/material.dart' as material; import '../../../static_files.dart' show staticUrls; +/// Describes the combined like data that will be rendered. +class LikeAndPackageData { + final String package; + final DateTime likeCreated; + final DateTime? lastPublished; + + LikeAndPackageData({ + required this.package, + required this.likeCreated, + required this.lastPublished, + }); +} + /// Renders the package list of /my-liked-packages page. -d.Node likedPackageListNode(List likes) { +d.Node likedPackageListNode(List likes) { final thumbUpOutlinedUrl = staticUrls.getAssetUrl('/static/img/thumb-up-24px.svg'); final thumbUpFilledUrl = @@ -27,7 +39,7 @@ d.Node likedPackageListNode(List likes) { d.h3( classes: ['packages-title'], child: d.a( - href: urls.pkgPageUrl(like.package!), + href: urls.pkgPageUrl(like.package), text: like.package, ), ), @@ -37,7 +49,7 @@ d.Node likedPackageListNode(List likes) { unelevated: true, customTypeClass: '-pub-like-button', attributes: { - 'data-package': like.package!, + 'data-package': like.package, 'data-thumb_up_outlined': thumbUpOutlinedUrl, 'data-thumb_up_filled': thumbUpFilledUrl, }, @@ -56,7 +68,12 @@ d.Node likedPackageListNode(List likes) { classes: ['packages-metadata'], children: [ d.text(' Liked '), - d.xAgoTimestamp(like.created!, datePrefix: 'on'), + d.xAgoTimestamp(like.likeCreated, datePrefix: 'on'), + if (like.lastPublished != null) ...[ + d.br(), + d.text(' Published '), + d.xAgoTimestamp(like.lastPublished!, datePrefix: 'on'), + ], ], ), ], diff --git a/app/lib/package/name_tracker.dart b/app/lib/package/name_tracker.dart index 66b51b8703..34d654c1a6 100644 --- a/app/lib/package/name_tracker.dart +++ b/app/lib/package/name_tracker.dart @@ -150,6 +150,16 @@ class NameTracker { /// Whether the name tracker has a record of the package. bool hasPackage(String name) => _data._hasPackage(name); + /// Returns the tracked data of a package, or `null` if [name] is + /// not a visible package. + TrackedPackage? getPackage(String name) { + final p = _data._packages[name]; + if (p != null && p.isVisible) { + return p; + } + return null; + } + /// Scans the Datastore and populates the tracker. @visibleForTesting Future reloadFromDatastore() async { diff --git a/app/test/frontend/golden/my_liked_packages.html b/app/test/frontend/golden/my_liked_packages.html index dcba119b0c..94dde70a45 100644 --- a/app/test/frontend/golden/my_liked_packages.html +++ b/app/test/frontend/golden/my_liked_packages.html @@ -196,6 +196,9 @@

@@ -214,6 +217,9 @@

diff --git a/app/test/frontend/templates_test.dart b/app/test/frontend/templates_test.dart index ea8301fb32..91f2c2d5ec 100644 --- a/app/test/frontend/templates_test.dart +++ b/app/test/frontend/templates_test.dart @@ -10,7 +10,6 @@ import 'package:_pub_shared/validation/html/html_validation.dart'; import 'package:clock/clock.dart'; import 'package:html/parser.dart'; import 'package:pub_dev/account/backend.dart'; -import 'package:pub_dev/account/models.dart'; import 'package:pub_dev/admin/models.dart'; import 'package:pub_dev/audit/backend.dart'; import 'package:pub_dev/audit/models.dart'; @@ -28,6 +27,7 @@ import 'package:pub_dev/frontend/templates/package.dart'; import 'package:pub_dev/frontend/templates/package_admin.dart'; import 'package:pub_dev/frontend/templates/publisher.dart'; import 'package:pub_dev/frontend/templates/report.dart'; +import 'package:pub_dev/frontend/templates/views/pkg/liked_package_list.dart'; import 'package:pub_dev/frontend/templates/views/pkg/score_tab.dart'; import 'package:pub_dev/package/backend.dart'; import 'package:pub_dev/package/models.dart'; @@ -701,19 +701,31 @@ void main() { final authenticatedUser = await requireAuthenticatedWebUser(); final user = authenticatedUser.user; final liked1 = DateTime.fromMillisecondsSinceEpoch(1574423824000); + final published1 = liked1.add(Duration(days: 10)); final liked2 = DateTime.fromMillisecondsSinceEpoch(1574423824000); + final published2 = liked2.subtract(Duration(days: 10)); final html = renderMyLikedPackagesPage( user: user, userSessionData: requestContext.sessionData!, likes: [ - LikeData(package: 'super_package', created: liked1), - LikeData(package: 'another_package', created: liked2) + LikeAndPackageData( + package: 'super_package', + likeCreated: liked1, + lastPublished: published1, + ), + LikeAndPackageData( + package: 'another_package', + likeCreated: liked2, + lastPublished: published2, + ), ], ); expectGoldenFile(html, 'my_liked_packages.html', timestamps: { 'user-created': user.created, 'liked1': liked1, + 'published1': published1, 'liked2': liked2, + 'published2': published2, }); }); });