-
Notifications
You must be signed in to change notification settings - Fork 894
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
Brave search conversion promotion in omnibox #12846
Conversation
a0da9e3
to
6659cc3
Compare
We want to locate conversion ask entry at second or last in omnibox popup. One more thing - when user select promotion entry, that promotion url could be visibile again in omnibox popup later like other history entries. In this case, history entry should have more priority? |
6c92777
to
648b01d
Compare
7f1bd18
to
a54829b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fallaciousreasoning Thanks for review! I think all of your comments are addressed or commented.
|
||
// If first match is not from search query with default provider, | ||
// it means there is more proper match from other providers. | ||
// In this case, remove promotion match from |result|. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
f22922e
to
e984d9a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but still worth having someone with a bit more experience in the brave codebase take a look.
Kindly ping :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to the minor feedback below, I noticed a couple of UI issues:
- There is an extra space character (" ") between the search term and the "- Brave Search" string.
- The result row is not highlighting on keyboard arrow navigation, only on mouse hover, but the other chromium result rows are
Similarly, I feel like with the banner option, the UX with the keyboard is a little strange. Using the keyboard I can press the down button and I know the banner is selected because the url changes to search.brave.com/xyz in the address bar, but there is no indication in the UI that it is the selected item. It does not have the left "selected indicator" nor does the background change via keyboard or mouse. I'm not sure if this is a design desicion so maybe we should ask @aguscruiz but I feel this banner row needs to either have a selection state, or not be selectable via keyboard.
Example here is where the banner is "selected" but it's not obvious in the UI:
|
||
ResetChildrenVisibility(); | ||
|
||
if (type_ == ConversionType::kButton) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be simpler and easier to read to have 2 separate views - one for the "button" type and another for the "banner" type? Either as children for this view or as replacement for this view.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this view has two childrens for each types.
button_type_container_
and banner_type_container_
for button and banner type.
Based on type, only one is visibile in this promotion view. Added comment about this - 68d8c81
// Use 14px font size for input text. | ||
views::Label::CustomFont emphasized_font = { | ||
GetFont(14, gfx::Font::Weight::SEMIBOLD)}; | ||
button_type_contents_input_ = input_container->AddChildView( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the screenshot this looks like it's the same or very similar text format as the regular search result row. Perhaps you could explain here, or leave a comment in the code as to why we're not using OmniboxMatchCellView
or even at the least OmniboxTextView
for the "button" version? I'm not very familiar with the omnibox views, so sorry if it's obvious why we can't use them. But I would have hoped we could do less custom views, and extend the existing ones. I'm sure there's a good reason for you to do it like this and I guess it's maybe because we have the button on the right and not underneath, so maybe leave a code comment, perhaps at the beginning of the ConfigureForButtonType
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it seems very similar but has slightly different for our purpose such as layout (you mentioned - buttons posiiton), bg, text colors or font. So, I decided to implement whole UI. In this case, I could set our view to whole area easily by only controlling upstream's view and our view's visibilty w/o touching upstream views.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@petemill On selection, selection indicator is only used and background color is not changed based on design spec. and only banner type spec(no indicator) is design decision.
cc @aguscruiz
|
||
ResetChildrenVisibility(); | ||
|
||
if (type_ == ConversionType::kButton) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this view has two childrens for each types.
button_type_container_
and banner_type_container_
for button and banner type.
Based on type, only one is visibile in this promotion view. Added comment about this - 68d8c81
// Use 14px font size for input text. | ||
views::Label::CustomFont emphasized_font = { | ||
GetFont(14, gfx::Font::Weight::SEMIBOLD)}; | ||
button_type_contents_input_ = input_container->AddChildView( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it seems very similar but has slightly different for our purpose such as layout (you mentioned - buttons posiiton), bg, text colors or font. So, I decided to implement whole UI. In this case, I could set our view to whole area easily by only controlling upstream's view and our view's visibilty w/o touching upstream views.
For option 3 (the small one) I think we can use the hover state as a focused state too, what do you think? As for option 2, I've mocked it up here, sorry, didn't think of that state but absolutely, needs a focused state. It's a thicker stroke (2px instead of 1) and in a slightly darker tone https://www.figma.com/file/8wncTFNBvKN7CS6LTXAMYs/Search-Conversion?node-id=770%3A38037 https://www.figma.com/file/8wncTFNBvKN7CS6LTXAMYs/Search-Conversion?node-id=770%3A38321 |
1fb90bf
to
c6bdf33
Compare
Kindly ping to @brave/deps-reviewers @brave/patch-reviewers @brave/chromium-src-reviewers :) |
c6bdf33
to
840832b
Compare
AddChildView(std::make_unique<OmniboxRowView>( | ||
i, edit_model_, | ||
- std::make_unique<OmniboxResultView>(this, edit_model_, i), | ||
+ std::make_unique<BraveOmniboxResultView>(this, edit_model_, i), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can replace OmniboxResultView
with BraveOmniboxResultView
in header/cc files?
using brave_search_conversion::ConversionType; | ||
|
||
void SortBraveSearchPromotionMatch( | ||
AutocompleteResult* result, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutable args should go last.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
AutocompleteResult* result, | ||
const AutocompleteInput& input, | ||
brave_search_conversion::ConversionType type) { | ||
if (result->size() == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DCHECK(result);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
void SetUp() override { | ||
SetMockLocale("en-US"); | ||
SetMockClient(); | ||
provider_ = new PromotionProvider(client_mock_.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base::MakeRefCounted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
namespace brave_search_conversion { | ||
|
||
bool IsPromotionEnabledCountry(const std::string& country_code) { | ||
static const base::flat_set<std::string> supported_country{"US", "CA", "DE", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base::NoDestructor
or just a simple array: const base::StringPiece kSupportedCountries[]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with simple array.
// Move match at |from| to |to|. | ||
void AutocompleteResult::MoveMatch(size_t from, size_t to) { | ||
DCHECK_LE(from, matches_.size()); | ||
DCHECK_LE(to, matches_.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be DCHECK_LT
, but actually we should do proper runtime if
checks. currently this looks like a crash after some upstream change.
RemoveMatch(matches_.begin() + from); | ||
|
||
if (from < to) | ||
to--; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if from == 0
and to == 1
, will this work as expected?
if (from < to) | ||
to--; | ||
|
||
matches_.insert(matches_.begin() + to, promotion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's rewrite this function via std::rotate
with iterators, should be much safer. some examples:
https://chromium-review.googlesource.com/c/chromium/src/+/1526299
https://stackoverflow.com/a/57399634
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with rotate
. It works great! 👍🏼
#define SetMatch \ | ||
virtual SetMatch(const AutocompleteMatch& match); \ | ||
friend class BraveOmniboxResultView; \ | ||
void SetMatchUnUsed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this way you don't need to copy-paste args and keep track of it on rebase:
#define SetMatch \
NotUsed(); \
friend class BraveOmniboxResultView; \
virtual void SetMatch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
if (prefs && prefs->GetBoolean(prefs::kDismissed)) | ||
return ConversionType::kNone; | ||
|
||
if (service) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should force prefs
and service
with DCHECK(prefs);
and DCHECK(service);
, otherwise this function can be misused.
We can split it into two and have the second one generate ConversionType
just by using static info (locale/country, features).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added GetConversionType()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@goodov Thanks for review. I think I address all comments except BraveOmniboxResultView
patching. It's still difficult for me.
#define SetMatch \ | ||
virtual SetMatch(const AutocompleteMatch& match); \ | ||
friend class BraveOmniboxResultView; \ | ||
void SetMatchUnUsed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
if (from < to) | ||
to--; | ||
|
||
matches_.insert(matches_.begin() + to, promotion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with rotate
. It works great! 👍🏼
namespace brave_search_conversion { | ||
|
||
bool IsPromotionEnabledCountry(const std::string& country_code) { | ||
static const base::flat_set<std::string> supported_country{"US", "CA", "DE", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with simple array.
void SetUp() override { | ||
SetMockLocale("en-US"); | ||
SetMockClient(); | ||
provider_ = new PromotionProvider(client_mock_.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
using brave_search_conversion::ConversionType; | ||
|
||
void SortBraveSearchPromotionMatch( | ||
AutocompleteResult* result, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
AutocompleteResult* result, | ||
const AutocompleteInput& input, | ||
brave_search_conversion::ConversionType type) { | ||
if (result->size() == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
if (prefs && prefs->GetBoolean(prefs::kDismissed)) | ||
return ConversionType::kNone; | ||
|
||
if (service) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added GetConversionType()
.
// Move match to |index|. | ||
void AutocompleteResult::ReorderMatch(const ACMatches::iterator& it, | ||
size_t index) { | ||
const bool move_to_end = (index < 0) || (index >= size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe index < 0
is always false. move to end works because (size_t)-1
creates max value which is always >= size()
. maybe make it as a flag or pass an iterator instead? currently index
doesn't do what it's declaring, this is confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I fixed making index
as int
type.
namespace features { | ||
|
||
// Brave search promotion match located at last low in omnibox popup. | ||
// This type seems more ads banner. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
more like ads banner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
bool IsPromotionEnabledCountry(const std::string& country_code) { | ||
constexpr base::StringPiece kSupportedCountries[] = {"US", "CA", "DE", "FR", | ||
"GB"}; | ||
return std::find(std::begin(kSupportedCountries), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base::Contains
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@goodov With match's additional info, code becomes much simpler.
Thanks and PTAL again!
namespace features { | ||
|
||
// Brave search promotion match located at last low in omnibox popup. | ||
// This type seems more ads banner. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
bool IsPromotionEnabledCountry(const std::string& country_code) { | ||
constexpr base::StringPiece kSupportedCountries[] = {"US", "CA", "DE", "FR", | ||
"GB"}; | ||
return std::find(std::begin(kSupportedCountries), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// Move match to |index|. | ||
void AutocompleteResult::ReorderMatch(const ACMatches::iterator& it, | ||
size_t index) { | ||
const bool move_to_end = (index < 0) || (index >= size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I fixed making index
as int
type.
@petemill @aguscruiz Updated screenshots that shows normal/hover or selected state. |
21e34c9
to
30dc2c9
Compare
enum class ConversionType { | ||
kButton, | ||
kBanner, | ||
kNone, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: usually this goes as a first item (kNone == 0
) in enums.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
providers_.push_back(new TopSitesProvider(provider_client_.get())); \ | ||
providers_.push_back(new SuggestedSitesProvider(provider_client_.get())); \ | ||
if (!provider_client_->IsOffTheRecord()) \ | ||
providers_.push_back(new PromotionProvider(provider_client_.get())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simonhong I think one more thing that we can add into this PR is to skip this and other Init
-like places when both features are turned off to make things safe in case we missed something.
you can make a bool function like bool IsBraveSearchConversionEnabled()
and insert it into some places to protect ourselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Added two places. here and UI side (https://github.com/brave/brave-core/pull/12846/files#diff-18e8763975abf287e0ef660d283f3485f5672dd3262390d753b738b132d58262R43).
I thinks double checking at both places (data provider and consumer) would be sufficient.
PromotionProvider Provides the autocomplete match for conversion promotion. And AutocompleteController adjust that match position based on conversion type.
Implement BraveOmniboxResultView by subclassing OmniboxResultView to provide promotion specfic views. BraveOmniboxResultView controls visibility upstream children and promotion children based on match. When promotion match is set, upstream children are all hidden and promotion children get visible.
Deleted chrome/test dependency from brave/components/search_engines. As //brave/components/search_engines target is included by /components/omnibox/browser due to brave search promotion, this deps caused error.
30dc2c9
to
0eb41e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work. UI looks really nice and polished.
One very minor issue I'm seeing that might be a general issue with the omnibox: Sometimes when I move the mouse from outside the item (for the Button version) to inside and then to outside again, the hover "sticks"
Another non-related issue, but worth noting is that the "set as default" button on brave search is always showing, yet it won't always work since the relevant browser API is limited to a certain amount of calls per day. The website needs to call brave.getCanSetDefaultSearchProvider
first to know whether to show the button or not.
Removed extra space between search term and Brave Search string. Added some comments about promotion view's children. Updated search promotion url Added more UI interaction for hover/selected state Updated test for checking promotion is not added in private profile
0eb41e9
to
983694e
Compare
Squashed into 4 commits and merged. |
Resolves brave/brave-browser#22616
This PR is for showing brave search promotion match in omnibox popup.
To do this, PromotionProvider adds promotion matches and BraveOmniboxResultView
renders promotion UI. There are two types of conversion. One is button type and the other is banner.
Button type match is located as a second match and Bannter type is located as a last match.
There position is re-arragned after upstreams match sorting.
First commit is
PromotionProvider
(non UI) part and second commit isBraveOmniboxResultView
(UI) part.Button type
Banner type
Submitter Checklist:
QA/Yes
orQA/No
;release-notes/include
orrelease-notes/exclude
;OS/...
) to the associated issuenpm run test -- brave_browser_tests
,npm run test -- brave_unit_tests
,npm run lint
,npm run gn_check
,npm run tslint
git rebase master
(if needed)Reviewer Checklist:
gn
After-merge Checklist:
changes has landed on
Test Plan:
Button type
--enable-features=BraveSearchOmniboxButton
command line switchBanner type
--enable-features=BraveSearchOmniboxBanner
command line switchNote: