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

Updates to history page #3281

Closed
wants to merge 1 commit into from
Closed
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/brave/about-history.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<meta charset="utf-8">
<meta name="availableLanguages" content="">
<meta name="defaultLanguage" content="en-US">
<meta name='theme-color' content='#035500'>
<meta name='theme-color' content='#565656'>
<link rel="shortcut icon"type="image/x-icon" href="data:image/x-icon;,">
<title data-l10n-id="historyTitle"></title>
<script src='js/about.js'></script>
Expand Down
6 changes: 6 additions & 0 deletions app/extensions/brave/locales/en-US/history.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
historyTitle=History
history=History
clearBrowsingDataNow=Clear browsing data
removeSelectedItems=Remove selected items
historySearch=Search history
time=Time
title=Title
domain=Domain
169 changes: 107 additions & 62 deletions js/about/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

// Note that these are webpack requires, not CommonJS node requiring requires
const React = require('react')
const Immutable = require('immutable')
const Sticky = require('react-stickynode')
const ImmutableComponent = require('../components/immutableComponent')
const Immutable = require('immutable')
const urlutils = require('../lib/urlutil.js')
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 Button = require('../components/button')

const ipc = window.chrome.ipc

Expand All @@ -18,59 +22,65 @@ require('../../less/about/siteDetails.less')
require('../../less/about/history.less')
require('../../node_modules/font-awesome/css/font-awesome.css')

