Skip to content

Dynamic Dashboard: Reviews Card Reload Functionality #12752

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 27 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
40184d4
Add functionality to open all reviews from card.
hafizrahman May 15, 2024
ff9c78b
Add initial reloadData function for Reviews card.
hafizrahman May 16, 2024
37193af
Add db loading and display the result.
hafizrahman May 16, 2024
b61d592
Strip HTML from the review text.
hafizrahman May 16, 2024
2eea667
Use the existing ReviewViewModel for data.
hafizrahman May 16, 2024
a47f3bb
Add functionality to fetch products after getting review product IDs,…
hafizrahman May 16, 2024
616b88b
Update visibility handling.
hafizrahman May 16, 2024
42da0b8
Merge branch 'trunk'
hafizrahman May 16, 2024
12d09c8
Make sure to only get the latest three reviews.
hafizrahman May 16, 2024
12708f7
shorten name.
hafizrahman May 16, 2024
cb58f27
Simplify siteID predicate.
hafizrahman May 16, 2024
4e8571f
Set prefix with constant.
hafizrahman May 16, 2024
2b2151b
Add shimmer/redact to review items too when sync is happening.
hafizrahman May 16, 2024
3246160
Delete dummy data
hafizrahman May 16, 2024
34094ec
Update incorrect dummy data usage.
hafizrahman May 16, 2024
4fa99a1
Update func name.
hafizrahman May 16, 2024
4ad2e89
Update logic to get related products locally first, then fetch if nee…
hafizrahman May 17, 2024
23839f6
Fix viewModel not being @ObservedObject
hafizrahman May 17, 2024
3d9286c
Simplify ForEach since we don't need index, just check last item.
hafizrahman May 17, 2024
9aec7bc
Update the way reviewText snippet is shown in the swiftUI way.
hafizrahman May 17, 2024
382f342
More color fix and updates.
hafizrahman May 17, 2024
62be5a9
Various refactor for product fetching.
hafizrahman May 17, 2024
702678f
Merge branch 'trunk'
hafizrahman May 17, 2024
ae19a09
Update review text to use AttributedString.
hafizrahman May 17, 2024
6cb627b
Update WooCommerce/Classes/ViewRelated/Dashboard/Reviews/ReviewsDashb…
hafizrahman May 20, 2024
37fc482
Add fetch limit to the products results controller initializer
hafizrahman May 20, 2024
40bb44a
Refactor remote fetch functions that do not need to return results.
hafizrahman May 20, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ final class DashboardViewModel: ObservableObject {
group.addTask { [weak self] in
await self?.productStockCardViewModel.reloadData()
}
group.addTask { [weak self] in
await self?.reviewsViewModel.reloadData()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,14 @@ struct ReviewsDashboardCard: View {
/// Scale of the view based on accessibility changes
@ScaledMetric private var scale: CGFloat = 1.0

private let viewModel: ReviewsDashboardCardViewModel

let dummyData: [ProductReview] = [
ProductReview(siteID: 1, reviewID: 1, productID: 1, dateCreated: Date(),
statusKey: "approved", reviewer: "Sherlock", reviewerEmail: "", reviewerAvatarURL: "",
review: "The best product in the whole world. This is meant to be really long to be more than two lines",
rating: 5, verified: true),
ProductReview(siteID: 1, reviewID: 2, productID: 1, dateCreated: Date(),
statusKey: "hold", reviewer: "Holmes", reviewerEmail: "", reviewerAvatarURL: "",
review: "Amazing!", rating: 5, verified: true),
ProductReview(siteID: 1, reviewID: 3, productID: 1, dateCreated: Date(),
statusKey: "approved", reviewer: "", reviewerEmail: "", reviewerAvatarURL: "",
review: "", rating: 5, verified: true)
]
@State private var showingAllReviews: Bool = false

@ObservedObject private var viewModel: ReviewsDashboardCardViewModel

init(viewModel: ReviewsDashboardCardViewModel) {
self.viewModel = viewModel
}


var body: some View {
VStack(alignment: .leading, spacing: Layout.padding) {
header
Expand All @@ -39,19 +27,29 @@ struct ReviewsDashboardCard: View {
.shimmering(active: viewModel.syncingData)
Divider()

ForEach(Array(dummyData.enumerated()), id: \.element.reviewID) { index, review in
ReviewRow(for: review, isLastItem: index == dummyData.count-1)
}
Divider()
viewAllReviewsButton
.padding(.horizontal, Layout.padding)
if viewModel.data.isNotEmpty {
ForEach(viewModel.data, id: \.review.reviewID) { reviewViewModel in
reviewRow(for: reviewViewModel,
isLastItem: reviewViewModel == viewModel.data.last)
}
.redacted(reason: viewModel.syncingData ? [.placeholder] : [])
.shimmering(active: viewModel.syncingData)

Divider()

viewAllReviewsButton
.padding(.horizontal, Layout.padding)
.redacted(reason: viewModel.syncingData ? [.placeholder] : [])
.shimmering(active: viewModel.syncingData)
}
}
.padding(.vertical, Layout.padding)
.background(Color(.listForeground(modal: false)))
.clipShape(RoundedRectangle(cornerSize: Layout.cornerSize))
.padding(.horizontal, Layout.padding)
LazyNavigationLink(destination: ReviewsView(siteID: viewModel.siteID), isActive: $showingAllReviews) {
EmptyView()
}
}
}

Expand Down Expand Up @@ -106,27 +104,34 @@ private extension ReviewsDashboardCard {
}
}

func ReviewRow(for review: ProductReview, isLastItem: Bool) -> some View {
func reviewRow(for viewModel: ReviewViewModel, isLastItem: Bool) -> some View {
HStack(alignment: .top, spacing: 0) {
Image(systemName: "bubble.fill")
.foregroundStyle(review.status == .hold ? Color.secondary : Color(.wooCommercePurple(.shade60)))
.foregroundStyle(viewModel.review.status == .hold ? Color.secondary : Color(.wooCommercePurple(.shade60)))
.padding(.horizontal, Layout.padding)
.padding(.vertical, Layout.cardPadding)


VStack(alignment: .leading) {
// TODO: use actual product name
authorText(author: review.reviewer, productName: "Fallen Angel Candelabra")
.bodyStyle()
.padding(.trailing, Layout.padding)
reviewText(text: review.review, shouldDisplayStatus: review.status == .hold)
.lineLimit(2)
.subheadlineStyle()
.padding(.trailing, Layout.padding)
.renderedIf(review.review.isNotEmpty)
if review.rating > 0 {
if let subject = viewModel.subject {
Text(subject)
.bodyStyle()
.padding(.trailing, Layout.padding)
}

reviewText(text: viewModel.snippetData.reviewText,
pendingText: viewModel.snippetData.pendingReviewsText,
divider: viewModel.snippetData.dot,
textColor: viewModel.snippetData.textColor,
accentColor: viewModel.snippetData.accentColor,
shouldDisplayStatus: viewModel.shouldDisplayStatus)
.lineLimit(2)
.subheadlineStyle()
.padding(.trailing, Layout.padding)

if viewModel.review.rating > 0 {
HStack(spacing: Layout.starRatingSpacing) {
ForEach(0..<review.rating, id: \.self) { _ in
ForEach(0..<viewModel.review.rating, id: \.self) { _ in
Image(systemName: "star.fill")
.resizable()
.frame(width: Constants.starSize * scale, height: Constants.starSize * scale)
Expand All @@ -141,26 +146,45 @@ private extension ReviewsDashboardCard {
}
}

func authorText(author: String, productName: String) -> some View {
if author.isNotEmpty {
return Text(String.localizedStringWithFormat(Localization.completeAuthorText, author, productName))
} else {
return Text(String.localizedStringWithFormat(Localization.incompleteAuthorText, productName))
func reviewText(text: String,
pendingText: String,
divider: String,
textColor: UIColor,
accentColor: UIColor,
shouldDisplayStatus: Bool) -> some View {

var pendingAttributedString: AttributedString {
var result = AttributedString(pendingText)
result.foregroundColor = Color(uiColor: accentColor)
return result
}

var dividerAttributedString: AttributedString {
var result = AttributedString(divider)
result.foregroundColor = Color(uiColor: textColor)
return result
}

var reviewAttributedString: AttributedString {
var result = AttributedString(text)
result.foregroundColor = Color(uiColor: textColor)
return result
}
}

func reviewText(text: String, shouldDisplayStatus: Bool) -> some View {
if shouldDisplayStatus {
return Text(Localization.pendingReview).foregroundColor(Color(uiColor: .wooOrange)) +
Text(" • " + text)
} else {
return Text(text)
var reviewText: AttributedString {
if shouldDisplayStatus {
return pendingAttributedString + dividerAttributedString + reviewAttributedString
} else {
return reviewAttributedString
}
}

return Text(reviewText)
}

var viewAllReviewsButton: some View {
Button {
/* TODO */
showingAllReviews = true
} label: {
HStack {
Text(Localization.viewAll)
Expand All @@ -171,7 +195,6 @@ private extension ReviewsDashboardCard {
}
.disabled(viewModel.syncingData)
}

}

private extension ReviewsDashboardCard {
Expand Down Expand Up @@ -199,26 +222,11 @@ private extension ReviewsDashboardCard {
value: "Status",
comment: "Status label on the Reviews card's filter area."
)
static let pendingReview = NSLocalizedString(
"reviewsDashboardCard.pendingReview",
value: "Pending review",
comment: "Additional label on a review when its status is hold."
)
static let viewAll = NSLocalizedString(
"reviewsDashboardCard.viewAll",
value: "View all reviews",
comment: "Button to navigate to Reviews list screen."
)
static let completeAuthorText = NSLocalizedString(
"reviewsDashboardCard.completeAuthorText",
value: "%@ left a review on %@",
comment: "Text displayed when the author of a review is known."
)
static let incompleteAuthorText = NSLocalizedString(
"reviewsDashboardCard.incompleteAuthorText",
value: "A customer left a review on %@",
comment: "Text displayed when the author of a review is unknown."
)
}
}

Expand Down
Loading