blob: 29cf3f17f95d2d3fa2e23b81a09944d887e61d9f [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/commerce/discounts_icon_view.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/views/commerce/discounts_bubble_dialog_view.h"
#include "components/commerce/core/commerce_feature_list.h"
#include "components/commerce/core/metrics/discounts_metric_collector.h"
#include "components/strings/grit/components_strings.h"
#include "components/tabs/public/tab_interface.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/view_class_properties.h"
DiscountsIconView::DiscountsIconView(
IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
PageActionIconView::Delegate* page_action_icon_delegate)
: PageActionIconView(nullptr,
0,
icon_label_bubble_delegate,
page_action_icon_delegate,
"Discounts") {
GetViewAccessibility().SetName(
l10n_util::GetStringUTF16(IDS_DISCOUNT_ICON_EXPANDED_TEXT));
SetUpForInOutAnimation();
SetProperty(views::kElementIdentifierKey, kDiscountsChipElementId);
}
DiscountsIconView::~DiscountsIconView() = default;
views::BubbleDialogDelegate* DiscountsIconView::GetBubble() const {
const commerce::CommerceUiTabHelper* tab_helper = GetTabHelper();
if (!tab_helper) {
return nullptr;
}
return tab_helper->GetDiscountsBubbleCoordinator().GetBubble();
}
void DiscountsIconView::SetIsLabelExpanded(bool is_expanded) {
is_label_expanded_ = is_expanded;
OnPropertyChanged(&is_label_expanded_, views::kPropertyEffectsNone);
}
bool DiscountsIconView::GetIsLabelExpanded() const {
return is_label_expanded_;
}
base::CallbackListSubscription
DiscountsIconView::AddIsLabelExpandedChangedCallback(
views::PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&is_label_expanded_, std::move(callback));
}
void DiscountsIconView::OnExecuting(
PageActionIconView::ExecuteSource execute_source) {
MaybeShowBubble(/*from_user=*/true);
commerce::CommerceUiTabHelper* tab_helper = GetTabHelper();
if (!tab_helper) {
return;
}
commerce::metrics::DiscountsMetricCollector::
RecordDiscountsPageActionIconClicked(
tab_helper->IsPageActionIconExpanded(PageActionIconType::kDiscounts),
tab_helper->GetDiscounts());
}
const gfx::VectorIcon& DiscountsIconView::GetVectorIcon() const {
return vector_icons::kShoppingmodeIcon;
}
void DiscountsIconView::UpdateImpl() {
bool should_show = ShouldShow();
if (should_show) {
MaybeShowPageActionLabel();
} else {
scoped_window_call_to_action_ptr_.reset();
HidePageActionLabel();
}
SetBackgroundVisibility(BackgroundVisibility::kWithLabel);
SetVisible(should_show);
}
bool DiscountsIconView::ShouldShow() {
if (!GetWebContents() || delegate()->ShouldHidePageActionIcons()) {
return false;
}
commerce::CommerceUiTabHelper* tab_helper = GetTabHelper();
return tab_helper && tab_helper->ShouldShowDiscountsIconView();
}
void DiscountsIconView::MaybeShowPageActionLabel() {
commerce::CommerceUiTabHelper* tab_helper = GetTabHelper();
if (!tab_helper ||
!tab_helper->ShouldExpandPageActionIcon(PageActionIconType::kDiscounts)) {
return;
}
if (!tabs::TabInterface::GetFromContents(GetWebContents())
->GetBrowserWindowInterface()
->CanShowCallToAction()) {
return;
}
scoped_window_call_to_action_ptr_ =
tabs::TabInterface::GetFromContents(GetWebContents())
->GetBrowserWindowInterface()
->ShowCallToAction();
should_extend_label_shown_duration_ = true;
AnimateIn(IDS_DISCOUNT_ICON_EXPANDED_TEXT);
}
void DiscountsIconView::HidePageActionLabel() {
UnpauseAnimation();
ResetSlideAnimation(false);
}
void DiscountsIconView::AnimationProgressed(const gfx::Animation* animation) {
PageActionIconView::AnimationProgressed(animation);
// When the label is fully revealed pause the animation for
// kLabelPersistDuration before resuming the animation and allowing the label
// to animate out. This is currently set to show for 12s including the in/out
// animation.
// TODO(crbug.com/40832707): This approach of inspecting the animation
// progress to extend the animation duration is quite hacky. This should be
// removed and the IconLabelBubbleView API expanded to support a finer level
// of control.
constexpr double kAnimationValueWhenLabelFullyShown = 0.5;
constexpr base::TimeDelta kLabelPersistDuration = base::Seconds(10.8);
if (should_extend_label_shown_duration_ &&
GetAnimationValue() >= kAnimationValueWhenLabelFullyShown) {
should_extend_label_shown_duration_ = false;
PauseAnimation();
animate_out_timer_.Start(
FROM_HERE, kLabelPersistDuration,
base::BindRepeating(&DiscountsIconView::UnpauseAnimation,
base::Unretained(this)));
SetIsLabelExpanded(true);
MaybeShowBubble(false);
}
}
void DiscountsIconView::UnpauseAnimation() {
IconLabelBubbleView::UnpauseAnimation();
// The label is collapsed during UnpauseAnimation, so sets the label is
// expanded to false here. See the comment in AnimationProgressed for more
// details.
SetIsLabelExpanded(false);
}
commerce::CommerceUiTabHelper* DiscountsIconView::GetTabHelper() {
return const_cast<commerce::CommerceUiTabHelper*>(
std::as_const(*this).GetTabHelper());
}
const commerce::CommerceUiTabHelper* DiscountsIconView::GetTabHelper() const {
auto* web_contents = GetWebContents();
if (!web_contents) {
return nullptr;
}
return tabs::TabInterface::GetFromContents(web_contents)
->GetTabFeatures()
->commerce_ui_tab_helper();
}
void DiscountsIconView::MaybeShowBubble(bool from_user) {
commerce::CommerceUiTabHelper* tab_helper = GetTabHelper();
if (!tab_helper) {
return;
}
const std::vector<commerce::DiscountInfo>& discount_infos =
tab_helper->GetDiscounts();
CHECK(!discount_infos.empty());
// Currently only uses the first discount info.
bool should_auto_show = tab_helper->ShouldAutoShowDiscountsBubble(
discount_infos[0].id, discount_infos[0].is_merchant_wide);
if (!from_user && !should_auto_show) {
return;
}
if (!from_user && should_auto_show) {
// If commerce::kDiscountDialogAutoPopupCounterfactual is enabled, we
// purposely not show the bubble.
bool should_suppress = base::FeatureList::IsEnabled(
commerce::kDiscountDialogAutoPopupCounterfactual);
commerce::metrics::DiscountsMetricCollector::
RecordDiscountAutoPopupEligibleButSuppressed(
GetWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId(),
should_suppress);
if (should_suppress) {
return;
}
}
if (animate_out_timer_.IsRunning()) {
animate_out_timer_.Stop();
}
tab_helper->ShowDiscountBubble(
discount_infos[0], base::BindOnce(&DiscountsIconView::UnpauseAnimation,
weak_ptr_factory_.GetWeakPtr()));
tab_helper->DiscountsBubbleShown(discount_infos[0].id);
commerce::metrics::DiscountsMetricCollector::RecordDiscountBubbleShown(
should_auto_show,
GetWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId(),
tab_helper->GetDiscounts());
}
BEGIN_METADATA(DiscountsIconView)
END_METADATA