diff --git a/app/lib/frontend/dom/material.dart b/app/lib/frontend/dom/material.dart index 8262b85cfe..78ba46ff54 100644 --- a/app/lib/frontend/dom/material.dart +++ b/app/lib/frontend/dom/material.dart @@ -108,13 +108,14 @@ d.Node raisedButton({ /// Renders a two-state material icon button d.Node iconButton({ - required String id, + String? id, required bool isOn, Map? attributes, required d.Image onIcon, required d.Image offIcon, bool disabled = false, String? title, + List? classes, }) { return d.element( 'button', @@ -122,6 +123,7 @@ d.Node iconButton({ classes: [ 'mdc-icon-button', if (isOn) 'mdc-icon-button--on', + ...?classes, ], attributes: { ...?attributes, diff --git a/app/lib/frontend/templates/detail_page.dart b/app/lib/frontend/templates/detail_page.dart index c511aacf93..1002df1d02 100644 --- a/app/lib/frontend/templates/detail_page.dart +++ b/app/lib/frontend/templates/detail_page.dart @@ -10,16 +10,13 @@ import 'views/shared/detail/tabs.dart'; final wideHeaderDetailPageClassName = '-wide-header-detail-page'; /// Renders the detail page's header template. -/// -/// The like button in the header will not be displayed when [isLiked] is null. d.Node renderDetailHeader({ required d.Node titleNode, d.Image? image, - int? packageLikes, - bool? isLiked, bool isFlutterFavorite = false, d.Node? metadataNode, d.Node? tagsNode, + d.Node? likeNode, /// Set true for more whitespace in the header. bool isLoose = false, @@ -28,10 +25,9 @@ d.Node renderDetailHeader({ titleNode: titleNode, metadataNode: metadataNode, tagsNode: tagsNode, + likeNode: likeNode, image: image, isLoose: isLoose, - isLiked: isLiked == true, - likeCount: packageLikes, isFlutterFavorite: isFlutterFavorite, ); } diff --git a/app/lib/frontend/templates/package.dart b/app/lib/frontend/templates/package.dart index ccad207151..fb8ed28845 100644 --- a/app/lib/frontend/templates/package.dart +++ b/app/lib/frontend/templates/package.dart @@ -5,6 +5,7 @@ import 'package:_pub_shared/data/page_data.dart'; import 'package:_pub_shared/search/tags.dart'; import 'package:collection/collection.dart' show IterableExtension; +import 'package:pub_dev/frontend/templates/views/pkg/liked_package_list.dart'; import '../../package/models.dart'; import '../../package/overrides.dart' show devDependencyPackages; @@ -129,8 +130,11 @@ d.Node renderPkgHeader(PackagePageData data) { package: package.name!, version: data.version.version!, ), - packageLikes: package.likes, - isLiked: data.isLiked, + likeNode: renderLikeButtonAndLabel( + package: package.name!, + likeCount: package.likes, + isLiked: data.isLiked, + ), isFlutterFavorite: (package.assignedTags ?? []).contains(PackageTags.isFlutterFavorite), metadataNode: metadataNode, @@ -307,7 +311,6 @@ PageData pkgPageData( version: selectedVersion.version!, publisherId: package.publisherId, isDiscontinued: package.isDiscontinued, - likes: package.likes, isLatest: package.latestVersion == selectedVersion.version, ), sessionAware: editable, diff --git a/app/lib/frontend/templates/package_misc.dart b/app/lib/frontend/templates/package_misc.dart index 9412b7f397..b7c973026b 100644 --- a/app/lib/frontend/templates/package_misc.dart +++ b/app/lib/frontend/templates/package_misc.dart @@ -200,6 +200,7 @@ d.Node replacedByLink(String replacedBy) { /// Renders the labeled scores widget (the score values in a compact layout). d.Node labeledScoresNodeFromPackageView(PackageView view, {String? version}) { return labeledScoresNode( + package: view.name, pkgScorePageUrl: urls.pkgScoreUrl(view.name, version: version), likeCount: view.likes, grantedPubPoints: view.grantedPubPoints, diff --git a/app/lib/frontend/templates/views/pkg/labeled_scores.dart b/app/lib/frontend/templates/views/pkg/labeled_scores.dart index 147278c7a8..7b89eb4bae 100644 --- a/app/lib/frontend/templates/views/pkg/labeled_scores.dart +++ b/app/lib/frontend/templates/views/pkg/labeled_scores.dart @@ -7,11 +7,15 @@ import 'package:_pub_shared/format/number_format.dart'; import '../../../dom/dom.dart' as d; d.Node labeledScoresNode({ + required String package, required String pkgScorePageUrl, required int likeCount, required int? grantedPubPoints, required int? thirtyDaysDownloads, }) { + final formattedLikes = compactFormat(likeCount); + final formattedDownloads = + thirtyDaysDownloads == null ? null : compactFormat(thirtyDaysDownloads); return d.a( classes: ['packages-scores'], href: pkgScorePageUrl, @@ -20,9 +24,10 @@ d.Node labeledScoresNode({ classes: ['packages-score', 'packages-score-like'], child: _labeledScore( 'likes', - '${compactFormat(likeCount).value}' - '${compactFormat(likeCount).suffix}', + // keep in-sync with pkg/web_app/lib/src/likes.dart + '${formattedLikes.value}${formattedLikes.suffix}', sign: ''), + attributes: {'data-package': package}, ), d.div( classes: ['packages-score', 'packages-score-health'], @@ -35,9 +40,8 @@ d.Node labeledScoresNode({ classes: ['packages-score', 'packages-score-downloads'], child: _labeledScore( 'downloads', - thirtyDaysDownloads != null - ? '${compactFormat(thirtyDaysDownloads).value}' - '${compactFormat(thirtyDaysDownloads).suffix}' + formattedDownloads != null + ? '${formattedDownloads.value}${formattedDownloads.suffix}' : null, sign: '', ), 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..e3654f3ea8 100644 --- a/app/lib/frontend/templates/views/pkg/liked_package_list.dart +++ b/app/lib/frontend/templates/views/pkg/liked_package_list.dart @@ -2,6 +2,8 @@ // 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 'package:_pub_shared/format/number_format.dart'; + import '../../../../account/models.dart'; import '../../../../shared/urls.dart' as urls; @@ -64,3 +66,50 @@ d.Node likedPackageListNode(List likes) { ), ); } + +d.Node renderLikeButtonAndLabel( + {required String package, required int likeCount, required bool isLiked}) { + return d.div( + classes: ['like-button-and-label'], + children: [ + material.iconButton( + classes: ['like-button-and-label--button'], + isOn: isLiked, + onIcon: d.Image( + src: staticUrls.getAssetUrl('/static/img/like-active.svg'), + alt: 'liked status: active', + width: 18, + height: 18, + ), + offIcon: d.Image( + src: staticUrls.getAssetUrl('/static/img/like-inactive.svg'), + alt: 'liked status: inactive', + width: 18, + height: 18, + ), + title: isLiked ? 'Unlike this package' : 'Like this package', + attributes: { + 'data-ga-click-event': 'toggle-like', + 'aria-pressed': isLiked ? 'true' : 'false', + }, + ), + d.span( + classes: ['like-button-and-label--count-wrapper'], + child: d.span( + classes: ['like-button-and-label--count'], + text: _formatPackageLikes(likeCount), + attributes: { + 'data-package': package, + 'data-value': likeCount.toString(), + }, + ), + ), + ], + ); +} + +// keep in-sync with pkg/web_app/lib/src/likes.dart +String? _formatPackageLikes(int? likesCount) { + if (likesCount == null) return null; + return formatWithSuffix(likesCount); +} diff --git a/app/lib/frontend/templates/views/pkg/score_tab.dart b/app/lib/frontend/templates/views/pkg/score_tab.dart index aa2a3ce60e..33fcef5642 100644 --- a/app/lib/frontend/templates/views/pkg/score_tab.dart +++ b/app/lib/frontend/templates/views/pkg/score_tab.dart @@ -318,11 +318,13 @@ d.Node _likeKeyFigureNode(int? likeCount) { label: 'likes', ); } + final formatted = compactFormat(likeCount); + // keep in-sync with pkg/web_app/lib/src/likes.dart return _keyFigureNode( - value: '${compactFormat(likeCount).value}' - '${compactFormat(likeCount).suffix}', + value: '${formatted.value}${formatted.suffix}', supplemental: '', label: 'likes', + classes: ['score-key-figure--likes'], ); } @@ -367,9 +369,10 @@ d.Node _keyFigureNode({ required String value, required String supplemental, required String label, + List? classes, }) { return d.div( - classes: ['score-key-figure'], + classes: ['score-key-figure', ...?classes], children: [ d.div( classes: ['score-key-figure-title'], diff --git a/app/lib/frontend/templates/views/shared/detail/header.dart b/app/lib/frontend/templates/views/shared/detail/header.dart index 631e34ebd6..3d58913647 100644 --- a/app/lib/frontend/templates/views/shared/detail/header.dart +++ b/app/lib/frontend/templates/views/shared/detail/header.dart @@ -2,10 +2,7 @@ // 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 'package:_pub_shared/format/number_format.dart'; - import '../../../../dom/dom.dart' as d; -import '../../../../dom/material.dart' as material; import '../../../../static_files.dart'; d.Node detailHeaderNode({ @@ -13,8 +10,7 @@ d.Node detailHeaderNode({ required d.Node? metadataNode, required d.Node? tagsNode, required d.Image? image, - required bool isLiked, - required int? likeCount, + required d.Node? likeNode, required bool isFlutterFavorite, /// Set true for more whitespace in the header. @@ -116,50 +112,13 @@ d.Node detailHeaderNode({ child: titleNode, ), d.div(classes: ['metadata'], child: metadataNode), - if (tagsNode != null || likeCount != null) + if (tagsNode != null || likeNode != null) d.div( classes: ['detail-tags-and-like'], children: [ if (tagsNode != null) d.div(classes: ['detail-tags'], child: tagsNode), - if (likeCount != null) - d.div( - classes: ['detail-like'], - children: [ - material.iconButton( - id: '-pub-like-icon-button', - isOn: isLiked, - onIcon: d.Image( - src: staticUrls - .getAssetUrl('/static/img/like-active.svg'), - alt: 'liked status: active', - width: 18, - height: 18, - ), - offIcon: d.Image( - src: staticUrls.getAssetUrl( - '/static/img/like-inactive.svg'), - alt: 'liked status: inactive', - width: 18, - height: 18, - ), - title: isLiked - ? 'Unlike this package' - : 'Like this package', - attributes: { - 'data-ga-click-event': 'toggle-like', - 'aria-pressed': isLiked ? 'true' : 'false', - }, - ), - d.span( - classes: ['likes-count'], - child: d.span( - id: 'likes-count', - text: _formatPackageLikes(likeCount), - ), - ), - ], - ), + if (likeNode != null) likeNode, ], ), ], @@ -170,9 +129,3 @@ d.Node detailHeaderNode({ ), ]); } - -// keep in-sync with pkg/web_app/lib/src/likes.dart -String? _formatPackageLikes(int? likesCount) { - if (likesCount == null) return null; - return formatWithSuffix(likesCount); -} diff --git a/app/test/frontend/golden/my_packages.html b/app/test/frontend/golden/my_packages.html index 0b6951dd63..249ba29977 100644 --- a/app/test/frontend/golden/my_packages.html +++ b/app/test/frontend/golden/my_packages.html @@ -201,7 +201,7 @@

%%x-ago%% -
+