Skip to content

Commit

Permalink
Product Details: Display cover tag on the first product image (#15041)
Browse files Browse the repository at this point in the history
  • Loading branch information
itsmeichigo authored Feb 5, 2025
2 parents a5f402a + 7439764 commit 7e6c816
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 13 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*** Use [*****] to indicate smoke tests of all critical flows should be run on the final IPA before release (e.g. major library or OS update).
21.7
-----
- [*] Product Details: Display cover tag on the first product image [https://github.com/woocommerce/woocommerce-ios/pull/15041]
- [*] Payments: Update learn more links to open Stripe-specific docs when using that gateway [https://github.com/woocommerce/woocommerce-ios/pull/15035]
- [x] Now, usernames and emails in text fields across multiple login views are no longer capitalized. [https://github.com/woocommerce/woocommerce-ios/pull/15002]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,33 @@ final class ProductImageCollectionViewCell: UICollectionViewCell {

var cancellableTask: Task<Void, Never>?

private(set) lazy var coverTagView: UIView = {
let containerView = UIView(frame: .zero)
containerView.backgroundColor = UIColor.primary
containerView.clipsToBounds = true
containerView.isHidden = true
containerView.layer.cornerRadius = Constants.tagCornerRadius
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(tagLabel)
containerView.pinSubviewToAllEdges(tagLabel, insets: Constants.tagEdgeInsets)
return containerView
}()

private lazy var tagLabel: UILabel = {
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.applyCaption1Style()
label.textColor = UIColor(light: .white, dark: .black)
label.text = Localization.tagLabel
return label
}()

override func awakeFromNib() {
super.awakeFromNib()
configureBackground()
configureImageView()
configureCellAppearance()
configureCoverTagView()
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
Expand Down Expand Up @@ -46,6 +68,14 @@ private extension ProductImageCollectionViewCell {
contentView.layer.borderColor = Colors.borderColor.cgColor
contentView.layer.masksToBounds = Settings.maskToBounds
}

func configureCoverTagView() {
contentView.addSubview(coverTagView)
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: coverTagView.leadingAnchor, constant: -Constants.tagPadding),
contentView.topAnchor.constraint(equalTo: coverTagView.topAnchor, constant: -Constants.tagPadding),
])
}
}

/// Constants
Expand All @@ -54,6 +84,9 @@ private extension ProductImageCollectionViewCell {
enum Constants {
static let cornerRadius = CGFloat(2.0)
static let borderWidth = CGFloat(0.5)
static let tagPadding = CGFloat(8)
static let tagCornerRadius = CGFloat(4)
static let tagEdgeInsets = UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4)
}

enum Colors {
Expand All @@ -65,4 +98,12 @@ private extension ProductImageCollectionViewCell {
static let imageContentMode = ContentMode.center
static let maskToBounds = true
}

enum Localization {
static let tagLabel = NSLocalizedString(
"productImageCollectionViewCell.tagLabel.text",
value: "Cover",
comment: "Label indicating the cover image of a product"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ private extension ProductImagesCollectionViewDataSource {
func configure(collectionView: UICollectionView, _ cell: UICollectionViewCell, for item: ProductImagesItem, at indexPath: IndexPath) {
switch item {
case .image(let status):
configureImageCell(cell, productImageStatus: status)
let isFirstImage = indexPath.item == 0
configureImageCell(cell, productImageStatus: status, isFirstImage: isFirstImage)
case .extendedAddImage(let isVariation):
if let cell = cell as? ExtendedAddProductImageCollectionViewCell {
cell.configurePlaceholderLabelForProductImages(isVariation: isVariation)
Expand All @@ -48,10 +49,10 @@ private extension ProductImagesCollectionViewDataSource {
}
}

func configureImageCell(_ cell: UICollectionViewCell, productImageStatus: ProductImageStatus) {
func configureImageCell(_ cell: UICollectionViewCell, productImageStatus: ProductImageStatus, isFirstImage: Bool) {
switch productImageStatus {
case .remote(let image):
configureRemoteImageCell(cell, productImage: image)
configureRemoteImageCell(cell, productImage: image, isFirstImage: isFirstImage)
case .uploading(let asset):
switch asset {
case .phAsset(let asset):
Expand All @@ -62,7 +63,7 @@ private extension ProductImagesCollectionViewDataSource {
}
}

func configureRemoteImageCell(_ cell: UICollectionViewCell, productImage: ProductImage) {
func configureRemoteImageCell(_ cell: UICollectionViewCell, productImage: ProductImage, isFirstImage: Bool) {
guard let cell = cell as? ProductImageCollectionViewCell else {
fatalError()
}
Expand All @@ -83,6 +84,7 @@ private extension ProductImagesCollectionViewDataSource {
cell?.imageView.contentMode = .scaleAspectFit
cell?.imageView.image = image
}
cell.coverTagView.isHidden = !isFirstImage
}

func configureUploadingImageCell(_ cell: UICollectionViewCell, asset: PHAsset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,19 @@ extension ProductImagesCollectionViewController {
let productImageStatus = productImageStatuses[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: productImageStatus.cellReuseIdentifier,
for: indexPath)
configureCell(cell, productImageStatus: productImageStatus)
let isFirstImage = indexPath.row == 0
configureCell(cell, productImageStatus: productImageStatus, isFirstImage: isFirstImage)
return cell
}
}

// MARK: Cell configurations
//
private extension ProductImagesCollectionViewController {
func configureCell(_ cell: UICollectionViewCell, productImageStatus: ProductImageStatus) {
func configureCell(_ cell: UICollectionViewCell, productImageStatus: ProductImageStatus, isFirstImage: Bool) {
switch productImageStatus {
case .remote(let image):
configureRemoteImageCell(cell, productImage: image)
configureRemoteImageCell(cell, productImage: image, isFirstImage: isFirstImage)
case .uploading(let asset):
switch asset {
case .phAsset(let asset):
Expand All @@ -94,7 +95,7 @@ private extension ProductImagesCollectionViewController {
}
}

func configureRemoteImageCell(_ cell: UICollectionViewCell, productImage: ProductImage) {
func configureRemoteImageCell(_ cell: UICollectionViewCell, productImage: ProductImage, isFirstImage: Bool) {
guard let cell = cell as? ProductImageCollectionViewCell else {
fatalError()
}
Expand All @@ -116,6 +117,7 @@ private extension ProductImagesCollectionViewController {
cell.imageView.contentMode = .scaleAspectFit
cell.imageView.image = image
}
cell.coverTagView.isHidden = !isFirstImage
}

func configureUploadingImageCell(_ cell: UICollectionViewCell, asset: PHAsset) {
Expand Down Expand Up @@ -254,7 +256,9 @@ extension ProductImagesCollectionViewController: UICollectionViewDragDelegate, U
}, completion: { [weak self] _ in
// [Workaround] Reload the collection view if there are more than
// one type of cells, for example, when there are any pending upload.
self?.reloadCollectionViewIfNeeded()
// Reloading is also necessary when the first image is updated.
let firstImageUpdated = item.sourceIndexPath?.item == 0 || destinationIndexPath.item == 0
self?.reloadCollectionViewIfNeeded(firstImageUpdated: firstImageUpdated)
})

coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
Expand All @@ -264,9 +268,10 @@ extension ProductImagesCollectionViewController: UICollectionViewDragDelegate, U
/// Reloads collection view only if there is any pending upload.
/// This makes sure that cells for pending uploads are reloaded properly
/// to remove their overlays after uploading is done.
/// If the first image is updated, reloading is also necessary to update the Cover tag.
///
private func reloadCollectionViewIfNeeded() {
if productImageStatuses.hasPendingUpload {
private func reloadCollectionViewIfNeeded(firstImageUpdated: Bool) {
if firstImageUpdated || productImageStatuses.hasPendingUpload {
collectionView.reloadData()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,10 @@ private extension ProductImagesViewController {
static let replacePhoto = NSLocalizedString("Replace Photo", comment: "Action to replace one photo on the Product images screen")
static let variableProductHelperText = NSLocalizedString("Only one photo can be displayed by variation",
comment: "Helper text above photo list in Product images screen")
static let dragAndDropHelperText = NSLocalizedString("Drag and drop to re-order photos",
comment: "Drag and drop helper text above photo list in Product images screen")
static let dragAndDropHelperText = NSLocalizedString(
"productImagesViewController.dragAndDropHelperText",
value: "Drag and drop to re-order photos. The first photo will be set as the cover.",
comment: "Drag and drop helper text above photo list in Product images screen"
)
}
}

0 comments on commit 7e6c816

Please sign in to comment.