Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Search Settings Panel: Search Suggestions (Part 1) #2726

Merged
merged 9 commits into from
Aug 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion app/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ let generateBraveManifest = () => {
'form-action': '\'none\'',
'referrer': 'no-referrer',
'style-src': '\'self\' \'unsafe-inline\'',
'img-src': '\'self\' data:'
'img-src': '* data:'
}

if (process.env.NODE_ENV === 'development') {
Expand Down
4 changes: 3 additions & 1 deletion app/extensions/brave/locales/en-US/preferences.properties
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ startsWithOptionLastTime=my windows / tabs from last time
startsWithOptionHomePage=my home page
startsWithOptionNewTabPage=the new tab page
myHomepage=My homepage is
defaultSearchEngine=Default search engine:
default=Default
searchEngine=Search Engine
engineGoKey=Engine Go Key (Type First)
switchToNewTabs=Switch to new tabs immediately
paintTabs=Show tabs in page theme color
tabsPerTabPage=Number of tabs per tab set:
Expand Down
7 changes: 7 additions & 0 deletions app/sessionStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ module.exports.loadAppState = () => {

try {
data = Object.assign(module.exports.defaultAppState(), JSON.parse(data))
// xml migration
if (data.settings['search.default-search-engine'] === 'content/search/google.xml') {
data.settings['search.default-search-engine'] = 'Google'
}
if (data.settings['search.default-search-engine'] === 'contnt/search/duckduckgo.xml') {
data.settings['search.default-search-engine'] = 'DuckDuckGo'
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great!

} catch (e) {
// TODO: Session state is corrupted, maybe we should backup this
// corrupted value for people to report into support.
Expand Down
2 changes: 1 addition & 1 deletion docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ AppStore
'general.downloads.default-save-path': string, // default path for saving files
'general.autohide-menu': boolean, // true if the Windows menu should be autohidden
'general.disable-title-mode': boolean, // true if title mode should always be disabled
'search.default-search-engine': string, // path to the open search XML
'search.default-search-engine': string, // name of search engine, from js/data/searchProviders.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to be set inside app.sessionStore based on the user's old selected xml engine. And only done once, after that it should jus preserve the setting. Also some users won't have an old xml setting if it is their first time install.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the same setting name as before so just check for special case values of content/search/google.xml and content/search/duckduckgo.xml when loading the session state and migrate accordingly.

'search.offer-search-suggestions': boolean, // true if suggestions should be offered from the default search engine when available.
'tabs.switch-to-new-tabs': boolean, // true if newly opened tabs should be focused immediately
'tabs.paint-tabs': boolean, // true if the page theme color and favicon color should be used for tabs
Expand Down
69 changes: 60 additions & 9 deletions js/about/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const messages = require('../constants/messages')
const settings = require('../constants/settings')
const aboutActions = require('./aboutActions')
const getSetting = require('../settings').getSetting
const SortableTable = require('../components/sortableTable')
const searchProviders = require('../data/searchProviders')

const adblock = appConfig.resourceNames.ADBLOCK
const cookieblock = appConfig.resourceNames.COOKIEBLOCK
Expand Down Expand Up @@ -173,19 +175,68 @@ class GeneralTab extends ImmutableComponent {
}
}

class SearchSelectEntry extends ImmutableComponent {
shouldComponentUpdate (nextProps, nextState) {
return this.props.settings.get('search.default-search-engine') !== nextProps.settings.get('search.default-search-engine')
}
render () {
return <div>
{this.props.settings.get('search.default-search-engine') === this.props.name
? <span className='fa fa-check-square' id='searchSelectIcon' /> : null}
</div>
}
}

class SearchEntry extends ImmutableComponent {
render () {
return <div>
<span style={this.props.iconStyle}>
</span>
<span style={{paddingLeft: '5px', verticalAlign: 'middle'}}>{this.props.name}</span>
</div>
}
}

class SearchShortcutEntry extends ImmutableComponent {
render () {
return <div style={{paddingLeft: '5px', verticalAlign: 'middle'}}>
{this.props.shortcut}
</div>
}
}

class SearchTab extends ImmutableComponent {
get searchProviders () {
let entries = searchProviders.providers
let array = []
const iconSize = 16
entries.forEach((entry) => {
let iconStyle = {
backgroundImage: `url(${entry.image})`,
minWidth: iconSize,
width: iconSize,
backgroundSize: iconSize,
height: iconSize,
display: 'inline-block',
verticalAlign: 'middle'
}
array.push([<SearchSelectEntry name={entry.name} settings={this.props.settings} />,
<SearchEntry name={entry.name} iconStyle={iconStyle}
onChangeSetting={this.props.onChangeSetting} />,
<SearchShortcutEntry shortcut={entry.shortcut} />])
})
return array
}

hoverCallback (rows) {
this.props.onChangeSetting(settings.DEFAULT_SEARCH_ENGINE, rows[1].props.children.props.name)
}

render () {
return <div>
<div className='sectionTitle' data-l10n-id='searchSettings' />
<SettingsList>
<SettingItem dataL10nId='defaultSearchEngine'>
<select value={getSetting(settings.DEFAULT_SEARCH_ENGINE, this.props.settings)}
onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.DEFAULT_SEARCH_ENGINE)}>
<option value='content/search/google.xml'>Google</option>
<option value='content/search/duckduckgo.xml'>DuckDuckGo</option>
</select>
</SettingItem>
</SettingsList>
<SortableTable headings={['default', 'searchEngine', 'engineGoKey']} rows={this.searchProviders}
isHover hoverCallback={this.hoverCallback.bind(this)} />
<div className='sectionTitle' data-l10n-id='locationBarSettings' />
<SettingsList>
<SettingCheckbox dataL10nId='showHistoryMatches' prefKey={settings.HISTORY_SUGGESTIONS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} />
Expand Down
24 changes: 17 additions & 7 deletions js/components/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const ipc = electron.ipcRenderer
// Actions
const windowActions = require('../actions/windowActions')
const webviewActions = require('../actions/webviewActions')
const loadOpenSearch = require('../lib/openSearch').loadOpenSearch
const contextMenus = require('../contextMenus')
const getSetting = require('../settings').getSetting
const getOrigin = require('../state/siteUtil').getOrigin
Expand Down Expand Up @@ -48,6 +47,8 @@ const keyCodes = require('../constants/keyCodes')
// State handling
const FrameStateUtil = require('../state/frameStateUtil')

const searchProviders = require('../data/searchProviders')

// Util
const cx = require('../lib/classSet.js')
const eventUtil = require('../lib/eventUtil')
Expand Down Expand Up @@ -201,16 +202,25 @@ class Main extends ImmutableComponent {
ipc.on(messages.LEAVE_FULL_SCREEN, this.exitFullScreen.bind(this))
}

loadOpenSearch () {
loadSearchProviders () {
let entries = searchProviders.providers
let engine = getSetting(settings.DEFAULT_SEARCH_ENGINE)
if (this.lastLoadedOpenSearch === undefined || engine !== this.lastLoadedOpenSearch) {
loadOpenSearch(engine).then((searchDetail) => windowActions.setSearchDetail(searchDetail))
this.lastLoadedOpenSearch = engine
if (this.lastLoadedSearchProviders === undefined || engine !== this.lastLoadedSearchProviders) {
entries.forEach((entry) => {
if (entry.name === engine) {
windowActions.setSearchDetail(Immutable.fromJS({
searchURL: entry.search,
autocompleteURL: entry.autocomplete
}))
this.lastLoadedSearchProviders = engine
return false
}
})
}
}

componentDidUpdate (prevProps) {
this.loadOpenSearch()
this.loadSearchProviders()
const activeFrame = FrameStateUtil.getActiveFrame(this.props.windowState)
const activeFramePrev = FrameStateUtil.getActiveFrame(prevProps.windowState)
const activeFrameTitle = activeFrame && (activeFrame.get('title') || activeFrame.get('location')) || ''
Expand Down Expand Up @@ -369,7 +379,7 @@ class Main extends ImmutableComponent {
windowActions.setContextMenuDetail()
})

this.loadOpenSearch()
this.loadSearchProviders()

window.addEventListener('mousemove', (e) => {
if (e.pageY !== this.pageY) {
Expand Down
8 changes: 6 additions & 2 deletions js/components/sortableTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class SortableTable extends ImmutableComponent {
componentDidMount (event) {
return tableSort(document.getElementsByClassName('sortableTable')[0])
}

render () {
var headings = []
var rows = []
Expand All @@ -26,7 +27,8 @@ class SortableTable extends ImmutableComponent {
headings[j] = headings[j] || <th className='sort-header' data-l10n-id={this.props.headings[j]} />
rows[i][j] = <td data-sort={this.props.rows[i][j]}>{this.props.rows[i][j] === true ? '✕' : this.props.rows[i][j]}</td>
}
rows[i] = <tr>{rows[i]}</tr>
rows[i] = <tr className={this.props.isHover ? 'rowHover' : ''}
onClick={this.props.hoverCallback.bind(this, rows[i])}>{rows[i]}</tr>
}
return <table className='sortableTable sort'>
<thead>
Expand All @@ -43,7 +45,9 @@ class SortableTable extends ImmutableComponent {

SortableTable.defaultProps = {
headings: React.PropTypes.array.isRequired,
rows: React.PropTypes.array.isRequired
rows: React.PropTypes.array.isRequired,
isHover: React.PropTypes.bool,
hoverCallback: React.PropTypes.func
}

module.exports = SortableTable
60 changes: 55 additions & 5 deletions js/components/urlBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const contextMenus = require('../contextMenus')
const dndData = require('../dndData')
const pdfjsExtensionId = require('../constants/config').PDFJSExtensionId
const windowStore = require('../stores/windowStore')
var searchProviders = require('../data/searchProviders')
const searchIconSize = 16

const { isUrl, isIntermediateAboutPage } = require('../lib/appUrlUtil')

Expand All @@ -35,6 +37,8 @@ class UrlBar extends ImmutableComponent {
this.onChange = this.onChange.bind(this)
this.onClick = this.onClick.bind(this)
this.onContextMenu = this.onContextMenu.bind(this)
this.activateSearchEngine = false
this.searchSelectEntry = null
}

get activeFrame () {
Expand Down Expand Up @@ -121,6 +125,11 @@ class UrlBar extends ImmutableComponent {
this.urlBarSuggestions.clickSelected(e)
} else {
let searchUrl = this.props.searchDetail.get('searchURL').replace('{searchTerms}', encodeURIComponent(location))
if (this.activateSearchEngine && this.searchSelectEntry !== null && !isLocationUrl) {
const replaceRE = new RegExp('^' + this.searchSelectEntry.shortcut + ' ', 'g')
location = location.replace(replaceRE, '')
searchUrl = this.searchSelectEntry.search.replace('{searchTerms}', encodeURIComponent(location))
}
location = isLocationUrl ? location : searchUrl
// do search.
if (e.altKey) {
Expand All @@ -134,6 +143,7 @@ class UrlBar extends ImmutableComponent {
// this can't go through appActions for some reason
// or the whole window will reload on the first page request
this.updateDOMInputFocus(false)
this.clearSearchEngine()
}
break
case KeyCodes.UP:
Expand All @@ -156,6 +166,7 @@ class UrlBar extends ImmutableComponent {
case KeyCodes.ESC:
e.preventDefault()
ipc.emit(messages.SHORTCUT_ACTIVE_FRAME_STOP)
this.clearSearchEngine()
break
case KeyCodes.DELETE:
if (e.shiftKey) {
Expand Down Expand Up @@ -193,6 +204,7 @@ class UrlBar extends ImmutableComponent {
// On blur, a user expects the text shown from the last autocomplete suffix
// to be auto entered as the new location.
this.updateLocationToSuggestion()
this.clearSearchEngine()
}

updateLocationToSuggestion () {
Expand All @@ -201,14 +213,40 @@ class UrlBar extends ImmutableComponent {
}
}

detectSearchEngine (input) {
let location = input || this.props.urlbar.get('location')
if (location !== null && location.length !== 0) {
const isLocationUrl = isUrl(location)
if (!isLocationUrl &&
!(this.searchSelectEntry && location.startsWith(this.searchSelectEntry.shortcut + ' '))) {
let entries = searchProviders.providers
entries.forEach((entry) => {
if (location.startsWith(entry.shortcut + ' ')) {
this.activateSearchEngine = true
this.searchSelectEntry = entry
return false
}
})
}
}
}

clearSearchEngine () {
this.activateSearchEngine = false
this.searchSelectEntry = null
}

onChange (e) {
windowActions.setUrlBarSelected(false)
windowActions.setUrlBarActive(true)
windowActions.setNavBarUserInput(e.target.value)
this.clearSearchEngine()
this.detectSearchEngine(e.target.value)
}

onFocus (e) {
windowActions.setUrlBarSelected(true)
this.detectSearchEngine()
}

onActiveFrameStop () {
Expand Down Expand Up @@ -337,12 +375,24 @@ class UrlBar extends ImmutableComponent {
onClick={this.onSiteInfo}
className={cx({
urlbarIcon: true,
'fa': true,
'fa-lock': this.isHTTPPage && this.props.isSecure && !this.props.urlbar.get('active'),
'fa-unlock-alt': this.isHTTPPage && !this.props.isSecure && !this.props.urlbar.get('active') && !this.props.titleMode,
'fa fa-file': this.props.urlbar.get('active') && this.props.loading === false,
'fa': !this.activateSearchEngine,
'fa-lock': !this.activateSearchEngine && this.isHTTPPage && this.isSecure && !this.props.urlbar.get('active'),
'fa-unlock-alt': !this.activateSearchEngine && this.isHTTPPage && !this.isSecure && !this.props.urlbar.get('active') && !this.props.titleMode,
'fa fa-file': !this.activateSearchEngine && this.props.urlbar.get('active') && this.props.loading === false,
extendedValidation: this.extendedValidationSSL
})} />
})}
style={
this.activateSearchEngine
? {
backgroundImage: `url(${this.searchSelectEntry.image})`,
minWidth: searchIconSize,
width: searchIconSize,
backgroundSize: searchIconSize,
height: searchIconSize,
marginTop: '3px',
marginRight: '3px'
} : {}
} />
{
this.props.titleMode
? <div id='titleBar'>
Expand Down
2 changes: 1 addition & 1 deletion js/constants/appConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ module.exports = {
'general.show-home-button': false,
'general.useragent.value': null, // Set at runtime
'general.autohide-menu': true,
'search.default-search-engine': 'content/search/google.xml',
'search.default-search-engine': 'Google',
'search.offer-search-suggestions': false, // false by default for privacy reasons
'tabs.switch-to-new-tabs': false,
'tabs.paint-tabs': true,
Expand Down
2 changes: 1 addition & 1 deletion js/contextMenus.js
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ function mainTemplateInit (nodeProps, frame) {
},
copyAddressMenuItem('copyImageAddress', nodeProps.srcURL)
)
if (getSetting(settings.DEFAULT_SEARCH_ENGINE) === 'content/search/google.xml' &&
if (getSetting(settings.DEFAULT_SEARCH_ENGINE) === 'Google' &&
nodeProps.srcURL && urlParse(nodeProps.srcURL).protocol !== 'data:') {
template.push(
{
Expand Down
Loading