1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_CUSTOM_HELP_BUBBLE_H_
#define COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_CUSTOM_HELP_BUBBLE_H_
#include <concepts>
#include "base/callback_list.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/user_education/common/help_bubble/help_bubble.h"
#include "ui/base/interaction/element_tracker.h"
namespace user_education {
class CustomHelpBubbleViews;
// Custom Help Bubbles
// A custom help bubble is a `HelpBubble` that wraps some UI that behaves like a
// help bubble, but is not one of the help bubbles defined here in
// `components/user_education`.
//
// The UI of a custom help bubble should implement `CustomHelpBubbleUi`,
// which allows it to send the same signals back to the promo controller as
// normal help bubble UIs do.
//
// This UI should be wrapped in a `HelpBubble` object that also implements
// `CustomHelpBubble`, which allows access to the special interface. Common
// implementations that wrap e.g. Views bubble dialogs and WebUI dialogs will
// be provided.
//
// All of these requirements are enforced via template requirements.
// This is an interface that must be implemented by custom help bubble UIs (or
// their controllers). Custom help bubble UI should not close themselves in
// response to user input but instead notify the system via `NotifyUserAction()`
// with one of the `UserAction` values.
//
// These user actions are observed by the promo controller, which uses them to
// record usage data, emit histograms, and properly mark the promo as dismissed
// or snoozed.
//
// Custom bubbles may be closed due to UI changes outside the bubble, via the
// promo being canceled, or through calls like `NotifyPromoFeatureUsed()`,
// `CloseBubbleAndContinuePromo()`, etc. These will be handled by the promo
// controller, not the bubble itself.
class CustomHelpBubbleUi {
public:
// Subset of `FeaturePromoClosedReason` that indicates the user engaged with
// the promo and the custom help bubble should close.
//
// A custom help bubble should not close itself, but rather, should notify one
// of these user actions and then wait to be closed.
enum class UserAction {
// The user pressed a button like "Got it" or "No thanks". The bubble will
// not be able to be shown again.
//
// This is differentiated from `kCancel` for metrics reasons - we want to
// know if the user interacted with an action button or just reflexively
// closed the bubble.
kDismiss,
// The user pressed a button like "Maybe later". This enables the help
// bubble to be shown again at a later time.
kSnooze,
// The user pressed a button or link that did some action in the browser,
// turned on a setting, etc.
kAction,
// The user pressed a default close button (X) or pressed ESC. All custom
// UI that are not mandatory legal or privacy messaging should have an (X)
// button and respond to ESC.
//
// This is differentiated from `kDismiss` for metrics reasons - we want to
// know if the user interacted with an action button or just reflexively
// closed the bubble.
kCancel,
};
using UserActionCallback = base::OnceCallback<void(UserAction)>;
CustomHelpBubbleUi();
CustomHelpBubbleUi(CustomHelpBubbleUi& other) = delete;
void operator=(CustomHelpBubbleUi& other) = delete;
virtual ~CustomHelpBubbleUi();
// Registers a callback to be invoked by the bubble (via `NotifyUserAction()`)
// when something causes the bubble to want to close itself (eg. a click on a
// Snooze button).
//
// The promo controller adds a callback when the bubble is registered,
// responds to the callback by closing the bubble.
base::CallbackListSubscription AddUserActionCallback(
UserActionCallback callback);
// Get this object as a weak pointer.
base::WeakPtr<CustomHelpBubbleUi> GetCustomUiAsWeakPtr();
protected:
// Notifies listeners that the user has done some significant input which
// should close the help bubble (such as clicking on "Snooze" button or on a
// link). The promo controller will record the result and close the bubble.
//
// Be aware that `this` may not be valid after calling this method.
void NotifyUserAction(UserAction user_action);
private:
friend class CustomHelpBubbleViews;
std::unique_ptr<base::OnceCallbackList<void(UserAction)>>
user_action_callbacks_;
base::WeakPtrFactory<CustomHelpBubbleUi> weak_ptr_factory_{this};
};
// Add-on interface for `HelpBubble`s that wrap `CustomHelpBubbleUi`.
class CustomHelpBubble {
public:
explicit CustomHelpBubble(CustomHelpBubbleUi& bubble);
virtual ~CustomHelpBubble();
CustomHelpBubbleUi* custom_bubble_ui() { return bubble_.get(); }
const CustomHelpBubbleUi* custom_bubble_ui() const { return bubble_.get(); }
private:
base::WeakPtr<CustomHelpBubbleUi> bubble_;
};
// A custom help bubble is both a `CustomHelpBubble` and a `HelpBubble`.
template <typename T>
concept IsCustomHelpBubble =
std::derived_from<T, HelpBubble> && std::derived_from<T, CustomHelpBubble>;
} // namespace user_education
#endif // COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_CUSTOM_HELP_BUBBLE_H_
|