class HistoryItem extends ImmutableComponent {
navigate () {
class HistoryDay extends ImmutableComponent {
navigate (entry) {
aboutActions.newFrame({
location: this.props.history.get('location'),
partitionNumber: this.props.history.get('partitionNumber')
location: entry.get('location'),
partitionNumber: entry.get('partitionNumber')
})
}
render () {
// Figure out the partition info display
let partitionNumberInfo
if (this.props.history.get('partitionNumber')) {
let l10nArgs = {
partitionNumber: this.props.history.get('partitionNumber')
}
partitionNumberInfo =
<span>&nbsp;(<span data-l10n-id='partitionNumber' data-l10n-args={JSON.stringify(l10nArgs)} />)</span>
}

var className = 'listItem'

// If the history item is in the selected folder, show
// it as selected
if (this.props.inSelectedFolder) {
className += ' selected'
}

return <div role='listitem'
className={className}
onContextMenu={aboutActions.contextMenu.bind(this, this.props.history.toJS(), 'history')}
data-context-menu-disable
onDoubleClick={this.navigate.bind(this)}>
{
this.props.history.get('customTitle') || this.props.history.get('title')
? <span className='aboutListItem' title={this.props.history.get('location')}>
<span className='aboutItemTitle'>{this.props.history.get('customTitle') || this.props.history.get('title')}</span>
{partitionNumberInfo}
<span className='aboutItemSeparator'>-</span><span className='aboutItemLocation'>{this.props.history.get('location')}</span>
</span>
: <span className='aboutListItem' title={this.props.history.get('location')}>
<span>{this.props.history.get('location')}</span>
{partitionNumberInfo}
</span>
}
return <div>
<div className='sectionTitle historyDayName'>{this.props.date}</div>
<SortableTable headings={['time', 'title', 'domain']}
rows={this.props.entries.map((entry) => [
new Date(entry.get('lastAccessedTime')).toLocaleTimeString(),
entry.get('customTitle') || entry.get('title')
? entry.get('customTitle') || entry.get('title')
: entry.get('location'),
urlutils.getHostname(entry.get('location'), true)
])}
rowObjects={this.props.entries}
columnClassNames={['time', 'title', 'domain']}
addHoverClass
onDoubleClick={this.navigate}
contextMenuName='history'
onContextMenu={aboutActions.contextMenu} />
</div>
}
}

class HistoryList extends ImmutableComponent {
class GroupedHistoryList extends ImmutableComponent {
getDayString (locale, item) {
return new Date(item.get('lastAccessedTime'))
.toLocaleDateString(locale, { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })
}
groupEntriesByDay (locale) {
const reduced = this.props.history.reduce((previousValue, currentValue, currentIndex, array) => {
const result = currentIndex === 1 ? [] : previousValue
if (currentIndex === 1) {
const firstDate = this.getDayString(locale, currentValue)
result.push({date: firstDate, entries: [previousValue]})
}
const date = this.getDayString(locale, currentValue)
const dateIndex = result.findIndex((entryByDate) => entryByDate.date === date)
if (dateIndex !== -1) {
result[dateIndex].entries.push(currentValue)
} else {
result.push({date: date, entries: [currentValue]})
}
return result
})
if (reduced) return reduced
return []
}
render () {
return <list className='siteDetailsList'>
const defaultLanguage = this.props.languageCodes.find((lang) => lang.includes(navigator.language)) || 'en-US'
const userLanguage = getSetting(settings.LANGUAGE, this.props.settings)
return <list className='historyList'>
{
this.props.history.map((entry) =>
<HistoryItem history={entry} />)
this.groupEntriesByDay(userLanguage || defaultLanguage).map((groupedEntry) =>
<HistoryDay date={groupedEntry.date} entries={groupedEntry.entries} />)
}
</list>
}
Expand All @@ -79,24 +89,23 @@ class HistoryList extends ImmutableComponent {
class AboutHistory extends React.Component {
constructor () {
super()
this.onChangeSelectedEntry = this.onChangeSelectedEntry.bind(this)
this.onChangeSearch = this.onChangeSearch.bind(this)
this.onClearSearchText = this.onClearSearchText.bind(this)
this.clearBrowsingDataNow = this.clearBrowsingDataNow.bind(this)
this.state = {
history: Immutable.Map(),
selectedEntry: 0,
search: ''
search: '',
settings: Immutable.Map(),
languageCodes: Immutable.Map()
}
ipc.on(messages.HISTORY_UPDATED, (e, detail) => {
this.setState({
history: Immutable.fromJS(detail && detail.history || {})
})
this.setState({ history: Immutable.fromJS(detail && detail.history || {}) })
})
}
onChangeSelectedEntry (id) {
this.setState({
selectedEntry: id,
search: ''
ipc.on(messages.SETTINGS_UPDATED, (e, settings) => {
this.setState({ settings: Immutable.fromJS(settings || {}) })
})
ipc.on(messages.LANGUAGE, (e, {languageCodes}) => {
this.setState({ languageCodes })
})
}
onChangeSearch (evt) {
Expand All @@ -109,16 +118,52 @@ class AboutHistory extends React.Component {
search: ''
})
}
searchedSiteDetails (searchTerm, siteDetails) {
return siteDetails.filter((siteDetail) => {
const title = siteDetail.get('customTitle') + siteDetail.get('title') + siteDetail.get('location')
return title.match(new RegExp(searchTerm, 'gi'))
})
}
historyDescendingOrder () {
return this.state.history.filter((site) => site.get('tags').isEmpty())
.sort((left, right) => {
if (left.get('lastAccessedTime') < right.get('lastAccessedTime')) return 1
if (left.get('lastAccessedTime') > right.get('lastAccessedTime')) return -1
return 0
}).slice(-500)
}
clearBrowsingDataNow () {
aboutActions.clearBrowsingDataNow()
}
render () {
return <div className='siteDetailsPage'>
<h2 data-l10n-id='history' />
<div className='siteDetailsPageHeader'>
<div data-l10n-id='history' className='sectionTitle' />
<div className='headerActions'>
<div className='searchWrapper'>
<input type='text' className={this.state.search ? 'searchInput' : 'searchInput searchInputPlaceholder'} placeholder='&#xf002;' id='historySearch' value={this.state.search} onChange={this.onChangeSearch} data-l10n-id='historySearch' />
{
this.state.search
? <span onClick={this.onClearSearchText} className='fa fa-close searchInputClear'></span>
: null
}
</div>
<Button l10nId='clearBrowsingDataNow' className='primaryButton clearBrowsingDataButton' onClick={this.clearBrowsingDataNow} />
</div>
</div>

<div className='siteDetailsPageContent'>
<Sticky enabled top={10}>
<HistoryList history={this.state.history.filter((site) => site.get('tags').isEmpty()).slice(-500)}
onChangeSelectedEntry={this.onChangeSelectedEntry}
selectedEntry={this.state.selectedEntry} />
</Sticky>
{
this.state.search
? <GroupedHistoryList
languageCodes={this.state.languageCodes}
settings={this.state.settings}
history={this.searchedSiteDetails(this.state.search, this.state.history)} />
: <GroupedHistoryList
languageCodes={this.state.languageCodes}
settings={this.state.settings}
history={this.historyDescendingOrder()} />
}
</div>
</div>
}
Expand Down
2 changes: 1 addition & 1 deletion js/about/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ class SearchTab extends ImmutableComponent {
return <div>
<div className='sectionTitle' data-l10n-id='searchSettings' />
<SortableTable headings={['default', 'searchEngine', 'engineGoKey']} rows={this.searchProviders}
isHover hoverCallback={this.hoverCallback.bind(this)} />
addHoverClass onClick={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
1 change: 1 addition & 0 deletions js/components/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Frame extends ImmutableComponent {
this.webview.send(messages.HISTORY_UPDATED, {
history: this.props.history.toJS()
})
this.webview.send(messages.SETTINGS_UPDATED, this.props.settings ? this.props.settings.toJS() : null)
} else if (location === 'about:downloads') {
this.webview.send(messages.DOWNLOADS_UPDATED, {
downloads: this.props.downloads.toJS()
Expand Down
2 changes: 1 addition & 1 deletion js/components/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ class Main extends ImmutableComponent {
onCloseFrame={this.onCloseFrame}
frameKey={frame.get('key')}
key={frame.get('key')}
settings={getBaseUrl(frame.get('location')) === 'about:preferences'
settings={getBaseUrl(frame.get('location')) === 'about:preferences' || getBaseUrl(frame.get('location')) === 'about:history'
? this.props.appState.get('settings') || emptyMap
: null}
bookmarks={frame.get('location') === 'about:bookmarks'
Expand Down
68 changes: 60 additions & 8 deletions js/components/sortableTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,72 @@ class SortableTable extends ImmutableComponent {
componentDidMount (event) {
return tableSort(document.getElementsByClassName('sortableTable')[0])
}

get hasClickHandler () {
return typeof this.props.onClick === 'function'
}
get hasColumnClassNames () {
return this.props.columnClassNames &&
this.props.columnClassNames.length === this.props.headings.length
}
get hasDoubleClickHandler () {
return typeof this.props.onDoubleClick === 'function'
}
get hasContextMenu () {
return typeof this.props.onContextMenu === 'function' &&
typeof this.props.contextMenuName === 'string'
}
getHandlerInput (rows, index) {
if (this.props.rowObjects) {
return typeof this.props.rowObjects[index].toJS === 'function'
? this.props.rowObjects[index].toJS()
: this.props.rowObjects[index]
}
return rows[index]
}
getRowAttributes (handlerInput, index) {
const rowAttributes = {}
if (this.props.addHoverClass) {
rowAttributes.className = 'rowHover'
}
if (this.hasClickHandler) {
rowAttributes.onClick = this.props.onClick.bind(this, handlerInput)
}
if (this.hasDoubleClickHandler) {
rowAttributes.onDoubleClick = this.props.onDoubleClick.bind(this, handlerInput)
}
if (this.hasContextMenu) {
rowAttributes.onContextMenu = this.props.onContextMenu.bind(this, handlerInput, this.props.contextMenuName)
}
return rowAttributes
}
render () {
var headings = []
var rows = []
let headings = []
let rows = []
let columnClassNames = []

if (!this.props.headings || !this.props.rows) {
return false
}

if (this.hasColumnClassNames) {
this.props.columnClassNames.forEach((className) => columnClassNames.push(className))
}

for (let i = 0; i < this.props.rows.length; i++) {
rows[i] = []
for (let j = 0; j < this.props.headings.length; j++) {
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][j] = typeof columnClassNames[j] === 'string'
? <td className={columnClassNames[j]} data-sort={this.props.rows[i][j]}>{this.props.rows[i][j] === true ? '✕' : this.props.rows[i][j]}</td>
: <td data-sort={this.props.rows[i][j]}>{this.props.rows[i][j] === true ? '✕' : this.props.rows[i][j]}</td>
}
rows[i] = <tr className={this.props.isHover ? 'rowHover' : ''}
onClick={this.props.hoverCallback.bind(this, rows[i])}>{rows[i]}</tr>

const handlerInput = this.getHandlerInput(rows, i)
const rowAttributes = this.getRowAttributes(handlerInput, i)

rows[i] = rowAttributes.onContextMenu
? <tr {...rowAttributes} data-context-menu-disable>{rows[i]}</tr>
: rows[i] = <tr {...rowAttributes}>{rows[i]}</tr>
}
return <table className='sortableTable sort'>
<thead>
Expand All @@ -46,8 +97,9 @@ class SortableTable extends ImmutableComponent {
SortableTable.defaultProps = {
headings: React.PropTypes.array.isRequired,
rows: React.PropTypes.array.isRequired,
isHover: React.PropTypes.bool,
hoverCallback: React.PropTypes.func
columnClassNames: React.PropTypes.array,
addHoverClass: React.PropTypes.bool,
contextMenuName: React.PropTypes.string
}

module.exports = SortableTable
2 changes: 1 addition & 1 deletion js/contextMenus.js
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ function onMainContextMenu (nodeProps, frame, contextMenuType) {
if (contextMenuType === 'bookmark' || contextMenuType === 'bookmark-folder') {
onSiteDetailContextMenu(Immutable.fromJS(nodeProps), Immutable.fromJS({ location: '', title: '', partitionNumber: frame.get('partitionNumber') }))
} else if (contextMenuType === 'history') {
onSiteDetailContextMenu(Immutable.fromJS(nodeProps), Immutable.fromJS({ location: '', title: '', partitionNumber: frame.get('partitionNumber') }))
onSiteDetailContextMenu(Immutable.fromJS(nodeProps))
} else if (contextMenuType === 'download') {
onDownloadsToolbarContextMenu(nodeProps.downloadId, Immutable.fromJS(nodeProps))
} else {
Expand Down
5 changes: 4 additions & 1 deletion js/lib/urlutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,11 @@ const UrlUtil = {
* @param {String} input The input URL.
* @returns {String} The host name.
*/
getHostname: function (input) {
getHostname: function (input, excludePort) {
try {
if (excludePort) {
return new window.URL(input).hostname
}
return new window.URL(input).host
} catch (e) {
return undefined
Expand Down
Loading