diff --git a/app/browser/api/topSites.js b/app/browser/api/topSites.js
index 48f3c9ccfce..a4fc6329a65 100644
--- a/app/browser/api/topSites.js
+++ b/app/browser/api/topSites.js
@@ -7,39 +7,33 @@
const Immutable = require('immutable')
const appActions = require('../../../js/actions/appActions')
const debounce = require('../../../js/lib/debounce')
-const siteUtil = require('../../../js/state/siteUtil')
+const historyState = require('../../common/state/historyState')
const {isSourceAboutUrl} = require('../../../js/lib/appUrlUtil')
-const aboutNewTabMaxEntries = 100
+const aboutNewTabMaxEntries = 18
let appStore
let minCountOfTopSites
let minAccessOfTopSites
-const compareSites = (site1, site2) => {
- if (!site1 || !site2) return false
- return site1.get('location') === site2.get('location') &&
- site1.get('partitionNumber') === site2.get('partitionNumber')
-}
-
const pinnedTopSites = (state) => {
- return (state.getIn(['about', 'newtab', 'pinnedTopSites']) || Immutable.List()).setSize(18)
+ return state.getIn(['about', 'newtab', 'pinnedTopSites'], Immutable.List())
}
const ignoredTopSites = (state) => {
- return state.getIn(['about', 'newtab', 'ignoredTopSites']) || Immutable.List()
+ return state.getIn(['about', 'newtab', 'ignoredTopSites'], Immutable.List())
}
-const isPinned = (state, siteProps) => {
- return pinnedTopSites(state).filter((site) => compareSites(site, siteProps)).size > 0
+const isPinned = (state, siteKey) => {
+ return pinnedTopSites(state).find(site => site.get('key') === siteKey)
}
-const isIgnored = (state, siteProps) => {
- return ignoredTopSites(state).filter((site) => compareSites(site, siteProps)).size > 0
+const isIgnored = (state, siteKey) => {
+ return ignoredTopSites(state).includes(siteKey)
}
const sortCountDescending = (left, right) => {
- const leftCount = left.get('count') || 0
- const rightCount = right.get('count') || 0
+ const leftCount = left.get('count', 0)
+ const rightCount = right.get('count', 0)
if (leftCount < rightCount) {
return 1
}
@@ -55,25 +49,6 @@ const sortCountDescending = (left, right) => {
return 0
}
-const removeDuplicateDomains = (list) => {
- const siteDomains = new Set()
- return list.filter((site) => {
- if (!site.get('location')) {
- return false
- }
- try {
- const hostname = require('../../common/urlParse')(site.get('location')).hostname
- if (!siteDomains.has(hostname)) {
- siteDomains.add(hostname)
- return true
- }
- } catch (e) {
- console.log('Error parsing hostname: ', e)
- }
- return false
- })
-}
-
const calculateTopSites = (clearCache) => {
if (clearCache) {
clearTopSiteCacheData()
@@ -91,18 +66,21 @@ const startCalculatingTopSiteData = debounce(() => {
}
const state = appStore.getState()
// remove folders; sort by visit count; enforce a max limit
- const sites = (state.get('sites') ? state.get('sites').toList() : new Immutable.List())
- .filter((site) => !siteUtil.isFolder(site) &&
- !siteUtil.isImportedBookmark(site) &&
- !isSourceAboutUrl(site.get('location')) &&
+ const sites = historyState.getSites(state)
+ .filter((site, key) => !isSourceAboutUrl(site.get('location')) &&
+ !isPinned(state, key) &&
+ !isIgnored(state, key) &&
(minCountOfTopSites === undefined || (site.get('count') || 0) >= minCountOfTopSites) &&
- (minAccessOfTopSites === undefined || (site.get('lastAccessedTime') || 0) >= minAccessOfTopSites))
+ (minAccessOfTopSites === undefined || (site.get('lastAccessedTime') || 0) >= minAccessOfTopSites)
+ )
.sort(sortCountDescending)
.slice(0, aboutNewTabMaxEntries)
+ .map((site, key) => site.set('key', key))
+ .toList()
for (let i = 0; i < sites.size; i++) {
- const count = sites.getIn([i, 'count']) || 0
- const access = sites.getIn([i, 'lastAccessedTime']) || 0
+ const count = sites.getIn([i, 'count'], 0)
+ const access = sites.getIn([i, 'lastAccessedTime'], 0)
if (minCountOfTopSites === undefined || count < minCountOfTopSites) {
minCountOfTopSites = count
}
@@ -111,32 +89,7 @@ const startCalculatingTopSiteData = debounce(() => {
}
}
- // Filter out pinned and ignored sites
- let unpinnedSites = sites.filter((site) => !(isPinned(state, site) || isIgnored(state, site)))
- unpinnedSites = removeDuplicateDomains(unpinnedSites)
-
- // Merge the pinned and unpinned lists together
- // Pinned items have priority because the position is important
- let gridSites = pinnedTopSites(state).map((pinnedSite) => {
- // Fetch latest siteDetail objects from appState.sites using location/partition
- if (pinnedSite) {
- const matches = sites.filter((site) => compareSites(site, pinnedSite))
- if (matches.size > 0) return matches.first()
- }
- // Default to unpinned items
- const firstSite = unpinnedSites.first()
- unpinnedSites = unpinnedSites.shift()
- return firstSite
- })
-
- // Include up to [aboutNewTabMaxEntries] entries so that folks
- // can ignore sites and have new items fill those empty spaces
- if (unpinnedSites.size > 0) {
- gridSites = gridSites.concat(unpinnedSites)
- }
-
- const finalData = gridSites.filter((site) => site != null)
- appActions.topSiteDataAvailable(finalData)
+ appActions.topSiteDataAvailable(sites)
}, 5 * 1000)
const clearTopSiteCacheData = () => {
diff --git a/app/browser/bookmarksExporter.js b/app/browser/bookmarksExporter.js
index 598501ed3da..e22f3be31d0 100644
--- a/app/browser/bookmarksExporter.js
+++ b/app/browser/bookmarksExporter.js
@@ -11,20 +11,31 @@ const electron = require('electron')
const dialog = electron.dialog
const app = electron.app
const BrowserWindow = electron.BrowserWindow
-const getSetting = require('../../js/settings').getSetting
+
+// State
+const bookmarkFoldersState = require('../common/state/bookmarkFoldersState')
+const bookmarsState = require('../common/state/bookmarksState')
+
+// Constants
const settings = require('../../js/constants/settings')
const siteTags = require('../../js/constants/siteTags')
+
+// Utils
+const {getSetting} = require('../../js/settings')
const siteUtil = require('../../js/state/siteUtil')
-const isWindows = process.platform === 'win32'
+const platformUtil = require('../common/lib/platformUtil')
+
const indentLength = 2
const indentType = ' '
-function showDialog (sites) {
+function showDialog (state) {
const focusedWindow = BrowserWindow.getFocusedWindow()
const fileName = moment().format('DD_MM_YYYY') + '.html'
const defaultPath = path.join(getSetting(settings.DEFAULT_DOWNLOAD_SAVE_PATH) || app.getPath('downloads'), fileName)
let personal = []
let other = []
+ const bookmarks = bookmarsState.getBookmarks(state)
+ const bookmarkFolders = bookmarkFoldersState.getFolders(state)
dialog.showSaveDialog(focusedWindow, {
defaultPath: defaultPath,
@@ -34,13 +45,14 @@ function showDialog (sites) {
}]
}, (fileName) => {
if (fileName) {
- personal = createBookmarkArray(sites)
- other = createBookmarkArray(sites, -1, false)
+ personal = createBookmarkArray(bookmarks, bookmarkFolders)
+ other = createBookmarkArray(bookmarks, bookmarkFolders, -1, false)
fs.writeFileSync(fileName, createBookmarkHTML(personal, other))
}
})
}
+// TODO refactor this based on the structure (now we send in bookmarks and folders)
function createBookmarkArray (sites, parentFolderId, first = true, depth = 1) {
const filteredBookmarks = parentFolderId
? sites.filter((site) => site.get('parentFolderId') === parentFolderId)
@@ -54,13 +66,12 @@ function createBookmarkArray (sites, parentFolderId, first = true, depth = 1) {
filteredBookmarks.forEach((site) => {
if (site.get('tags').includes(siteTags.BOOKMARK) && site.get('location')) {
- title = site.get('customTitle') || site.get('title') || site.get('location')
+ title = site.get('title') || site.get('location')
payload.push(`${indentNext}
${title}`)
} else if (siteUtil.isFolder(site)) {
const folderId = site.get('folderId')
- title = site.get('customTitle') || site.get('title')
- payload.push(`${indentNext}${title}
`)
+ payload.push(`${indentNext}${site.get('title')}
`)
payload = payload.concat(createBookmarkArray(sites, folderId, true, (depth + 1)))
}
})
@@ -71,7 +82,7 @@ function createBookmarkArray (sites, parentFolderId, first = true, depth = 1) {
}
function createBookmarkHTML (personal, other) {
- const breakTag = (isWindows) ? '\r\n' : '\n'
+ const breakTag = (platformUtil.isWindows()) ? '\r\n' : '\n'
const title = 'Bookmarks'
return `
diff --git a/app/browser/menu.js b/app/browser/menu.js
index a5b550a8619..f241dd0a3c3 100644
--- a/app/browser/menu.js
+++ b/app/browser/menu.js
@@ -17,7 +17,6 @@ const appConstants = require('../../js/constants/appConstants')
const windowConstants = require('../../js/constants/windowConstants')
const messages = require('../../js/constants/messages')
const settings = require('../../js/constants/settings')
-const siteTags = require('../../js/constants/siteTags')
// State
const {getByTabId} = require('../common/state/tabState')
@@ -35,8 +34,8 @@ const frameStateUtil = require('../../js/state/frameStateUtil')
const menuUtil = require('../common/lib/menuUtil')
const {getSetting} = require('../../js/settings')
const locale = require('../locale')
-const {isLocationBookmarked} = require('../../js/state/siteUtil')
const platformUtil = require('../common/lib/platformUtil')
+const bookmarkUtil = require('../common/lib/bookmarkUtil')
const isDarwin = platformUtil.isDarwin()
const isLinux = platformUtil.isLinux()
const isWindows = platformUtil.isWindows()
@@ -376,7 +375,7 @@ const updateRecentlyClosedMenuItems = (state) => {
}
const isCurrentLocationBookmarked = (state) => {
- return isLocationBookmarked(state, currentLocation)
+ return bookmarkUtil.isLocationBookmarked(state, currentLocation)
}
const createBookmarksSubmenu = (state) => {
@@ -406,6 +405,7 @@ const createBookmarksSubmenu = (state) => {
CommonMenu.exportBookmarksMenuItem()
]
+ // TODO we should send sites in, but first level bookmarks and folders
const bookmarks = menuUtil.createBookmarkTemplateItems(state.get('sites'))
if (bookmarks.length > 0) {
submenu.push(CommonMenu.separatorMenuItem)
@@ -698,33 +698,15 @@ const doAction = (state, action) => {
createMenu(state)
}
break
- case appConstants.APP_ADD_SITE:
+
case appConstants.APP_ADD_BOOKMARK:
case appConstants.APP_EDIT_BOOKMARK:
- {
- if (action.tag === siteTags.BOOKMARK || action.tag === siteTags.BOOKMARK_FOLDER) {
- createMenu(state)
- } else if (action.siteDetail && action.siteDetail.constructor === Immutable.List && action.tag === undefined) {
- let shouldRebuild = false
- action.siteDetail.forEach((site) => {
- const tag = site.getIn(['tags', 0])
- if (tag === siteTags.BOOKMARK || tag === siteTags.BOOKMARK_FOLDER) {
- shouldRebuild = true
- }
- })
- if (shouldRebuild) {
- createMenu(state)
- }
- }
- break
- }
- case appConstants.APP_REMOVE_SITE:
- {
- if (action.tag === siteTags.BOOKMARK || action.tag === siteTags.BOOKMARK_FOLDER) {
- createMenu(state)
- }
- break
- }
+ case appConstants.APP_REMOVE_BOOKMARK:
+ case appConstants.APP_ADD_BOOKMARK_FOLDER:
+ case appConstants.APP_EDIT_BOOKMARK_FOLDER:
+ case appConstants.APP_REMOVE_BOOKMARK_FOLDER:
+ createMenu(state)
+ break
case appConstants.APP_ON_CLEAR_BROWSING_DATA:
{
const defaults = state.get('clearBrowsingDataDefaults')
diff --git a/app/browser/reducers/sitesReducer.js b/app/browser/reducers/sitesReducer.js
index 6f5035404bf..4ff281e0bcf 100644
--- a/app/browser/reducers/sitesReducer.js
+++ b/app/browser/reducers/sitesReducer.js
@@ -4,18 +4,33 @@
'use strict'
+const Immutable = require('immutable')
+
+// Actions
+const syncActions = require('../../../js/actions/syncActions')
+const appActions = require('../../../js/actions/appActions')
+
+// State
+const siteCache = require('../../common/state/siteCache')
+const tabState = require('../../common/state/tabState')
+const bookmarkFoldersState = require('../../common/state/bookmarkFoldersState')
+const pinnedSitesState = require('../../common/state/pinnedSitesState')
+const bookmarksState = require('../../common/state/bookmarksState')
+const historyState = require('../../common/state/historyState')
+
+// Constants
const appConstants = require('../../../js/constants/appConstants')
+const settings = require('../../../js/constants/settings')
+
+// Utils
const filtering = require('../../filtering')
-const siteCache = require('../../common/state/siteCache')
-const siteTags = require('../../../js/constants/siteTags')
const siteUtil = require('../../../js/state/siteUtil')
-const syncActions = require('../../../js/actions/syncActions')
const syncUtil = require('../../../js/state/syncUtil')
-const Immutable = require('immutable')
-const settings = require('../../../js/constants/settings')
+const pinnedSitesUtil = require('../../common/lib/pinnedSitesUtil')
+const bookmarkFoldersUtil = require('../../common/lib/bookmarkFoldersUtil')
+const bookmarkUtil = require('../../common/lib/bookmarkUtil')
const {getSetting} = require('../../../js/settings')
const writeActions = require('../../../js/constants/sync/proto').actions
-const tabState = require('../../common/state/tabState')
const syncEnabled = () => {
return getSetting(settings.SYNC_ENABLED) === true
@@ -25,7 +40,7 @@ const updateTabBookmarked = (state, tabValue) => {
if (!tabValue || !tabValue.get('tabId')) {
return state
}
- const bookmarked = siteUtil.isLocationBookmarked(state, tabValue.get('url'))
+ const bookmarked = bookmarkUtil.isLocationBookmarked(state, tabValue.get('url'))
return tabState.updateTabValue(state, tabValue.set('bookmarked', bookmarked))
}
@@ -48,64 +63,127 @@ const sitesReducer = (state, action, immutableAction) => {
const temp = state.get('tempClearBrowsingData', Immutable.Map())
const clearData = defaults ? defaults.merge(temp) : temp
if (clearData.get('browserHistory')) {
- state = state.set('sites', siteUtil.clearHistory(state.get('sites')))
+ state = historyState.clearSites()
filtering.clearHistory()
}
break
}
- case appConstants.APP_ADD_SITE:
+ case appConstants.APP_ADD_HISTORY_SITE:
{
const isSyncEnabled = syncEnabled()
- if (Immutable.List.isList(action.siteDetail)) {
- action.siteDetail.forEach((s) => {
- state = siteUtil.addSite(state, s, action.tag, action.skipSync)
- if (isSyncEnabled) {
- state = syncUtil.updateSiteCache(state, s)
- }
- })
- } else {
- let sites = state.get('sites')
- if (!action.siteDetail.get('folderId') && siteUtil.isFolder(action.siteDetail)) {
- action.siteDetail = action.siteDetail.set('folderId', siteUtil.getNextFolderId(sites))
- }
- state = siteUtil.addSite(state, action.siteDetail, action.tag, action.skipSync)
- if (isSyncEnabled) {
- state = syncUtil.updateSiteCache(state, action.siteDetail)
- }
+ state = historyState.addSite(state, action.siteDetail)
+ if (isSyncEnabled) {
+ state = syncUtil.updateSiteCache(state, action.siteDetail)
}
break
}
case appConstants.APP_ADD_BOOKMARK:
- case appConstants.APP_EDIT_BOOKMARK:
{
const isSyncEnabled = syncEnabled()
- const sites = state.get('sites')
const closestKey = action.closestKey
- let site = action.siteDetail
+ let bookmark = action.siteDetail
- if (site == null || action.tag == null) {
+ if (bookmark == null) {
break
}
- if (!site.get('folderId') && action.tag === siteTags.BOOKMARK_FOLDER) {
- site = site.set('folderId', siteUtil.getNextFolderId(sites))
+ state = bookmarksState.addBookmark(state, bookmark)
+
+ // TODO implement move
+ if (closestKey != null) {
+ const sourceKey = siteUtil.getSiteKey(bookmark)
+ state = siteUtil.moveSite(state, sourceKey, closestKey, false, false, true)
+ }
+
+ if (isSyncEnabled) {
+ state = syncUtil.updateSiteCache(state, bookmark)
+ }
+
+ state = updateActiveTabBookmarked(state)
+ break
+ }
+ case appConstants.APP_ADD_BOOKMARKS:
+ {
+ // TODO we need to do it like this until we implement action inside actions
+ action.bookmarkList.forEach(bookmark => appActions.addBookmark(bookmark))
+ break
+ }
+ case appConstants.APP_EDIT_BOOKMARK:
+ {
+ const isSyncEnabled = syncEnabled()
+ let bookmark = action.siteDetail
+
+ if (bookmark == null) {
+ break
}
- state = siteUtil.addSite(state, site, action.tag, action.editKey)
+ state = bookmarksState.editBookmark(state, bookmark, action.editKey)
+ if (isSyncEnabled) {
+ state = syncUtil.updateSiteCache(state, bookmark)
+ }
+
+ state = updateActiveTabBookmarked(state)
+ break
+ }
+ case appConstants.APP_REMOVE_BOOKMARK:
+ {
+ state = bookmarksState.removeBookmark(state, action.bookmarkKey)
+ state = updateActiveTabBookmarked(state)
+ break
+ }
+ case appConstants.APP_ADD_BOOKMARK_FOLDER:
+ {
+ const isSyncEnabled = syncEnabled()
+ const closestKey = action.closestKey
+ let folder = action.folderDetails
+
+ if (folder == null) {
+ break
+ }
+
+ state = bookmarkFoldersState.addFolder(state, folder)
+
+ // TODO move folder
if (closestKey != null) {
- const sourceKey = siteUtil.getSiteKey(site)
+ const sourceKey = siteUtil.getSiteKey(folder)
state = siteUtil.moveSite(state, sourceKey, closestKey, false, false, true)
}
if (isSyncEnabled) {
- state = syncUtil.updateSiteCache(state, site)
+ state = syncUtil.updateSiteCache(state, folder)
+ }
+ break
+ }
+ case appConstants.APP_ADD_BOOKMARK_FOLDERS:
+ {
+ // TODO we need to do it like this until we implement action inside actions
+ action.folderList.forEach(folder => appActions.addBookmarkFolder(folder))
+ break
+ }
+ case appConstants.APP_EDIT_BOOKMARK_FOLDER:
+ {
+ const isSyncEnabled = syncEnabled()
+ let folder = action.folderDetails
+
+ if (folder == null) {
+ break
+ }
+
+ state = bookmarkFoldersState.editFolder(state, folder, action.editKey)
+
+ if (isSyncEnabled) {
+ state = syncUtil.updateSiteCache(state, folder)
}
- state = updateActiveTabBookmarked(state)
break
}
- case appConstants.APP_REMOVE_SITE:
+ case appConstants.APP_REMOVE_BOOKMARK_FOLDER:
+ {
+ state = bookmarkFoldersState.removeFolder(state, action.folderKey)
+ break
+ }
+ case appConstants.APP_REMOVE_HISTORY_SITE:
const removeSiteSyncCallback = action.skipSync ? undefined : syncActions.removeSite
state = siteUtil.removeSite(state, action.siteDetail, action.tag, true, removeSiteSyncCallback)
if (syncEnabled()) {
@@ -124,7 +202,7 @@ const sitesReducer = (state, action, immutableAction) => {
}
break
case appConstants.APP_APPLY_SITE_RECORDS:
- let nextFolderId = siteUtil.getNextFolderId(state.get('sites'))
+ let nextFolderId = bookmarkFoldersUtil.getNextFolderId(state.get('bookmarkFolders'))
// Ensure that all folders are assigned folderIds
action.records.forEach((record, i) => {
if (record.action !== writeActions.DELETE &&
@@ -159,17 +237,17 @@ const sitesReducer = (state, action, immutableAction) => {
if (immutableAction.getIn(['changeInfo', 'pinned']) != null) {
const pinned = immutableAction.getIn(['changeInfo', 'pinned'])
const tabId = immutableAction.getIn(['tabValue', 'tabId'])
- const tab = state.get('tabs').find((tab) => tab.get('tabId') === tabId)
+ const tab = tabState.getByTabId(state, tabId)
if (!tab) {
console.warn('Trying to pin a tabId which does not exist:', tabId, 'tabs: ', state.get('tabs').toJS())
break
}
- const sites = state.get('sites')
- const siteDetail = siteUtil.getDetailFromTab(tab, siteTags.PINNED, sites)
+ const sites = pinnedSitesState.getSites(state)
+ const siteDetail = pinnedSitesUtil.getDetailsFromTab(sites, tab)
if (pinned) {
- state = siteUtil.addSite(state, siteDetail, siteTags.PINNED)
+ state = pinnedSitesState.addPinnedSite(state, siteDetail)
} else {
- state = siteUtil.removeSite(state, siteDetail, siteTags.PINNED)
+ state = pinnedSitesState.removePinnedSite(state, siteDetail)
}
if (syncEnabled()) {
state = syncUtil.updateSiteCache(state, siteDetail)
@@ -180,11 +258,26 @@ const sitesReducer = (state, action, immutableAction) => {
case appConstants.APP_CREATE_TAB_REQUESTED: {
const createProperties = immutableAction.get('createProperties')
if (createProperties.get('pinned')) {
- state = siteUtil.addSite(state,
- siteUtil.getDetailFromCreateProperties(createProperties), siteTags.PINNED)
+ state = pinnedSitesUtil.addPinnedSite(state, pinnedSitesUtil.getDetailFromProperties(createProperties))
}
break
}
+ case appConstants.APP_ON_PINNED_TAB_REORDER:
+ state = pinnedSitesState.reOrderSite(
+ state,
+ action.siteKey,
+ action.destinationKey,
+ action.prepend
+ )
+
+ // TODO add bookmark sort on a new state
+
+ // TODO do we need this for pinned sites?
+ if (syncEnabled()) {
+ const newSite = state.getIn(['pinnedSites', action.siteKey])
+ state = syncUtil.updateSiteCache(state, newSite)
+ }
+ break
}
return state
}
diff --git a/app/browser/reducers/urlBarSuggestionsReducer.js b/app/browser/reducers/urlBarSuggestionsReducer.js
index 8873091a9ad..0a4f7b827f5 100644
--- a/app/browser/reducers/urlBarSuggestionsReducer.js
+++ b/app/browser/reducers/urlBarSuggestionsReducer.js
@@ -7,24 +7,18 @@
const appConstants = require('../../../js/constants/appConstants')
const {generateNewSuggestionsList, generateNewSearchXHRResults} = require('../../common/lib/suggestion')
const {init, add} = require('../../common/lib/siteSuggestions')
-const Immutable = require('immutable')
const {makeImmutable} = require('../../common/state/immutableUtil')
const tabState = require('../../common/state/tabState')
const urlBarSuggestionsReducer = (state, action) => {
switch (action.actionType) {
- case appConstants.APP_ADD_SITE:
+ case appConstants.APP_ADD_HISTORY_SITE:
case appConstants.APP_ADD_BOOKMARK:
case appConstants.APP_EDIT_BOOKMARK:
- if (Immutable.List.isList(action.siteDetail)) {
- action.siteDetail.forEach((s) => {
- add(s)
- })
- } else {
- add(action.siteDetail)
- }
+ add(action.siteDetail)
break
case appConstants.APP_SET_STATE:
+ // TODO do we need to merge bookmarks and history into one?
init(Object.values(action.appState.get('sites').toJS()))
break
case appConstants.APP_URL_BAR_TEXT_CHANGED:
diff --git a/app/browser/tabs.js b/app/browser/tabs.js
index 06743fc16dc..82e7cab40ce 100644
--- a/app/browser/tabs.js
+++ b/app/browser/tabs.js
@@ -22,12 +22,14 @@ const messages = require('../../js/constants/messages')
const aboutHistoryState = require('../common/state/aboutHistoryState')
const appStore = require('../../js/stores/appStore')
const appConfig = require('../../js/constants/appConfig')
-const siteTags = require('../../js/constants/siteTags')
const {newTabMode} = require('../common/constants/settingsEnums')
const {cleanupWebContents, currentWebContents, getWebContents, updateWebContents} = require('./webContentsCache')
const {FilterOptions} = require('ad-block')
const {isResourceEnabled} = require('../filtering')
const autofill = require('../autofill')
+const bookmarksState = require('../common/state/bookmarksState')
+const bookmarkFoldersState = require('../common/state/bookmarkFoldersState')
+const historyState = require('../common/state/historyState')
let currentPartitionNumber = 0
const incrementPartitionNumber = () => ++currentPartitionNumber
@@ -134,21 +136,11 @@ ipcMain.on(messages.ABOUT_COMPONENT_INITIALIZED, (e) => {
})
})
-const getBookmarksData = function (state) {
- let bookmarkSites = new Immutable.OrderedMap()
- let bookmarkFolderSites = new Immutable.OrderedMap()
- state.get('sites').forEach((site, siteKey) => {
- const tags = site.get('tags')
- if (tags.includes(siteTags.BOOKMARK)) {
- bookmarkSites = bookmarkSites.set(siteKey, site)
- }
- if (tags.includes(siteTags.BOOKMARK_FOLDER)) {
- bookmarkFolderSites = bookmarkFolderSites.set(siteKey, site)
- }
- })
- const bookmarks = bookmarkSites.toList().toJS()
- const bookmarkFolders = bookmarkFolderSites.toList().toJS()
- return {bookmarks, bookmarkFolders}
+const getBookmarksData = (state) => {
+ return {
+ bookmarks: bookmarksState.getBookmarks(state).toList().toJS(),
+ bookmarkFolders: bookmarkFoldersState.getFolders(state).toList().toJS()
+ }
}
const updateAboutDetails = (tab, tabValue) => {
@@ -808,7 +800,7 @@ const api = {
getHistoryEntries: (state, action) => {
const tab = getWebContents(action.get('tabId'))
- const sites = state ? state.get('sites') : null
+ const sites = state ? historyState.getSites(state) : null
if (tab && !tab.isDestroyed()) {
let history = {
@@ -832,7 +824,7 @@ const api = {
// TODO: return brave lion (or better: get icon from extension if possible as data URI)
} else {
if (sites) {
- const site = sites.find(function (element) { return element.get('location') === url })
+ const site = sites.find((element) => element.get('location') === url)
if (site) {
entry.icon = site.get('favicon')
}
diff --git a/app/browser/windows.js b/app/browser/windows.js
index 028aee44eed..fec40cd6d14 100644
--- a/app/browser/windows.js
+++ b/app/browser/windows.js
@@ -9,14 +9,14 @@ const debounce = require('../../js/lib/debounce')
const {getSetting} = require('../../js/settings')
const locale = require('../locale')
const LocalShortcuts = require('../localShortcuts')
-const {getPinnedSiteProps} = require('../common/lib/windowsUtil')
const {makeImmutable} = require('../common/state/immutableUtil')
const {getPinnedTabsByWindowId} = require('../common/state/tabState')
const messages = require('../../js/constants/messages')
const settings = require('../../js/constants/settings')
-const siteTags = require('../../js/constants/siteTags')
const windowState = require('../common/state/windowState')
const Immutable = require('immutable')
+const pinnedSitesState = require('../common/state/pinnedSitesState')
+const pinnedSitesUtil = require('../common/lib/pinnedSitesUtil')
// TODO(bridiver) - set window uuid
let currentWindows = {}
@@ -69,8 +69,7 @@ const updatePinnedTabs = (win) => {
const appStore = require('../../js/stores/appStore')
const state = appStore.getState()
const windowId = win.id
- const pinnedSites = state.get('sites').toList().filter((site) =>
- site.get('tags').includes(siteTags.PINNED)).map(site => getPinnedSiteProps(site))
+ const pinnedSites = pinnedSitesState.getSites(state).map(site => pinnedSitesUtil.getPinnedSiteProps(site))
const pinnedTabs = getPinnedTabsByWindowId(state, windowId)
pinnedSites.filter((site) =>
diff --git a/app/common/lib/bookmarkFoldersUtil.js b/app/common/lib/bookmarkFoldersUtil.js
new file mode 100644
index 00000000000..c30f9cb38cd
--- /dev/null
+++ b/app/common/lib/bookmarkFoldersUtil.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const isFolderNameValid = (title) => {
+ return (title != null && title !== 0) && title.trim().length > 0
+}
+
+const getNextFolderIdItem = (folders) =>
+ folders.max((folderA, folderB) => {
+ const folderIdA = folderA.get('folderId')
+ const folderIdB = folderB.get('folderId')
+ if (folderIdA === folderIdB) {
+ return 0
+ }
+ if (folderIdA === undefined) {
+ return false
+ }
+ if (folderIdB === undefined) {
+ return true
+ }
+ return folderIdA > folderIdB
+ })
+
+const getNextFolderId = (folders) => {
+ const defaultFolderId = 0
+ if (!folders) {
+ return defaultFolderId
+ }
+ const maxIdItem = getNextFolderIdItem(folders)
+ return (maxIdItem ? (maxIdItem.get('folderId') || 0) : 0) + 1
+}
+
+const getNextFolderName = (folders, name) => {
+ if (!folders) {
+ return name
+ }
+ const site = folders.find((site) => site.get('title') === name)
+ if (!site) {
+ return name
+ }
+ const filenameFormat = /(.*) \((\d+)\)/
+ let result = filenameFormat.exec(name)
+ if (!result) {
+ return getNextFolderName(folders, name + ' (1)')
+ }
+
+ const nextNum = parseInt(result[2]) + 1
+ return getNextFolderName(folders, result[1] + ' (' + nextNum + ')')
+}
+
+module.exports = {
+ isFolderNameValid,
+ getNextFolderId,
+ getNextFolderName
+}
diff --git a/app/common/lib/bookmarkUtil.js b/app/common/lib/bookmarkUtil.js
index a8c399db21f..1d415ac0f13 100644
--- a/app/common/lib/bookmarkUtil.js
+++ b/app/common/lib/bookmarkUtil.js
@@ -4,6 +4,9 @@
const Immutable = require('immutable')
+// State
+const bookmarksState = require('../state/bookmarksState')
+
// Constants
const dragTypes = require('../../../js/constants/dragTypes')
const {bookmarksToolbarMode} = require('../constants/settingsEnums')
@@ -12,17 +15,12 @@ const settings = require('../../../js/constants/settings')
// Utils
const domUtil = require('../../renderer/lib/domUtil')
const siteUtil = require('../../../js/state/siteUtil')
+const siteCache = require('../state/siteCache')
const {calculateTextWidth} = require('../../../js/lib/textCalculator')
const {iconSize} = require('../../../js/constants/config')
const {getSetting} = require('../../../js/settings')
-function bookmarkHangerHeading (editMode, isFolder, isAdded) {
- if (isFolder) {
- return editMode
- ? 'bookmarkFolderEditing'
- : 'bookmarkFolderAdding'
- }
-
+const bookmarkHangerHeading = (editMode, isAdded) => {
if (isAdded) {
return 'bookmarkAdded'
}
@@ -31,20 +29,8 @@ function bookmarkHangerHeading (editMode, isFolder, isAdded) {
? 'bookmarkEdit'
: 'bookmarkCreateNew'
}
-
-const displayBookmarkName = (detail) => {
- const customTitle = detail.get('customTitle')
- if (customTitle !== undefined && customTitle !== '') {
- return customTitle || ''
- }
- return detail.get('title') || ''
-}
-
-const isBookmarkNameValid = (title, location, isFolder, customTitle) => {
- const newTitle = title || customTitle
- return isFolder
- ? (newTitle != null && newTitle !== 0) && newTitle.trim().length > 0
- : location != null && location.trim().length > 0
+const isBookmarkNameValid = (location) => {
+ return location != null && location.trim().length > 0
}
const showOnlyFavicon = () => {
@@ -115,7 +101,7 @@ const getToolbarBookmarks = (state) => {
if (favicon && onlyFavicon) {
widthAccountedFor += padding + iconWidth + currentChevronWidth
} else {
- const text = current.get('customTitle') || current.get('title') || current.get('location')
+ const text = current.get('title') || current.get('location')
widthAccountedFor += Math.min(calculateTextWidth(text, `${fontSize} ${fontFamily}`) + padding + iconWidth + currentChevronWidth, maxWidth)
}
widthAccountedFor += margin
@@ -133,12 +119,69 @@ const getToolbarBookmarks = (state) => {
return lastValue
}
+const getDetailFromFrame = (frame) => {
+ return Immutable.fromJS({
+ location: frame.get('location'),
+ title: frame.get('title'),
+ partitionNumber: frame.get('partitionNumber'),
+ favicon: frame.get('icon'),
+ themeColor: frame.get('themeColor') || frame.get('computedThemeColor')
+ })
+}
+
+/**
+ * Checks if a location is bookmarked.
+ *
+ * @param state The application state Immutable map
+ * @param {string} location
+ * @return {boolean}
+ */
+const isLocationBookmarked = (state, location) => {
+ const bookmarks = bookmarksState.getBookmarks(state)
+ const siteKeys = siteCache.getLocationSiteKeys(state, location)
+
+ if (!siteKeys || siteKeys.length === 0) {
+ return false
+ }
+
+ return siteKeys.some(key => bookmarks.has(key))
+}
+
+/**
+ * Converts a siteDetail to createProperties format
+ * @param {Object} bookmark - A bookmark detail as per app state
+ * @return {Object} A createProperties plain JS object, not ImmutableJS
+ */
+const toCreateProperties = (bookmark) => {
+ return {
+ url: bookmark.get('location'),
+ partitionNumber: bookmark.get('partitionNumber')
+ }
+}
+
+/**
+ * Filters bookmarks relative to a parent folder
+ * @param state - The application state
+ * @param folderKey The folder key to filter to
+ */
+const getBookmarksByParentId = (state, folderKey) => {
+ const bookmarks = bookmarksState.getBookmarks(state)
+ if (!folderKey) {
+ return bookmarks
+ }
+
+ return bookmarks.filter((bookmark) => bookmark.get('parentFolderId') === folderKey)
+}
+
module.exports = {
bookmarkHangerHeading,
- displayBookmarkName,
isBookmarkNameValid,
showOnlyFavicon,
showFavicon,
getDNDBookmarkData,
- getToolbarBookmarks
+ getToolbarBookmarks,
+ getDetailFromFrame,
+ isLocationBookmarked,
+ toCreateProperties,
+ getBookmarksByParentId
}
diff --git a/app/common/lib/historyUtil.js b/app/common/lib/historyUtil.js
index c3cade0a637..b3101b3a58c 100644
--- a/app/common/lib/historyUtil.js
+++ b/app/common/lib/historyUtil.js
@@ -8,15 +8,13 @@ const {makeImmutable} = require('../state/immutableUtil')
const siteUtil = require('../../../js/state/siteUtil')
const aboutHistoryMaxEntries = 500
-module.exports.maxEntries = aboutHistoryMaxEntries
-
const sortTimeDescending = (left, right) => {
if (left.get('lastAccessedTime') < right.get('lastAccessedTime')) return 1
if (left.get('lastAccessedTime') > right.get('lastAccessedTime')) return -1
return 0
}
-module.exports.getHistory = (sites) => {
+const getHistory = (sites) => {
sites = makeImmutable(sites) ? makeImmutable(sites).toList() : new Immutable.List()
return sites.filter((site) => siteUtil.isHistoryEntry(site))
.sort(sortTimeDescending)
@@ -30,7 +28,7 @@ const getDayString = (entry, locale) => {
: ''
}
-module.exports.groupEntriesByDay = (history, locale) => {
+const groupEntriesByDay = (history, locale) => {
const reduced = history.reduce((previousValue, currentValue, currentIndex, array) => {
const result = currentIndex === 1 ? [] : previousValue
if (currentIndex === 1) {
@@ -60,7 +58,7 @@ module.exports.groupEntriesByDay = (history, locale) => {
* Return an array with ALL entries.
* Format is expected to be array containing one array per day.
*/
-module.exports.totalEntries = (entriesByDay) => {
+const totalEntries = (entriesByDay) => {
entriesByDay = makeImmutable(entriesByDay) || new Immutable.List()
let result = new Immutable.List()
@@ -69,3 +67,50 @@ module.exports.totalEntries = (entriesByDay) => {
})
return result
}
+
+const prepareHistoryEntry = (siteDetail) => {
+ return makeImmutable({
+ lastAccessedTime: new Date().getTime(),
+ objectId: undefined,
+ title: siteDetail.get('title'),
+ location: siteDetail.get('location'),
+ themeColor: siteDetail.get('themeColor'),
+ favicon: siteDetail.get('favicon', siteDetail.get('icon')),
+ count: 1
+ })
+}
+
+const mergeSiteDetails = (oldDetail, newDetail) => {
+ const objectId = newDetail.has('objectId') ? newDetail.get('objectId') : oldDetail.get('objectId', undefined)
+
+ let site = makeImmutable({
+ title: newDetail.get('title'),
+ location: newDetail.get('location'),
+ count: ~~oldDetail.get('count', 0) + 1,
+ lastAccessedTime: new Date().getTime(),
+ objectId
+ })
+
+ const themeColor = newDetail.has('themeColor') ? newDetail.get('themeColor') : oldDetail.get('themeColor')
+ if (themeColor) {
+ site = site.set('themeColor', themeColor)
+ }
+
+ // we need to have a fallback to icon, because frame has icon for it
+ const favicon = (newDetail.has('favicon') || newDetail.has('icon'))
+ ? newDetail.get('favicon', newDetail.get('icon'))
+ : oldDetail.get('favicon')
+ if (favicon) {
+ site = site.set('favicon', favicon)
+ }
+
+ return site
+}
+
+module.exports = {
+ getHistory,
+ groupEntriesByDay,
+ totalEntries,
+ prepareHistoryEntry,
+ mergeSiteDetails
+}
diff --git a/app/common/lib/menuUtil.js b/app/common/lib/menuUtil.js
index 65f24d89987..611c79d12cd 100644
--- a/app/common/lib/menuUtil.js
+++ b/app/common/lib/menuUtil.js
@@ -72,7 +72,7 @@ module.exports.setTemplateItemChecked = (template, label, checked) => {
}
return null
}
-
+// TODO refactor to the new structure
const createBookmarkTemplateItems = (bookmarks, parentFolderId) => {
const filteredBookmarks = parentFolderId
? bookmarks.filter((bookmark) => bookmark.get('parentFolderId') === parentFolderId)
@@ -89,7 +89,7 @@ const createBookmarkTemplateItems = (bookmarks, parentFolderId) => {
// and as such there may need to be another mechanism or cache
//
// see: https://github.com/brave/browser-laptop/issues/3050
- label: site.get('customTitle') || site.get('title') || site.get('location'),
+ label: site.get('title') || site.get('location'),
click: (item, focusedWindow, e) => {
if (eventUtil.isForSecondaryAction(e)) {
appActions.createTabRequested({
@@ -106,7 +106,7 @@ const createBookmarkTemplateItems = (bookmarks, parentFolderId) => {
const folderId = site.get('folderId')
const submenuItems = bookmarks.filter((bookmark) => bookmark.get('parentFolderId') === folderId)
payload.push({
- label: site.get('customTitle') || site.get('title'),
+ label: site.get('title'),
submenu: submenuItems.count() > 0 ? createBookmarkTemplateItems(bookmarks, folderId) : null
})
}
diff --git a/app/common/lib/pinnedSitesUtil.js b/app/common/lib/pinnedSitesUtil.js
new file mode 100644
index 00000000000..d9662763128
--- /dev/null
+++ b/app/common/lib/pinnedSitesUtil.js
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Immutable = require('immutable')
+const siteUtil = require('../../../js/state/siteUtil')
+const {makeImmutable} = require('../state/immutableUtil')
+
+const getSitesBySubkey = (sites, siteKey) => {
+ if (!sites || !siteKey) {
+ return makeImmutable([])
+ }
+ const splitKey = siteKey.split('|', 2)
+ const partialKey = splitKey.join('|')
+ const matches = sites.filter((site, key) => {
+ return key.indexOf(partialKey) > -1
+ })
+ return matches.toList()
+}
+
+const getDetailsFromTab = (sites, tab) => {
+ let location = tab.get('url')
+ const partitionNumber = tab.get('partitionNumber')
+ let parentFolderId
+
+ // TODO check if needed https://github.com/brave/browser-laptop/pull/8588
+ // we need to find which sites should be send in, I am guessing bookmarks
+
+ // if site map is available, look up extra information:
+ // - original url (if redirected)
+ // - parent folder id
+ if (sites) {
+ // get all sites matching URL and partition (disregarding parentFolderId)
+ let siteKey = siteUtil.getSiteKey(makeImmutable({location, partitionNumber}))
+ let results = getSitesBySubkey(sites, siteKey)
+
+ // only check for provisional location if entry is not found
+ if (results.size === 0) {
+ // if provisional location is different, grab any results which have that URL
+ // this may be different if the site was redirected
+ const provisionalLocation = tab.getIn(['frame', 'provisionalLocation'])
+ if (provisionalLocation && provisionalLocation !== location) {
+ siteKey = siteUtil.getSiteKey(makeImmutable({
+ location: provisionalLocation,
+ partitionNumber
+ }))
+ results = results.merge(getSitesBySubkey(sites, siteKey))
+ }
+ }
+
+ // update details which get returned below
+ if (results.size > 0) {
+ location = results.getIn([0, 'location'])
+ parentFolderId = results.getIn([0, 'parentFolderId'])
+ }
+ }
+
+ const siteDetail = {
+ location: location,
+ title: tab.get('title')
+ }
+
+ // TODO I think that we don't need this one
+ if (partitionNumber) {
+ siteDetail.partitionNumber = partitionNumber
+ }
+
+ if (parentFolderId) {
+ siteDetail.parentFolderId = parentFolderId
+ }
+
+ return makeImmutable(siteDetail)
+}
+
+const getDetailFromProperties = (createProperties) => {
+ const siteDetail = {
+ location: createProperties.get('url')
+ }
+
+ if (createProperties.get('partitionNumber') !== undefined) {
+ siteDetail.partitionNumber = createProperties.get('partitionNumber')
+ }
+ return makeImmutable(siteDetail)
+}
+
+const getDetailFromFrame = (frame) => {
+ const pinnedLocation = frame.get('pinnedLocation')
+ let location = frame.get('location')
+ if (pinnedLocation !== 'about:blank') {
+ location = pinnedLocation
+ }
+
+ return makeImmutable({
+ location,
+ title: frame.get('title'),
+ partitionNumber: frame.get('partitionNumber'),
+ favicon: frame.get('icon'),
+ themeColor: frame.get('themeColor') || frame.get('computedThemeColor')
+ })
+}
+
+const getPinnedSiteProps = site => {
+ return Immutable.fromJS({
+ location: site.get('location'),
+ order: site.get('order'),
+ partitionNumber: site.get('partitionNumber', 0)
+ })
+}
+
+module.exports = {
+ getDetailsFromTab,
+ getDetailFromProperties,
+ getDetailFromFrame,
+ getPinnedSiteProps
+}
diff --git a/app/common/lib/siteSuggestions.js b/app/common/lib/siteSuggestions.js
index 229d1bb4841..e2c40e21255 100644
--- a/app/common/lib/siteSuggestions.js
+++ b/app/common/lib/siteSuggestions.js
@@ -61,11 +61,8 @@ const tokenizeInput = (data) => {
return []
}
url = data.location
- if (data.customTitle) {
- parts = getPartsFromNonUrlInput(data.customTitle)
- }
if (data.title) {
- parts = parts.concat(getPartsFromNonUrlInput(data.title))
+ parts = getPartsFromNonUrlInput(data.title)
}
if (data.tags) {
parts = parts.concat(data.tags.map(getTagToken))
diff --git a/app/common/lib/windowsUtil.js b/app/common/lib/windowsUtil.js
deleted file mode 100644
index 4141a5a060d..00000000000
--- a/app/common/lib/windowsUtil.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Immutable = require('immutable')
-
-const getPinnedSiteProps = site => {
- return Immutable.fromJS({
- location: site.get('location'),
- order: site.get('order'),
- partitionNumber: site.get('partitionNumber') || 0
- })
-}
-
-module.exports = {
- getPinnedSiteProps
-}
diff --git a/app/common/state/aboutHistoryState.js b/app/common/state/aboutHistoryState.js
index 6f9048993e6..f52a4fede69 100644
--- a/app/common/state/aboutHistoryState.js
+++ b/app/common/state/aboutHistoryState.js
@@ -2,7 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
+const Immutable = require('immutable')
const {makeImmutable} = require('./immutableUtil')
+const historyState = require('./historyState')
const historyUtil = require('../lib/historyUtil')
const aboutHistoryState = {
@@ -10,10 +12,17 @@ const aboutHistoryState = {
state = makeImmutable(state)
return state.getIn(['about', 'history'])
},
+
setHistory: (state) => {
state = makeImmutable(state)
- state = state.setIn(['about', 'history', 'entries'],
- historyUtil.getHistory(state.get('sites')))
+ const sites = historyState.getSites(state)
+ state = state.setIn(['about', 'history', 'entries'], historyUtil.getHistory(sites))
+ return state.setIn(['about', 'history', 'updatedStamp'], new Date().getTime())
+ },
+
+ clearHistory: (state) => {
+ state = makeImmutable(state)
+ state = state.setIn(['about', 'history', 'entries'], Immutable.Map())
return state.setIn(['about', 'history', 'updatedStamp'], new Date().getTime())
}
}
diff --git a/app/common/state/aboutNewTabState.js b/app/common/state/aboutNewTabState.js
index ea855edaf04..ac9a551bdd2 100644
--- a/app/common/state/aboutNewTabState.js
+++ b/app/common/state/aboutNewTabState.js
@@ -31,6 +31,13 @@ const aboutNewTabState = {
topSites = makeImmutable(topSites)
state = state.setIn(['about', 'newtab', 'sites'], topSites)
return state.setIn(['about', 'newtab', 'updatedStamp'], new Date().getTime())
+ },
+
+ clearTopSites: (state) => {
+ state = makeImmutable(state)
+
+ state = state.setIn(['about', 'newtab', 'sites'], makeImmutable([]))
+ return state.setIn(['about', 'newtab', 'updatedStamp'], new Date().getTime())
}
}
diff --git a/app/common/state/bookmarkFoldersState.js b/app/common/state/bookmarkFoldersState.js
new file mode 100644
index 00000000000..d9aa9ee81a9
--- /dev/null
+++ b/app/common/state/bookmarkFoldersState.js
@@ -0,0 +1,126 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const assert = require('assert')
+const Immutable = require('immutable')
+
+// Actions
+const syncActions = require('../../../js/actions/syncActions')
+
+// Constants
+const settings = require('../../../js/constants/settings')
+
+// State
+const bookmarksState = require('./bookmarksState')
+
+// Utils
+const bookmarkFoldersUtil = require('../lib/bookmarkFoldersUtil')
+const siteUtil = require('../../../js/state/siteUtil')
+const {makeImmutable, isMap} = require('./immutableUtil')
+const {getSetting} = require('../../../js/settings')
+
+const validateState = function (state) {
+ state = makeImmutable(state)
+ assert.ok(isMap(state), 'state must be an Immutable.Map')
+ assert.ok(isMap(state.get('bookmarkFolders')), 'state must contain an Immutable.Map of bookmarkFolders')
+ return state
+}
+
+const bookmarkFoldersState = {
+ getFolders: (state) => {
+ state = validateState(state)
+ return state.get('bookmarkFolders', Immutable.Map())
+ },
+
+ getFolder: (state, folderKey) => {
+ state = validateState(state)
+ return state.getIn(['bookmarkFolders', folderKey], Immutable.Map())
+ },
+
+ addFolder: (state, folderDetails) => {
+ state = validateState(state)
+ let folders = state.get('bookmarkFolders')
+
+ const folderId = bookmarkFoldersUtil.getNextFolderId(folders)
+
+ const newFolder = makeImmutable({
+ title: folderDetails.get('title'),
+ folderId: ~~folderId,
+ parentFolderId: ~~folderDetails.get('parentFolderId', 0),
+ order: folders.size, // TODO order should be based on a parent
+ partitionNumber: ~~folderDetails.get('partitionNumber', 0),
+ objectId: null
+ })
+
+ state = state.setIn(['bookmarkFolders', folderId], newFolder)
+ return state
+ },
+
+ editFolder: (state, folderDetails, editKey) => {
+ state = validateState(state)
+ let sites = state.get('bookmarkFolders')
+
+ const oldFolder = sites.get(editKey)
+
+ const newFolder = oldFolder.merge(makeImmutable({
+ title: folderDetails.get('title'),
+ parentFolderId: ~~folderDetails.get('parentFolderId', 0)
+ }))
+
+ state = state.setIn(['bookmarkFolders', editKey], newFolder)
+ return state
+ },
+
+ removeFolder: (state, folderKey) => {
+ const folders = bookmarkFoldersState.getFolders(state)
+ const folder = bookmarkFoldersState.getFolder(state, folderKey)
+
+ if (folder.isEmpty()) {
+ return state
+ }
+
+ if (getSetting(settings.SYNC_ENABLED) === true) {
+ syncActions.removeSite(folder)
+ }
+
+ const folderId = folder.get('folderId')
+ state = bookmarksState.removeBookmarksByParentId(state, folderId)
+ folders.filter(folder => folder.get('parentId') === folderKey)
+ .map(folder => {
+ state = bookmarkFoldersState.removeFolder(state, folder.get('folderId'))
+ })
+
+ // TODO add reorder function both folders and bookmarks
+
+ return state.deleteIn(['sites', folderKey])
+ },
+
+ getFoldersWithoutKey: (state, folderKey, parentId = 0, labelPrefix = '') => {
+ let folders = []
+ const results = bookmarkFoldersState.getFolders(state)
+ .filter(site => site.get('parentFolderId', 0) === parentId)
+ .toList()
+ .sort(siteUtil.siteSort)
+
+ const resultSize = results.size
+ for (let i = 0; i < resultSize; i++) {
+ const folder = results.get(i)
+ if (folder.get('folderId') === folderKey) {
+ continue
+ }
+
+ const label = labelPrefix + folder.get('title')
+ folders.push({
+ folderId: folder.get('folderId'),
+ label
+ })
+ const subSites = bookmarkFoldersState.getFoldersWithoutKey(state, folderKey, folder.get('folderId'), (label || '') + ' / ')
+ folders = folders.concat(subSites)
+ }
+
+ return folders
+ }
+}
+
+module.exports = bookmarkFoldersState
diff --git a/app/common/state/bookmarksState.js b/app/common/state/bookmarksState.js
new file mode 100644
index 00000000000..208c8199f93
--- /dev/null
+++ b/app/common/state/bookmarksState.js
@@ -0,0 +1,149 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const assert = require('assert')
+const Immutable = require('immutable')
+
+// Constants
+const settings = require('../../../js/constants/settings')
+
+// State
+const historyState = require('./historyState')
+
+// Actions
+const syncActions = require('../../../js/actions/syncActions')
+
+// Utils
+const siteUtil = require('../../../js/state/siteUtil')
+const UrlUtil = require('../../../js/lib/urlutil')
+const siteCache = require('./siteCache')
+const {getSetting} = require('../../../js/settings')
+const {makeImmutable, isMap} = require('./immutableUtil')
+
+const validateState = function (state) {
+ state = makeImmutable(state)
+ assert.ok(isMap(state), 'state must be an Immutable.Map')
+ assert.ok(isMap(state.get('bookmarks')), 'state must contain an Immutable.Map of bookmarks')
+ return state
+}
+
+const bookmarksState = {
+ getBookmarks: (state) => {
+ state = validateState(state)
+ return state.get('bookmarks')
+ },
+
+ getBookmark: (state, key) => {
+ state = validateState(state)
+ return state.getIn(['bookmarks', key], Immutable.Map())
+ },
+
+ addBookmark: (state, bookmarkDetail) => {
+ let bookmarks = bookmarksState.getBookmarks(state)
+
+ let location
+ if (bookmarkDetail.has('location')) {
+ location = UrlUtil.getLocationIfPDF(bookmarkDetail.get('location'))
+ bookmarkDetail = bookmarkDetail.set('location', location)
+ }
+
+ const key = siteUtil.getSiteKey(bookmarkDetail)
+ const historyItem = historyState.getSite(state, key)
+
+ let bookmark = makeImmutable({
+ title: bookmarkDetail.get('title'),
+ location: bookmarkDetail.get('location'),
+ parentFolderId: ~~bookmarkDetail.get('parentFolderId', 0),
+ order: bookmarks.size, // TODO order should be based on a parentId
+ partitionNumber: ~~historyItem.get('partitionNumber', 0),
+ objectId: null,
+ favicon: historyItem.get('favicon'),
+ themeColor: historyItem.get('themeColor')
+ })
+
+ if (key === null) {
+ return state
+ }
+
+ state = state.setIn(['bookmarks', key], bookmark)
+ state = siteCache.addLocationSiteKey(state, location, key)
+ return state
+ },
+
+ editBookmark: (state, bookmarkDetail, editKey) => {
+ const bookmark = bookmarksState.getBookmark(state, editKey)
+ let newBookmark = bookmark.merge(bookmarkDetail)
+
+ let location
+ if (newBookmark.has('location')) {
+ location = UrlUtil.getLocationIfPDF(newBookmark.get('location'))
+ newBookmark = newBookmark.set('location', location)
+ }
+
+ state = siteCache.removeLocationSiteKey(state, bookmark.get('location'), editKey)
+ state = state.deleteIn(['bookmarks', editKey])
+
+ const newKey = siteUtil.getSiteKey(newBookmark)
+ if (newKey === null) {
+ return state
+ }
+
+ state = state.setIn(['bookmarks', newKey], newBookmark)
+ state = siteCache.addLocationSiteKey(state, location, newKey)
+ return state
+ },
+
+ removeBookmark: (state, bookmarkKey) => {
+ const bookmark = bookmarksState.getBookmark(state, bookmarkKey)
+
+ if (bookmark.isEmpty()) {
+ return state
+ }
+
+ if (getSetting(settings.SYNC_ENABLED) === true) {
+ syncActions.removeSite(bookmark)
+ }
+
+ state = siteCache.removeLocationSiteKey(state, bookmark.get('location'), bookmarkKey)
+
+ // TODO add reorder function
+
+ return state.deleteIn(['bookmarks', bookmarkKey])
+ },
+
+ removeBookmarksByParentId: (state, parentId) => {
+ let bookmarks = bookmarksState.getBookmarks(state)
+
+ bookmarks = bookmarks.filter(bookmark => bookmark.get('parentId') !== parentId)
+
+ return state.set('bookmarks', bookmarks)
+ },
+
+ /**
+ * Update the favicon URL for all entries in the state sites
+ * which match a given location. Currently, there should only be
+ * one match, but this will handle multiple.
+ *
+ * @param state The application state
+ * @param location URL for the entry needing an update
+ * @param favicon favicon URL
+ */
+ updateSiteFavicon: (state, location, favicon) => {
+ if (UrlUtil.isNotURL(location)) {
+ return state
+ }
+
+ const siteKeys = siteCache.getLocationSiteKeys(state, location)
+ if (!siteKeys || siteKeys.length === 0) {
+ return state
+ }
+
+ siteKeys.forEach((siteKey) => {
+ state = state.setIn(['bookmarks', siteKey, 'favicon'], favicon)
+ })
+ return state
+ }
+}
+
+module.exports = bookmarksState
diff --git a/app/common/state/historyState.js b/app/common/state/historyState.js
new file mode 100644
index 00000000000..326b310223a
--- /dev/null
+++ b/app/common/state/historyState.js
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const assert = require('assert')
+const Immutable = require('immutable')
+const siteUtil = require('../../../js/state/siteUtil')
+const historyUtil = require('../lib/historyUtil')
+const urlUtil = require('../../../js/lib/urlutil')
+const siteCache = require('./siteCache')
+const {makeImmutable, isMap} = require('./immutableUtil')
+
+const validateState = function (state) {
+ state = makeImmutable(state)
+ assert.ok(isMap(state), 'state must be an Immutable.Map')
+ assert.ok(isMap(state.get('historySites')), 'state must contain an Immutable.Map of historySites')
+ return state
+}
+
+const historyState = {
+ getSites: (state) => {
+ state = validateState(state)
+ return state.get('historySites', Immutable.Map())
+ },
+
+ getSite: (state, key) => {
+ state = validateState(state)
+ return state.getIn(['historySites', key], Immutable.Map())
+ },
+
+ addSite: (state, siteDetail) => {
+ let sites = historyState.getSites(state)
+ let siteKey = siteUtil.getSiteKey(siteDetail)
+ siteDetail = makeImmutable(siteDetail)
+
+ const oldSite = sites.get(siteKey)
+ let site
+ if (oldSite) {
+ site = historyUtil.mergeSiteDetails(oldSite, siteDetail)
+ } else {
+ let location
+ if (siteDetail.has('location')) {
+ location = urlUtil.getLocationIfPDF(siteDetail.get('location'))
+ siteDetail = siteDetail.set('location', location)
+ }
+
+ siteKey = siteUtil.getSiteKey(siteDetail)
+ site = historyUtil.prepareHistoryEntry(siteDetail)
+ state = siteCache.addLocationSiteKey(state, location, siteKey)
+ }
+
+ state = state.setIn(['historySites', siteKey], site)
+ return state
+ },
+
+ removeSite: () => {
+ // TODO implement
+ },
+
+ clearSites: (state) => {
+ return state.set('historySites', Immutable.Map())
+ }
+}
+
+module.exports = historyState
diff --git a/app/common/state/pinnedSitesState.js b/app/common/state/pinnedSitesState.js
new file mode 100644
index 00000000000..b231d780a53
--- /dev/null
+++ b/app/common/state/pinnedSitesState.js
@@ -0,0 +1,125 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const assert = require('assert')
+const Immutable = require('immutable')
+const siteCache = require('./siteCache')
+const siteUtil = require('../../../js/state/siteUtil')
+const urlUtil = require('../../../js/lib/urlutil')
+const {makeImmutable, isMap} = require('./immutableUtil')
+
+const validateState = function (state) {
+ state = makeImmutable(state)
+ assert.ok(isMap(state), 'state must be an Immutable.Map')
+ assert.ok(isMap(state.get('pinnedSites')), 'state must contain an Immutable.Map of pinnedSites')
+ return state
+}
+
+const pinnedSiteState = {
+ getSites: (state) => {
+ state = validateState(state)
+ return state.get('pinnedSites')
+ },
+
+ /**
+ * Adds the specified siteDetail in appState.pinnedSites.
+ * @param {Immutable.Map} state The application state Immutable map
+ * @param {Immutable.Map} site The siteDetail that we want to add
+ */
+ addPinnedSite: (state, site) => {
+ state = validateState(state)
+ const sites = pinnedSiteState.getSites(state) || Immutable.Map()
+ let location
+ if (site.has('location')) {
+ location = urlUtil.getLocationIfPDF(site.get('location'))
+ site = site.set('location', location)
+ }
+
+ site = site.set('order', sites.size)
+
+ const key = siteUtil.getSiteKey(site)
+ if (key === null) {
+ return state
+ }
+
+ state = state.setIn(['pinnedSites', key], site)
+ state = siteCache.addLocationSiteKey(state, location, key)
+ return state
+ },
+
+ /**
+ * Removes the given pinned site from the pinnedSites
+ *
+ * @param {Immutable.Map} state The application state Immutable map
+ * @param {Immutable.Map} siteDetail The siteDetail to be removed
+ * @return {Immutable.Map} The new state Immutable object
+ */
+ removePinnedSite: (state, siteDetail) => {
+ state = validateState(state)
+ const key = siteUtil.getSiteKey(siteDetail)
+ if (!key) {
+ return state
+ }
+
+ const location = siteDetail.get('location')
+ state = siteCache.removeLocationSiteKey(state, location, key)
+
+ const stateKey = ['pinnedSites', key]
+ let site = state.getIn(stateKey)
+ if (!site) {
+ return state
+ }
+
+ // TODO update order, so that is up to date
+
+ return state.deleteIn(stateKey, site)
+ },
+
+ /**
+ * Moves the specified pinned site from one location to another
+ *
+ * @param state The application state Immutable map
+ * @param sourceKey The site key to move
+ * @param destinationKey The site key to move to
+ * @param prepend Whether the destination detail should be prepended or not
+ * @return The new state Immutable object
+ */
+ reOrderSite: (state, sourceKey, destinationKey, prepend) => {
+ state = validateState(state)
+ let sites = state.get('pinnedSites')
+ let sourceSite = sites.get(sourceKey, Immutable.Map())
+ const destinationSite = sites.get(destinationKey, Immutable.Map())
+
+ if (sourceSite.isEmpty()) {
+ return state
+ }
+
+ const sourceSiteIndex = sourceSite.get('order')
+ const destinationSiteIndex = destinationSite.get('order')
+ let newIndex = destinationSiteIndex + (prepend ? 0 : 1)
+ if (destinationSiteIndex > sourceSiteIndex) {
+ --newIndex
+ }
+
+ state = state.set('pinnedSites', state.get('pinnedSites').map((site, index) => {
+ const siteOrder = site.get('order')
+ if (index === sourceKey) {
+ return site
+ }
+
+ if (siteOrder >= newIndex && siteOrder < sourceSiteIndex) {
+ return site.set('order', siteOrder + 1)
+ } else if (siteOrder <= newIndex && siteOrder > sourceSiteIndex) {
+ return site.set('order', siteOrder - 1)
+ }
+
+ return site
+ }))
+
+ sourceSite = sourceSite.set('order', newIndex)
+ return state.setIn(['pinnedSites', sourceKey], sourceSite)
+ }
+}
+
+module.exports = pinnedSiteState
diff --git a/app/common/state/siteCache.js b/app/common/state/siteCache.js
index beb1a207928..a436daae1f9 100644
--- a/app/common/state/siteCache.js
+++ b/app/common/state/siteCache.js
@@ -7,6 +7,8 @@ const siteUtil = require('../../../js/state/siteUtil')
const appUrlUtil = require('../../../js/lib/appUrlUtil')
const UrlUtil = require('../../../js/lib/urlutil')
+// TODO what to do with this file?
+
const createLocationSiteKeysCache = (state) => {
state = state.set('locationSiteKeysCache', new Immutable.Map())
state.get('sites').forEach((site, siteKey) => {
diff --git a/app/common/state/siteState.js b/app/common/state/siteState.js
deleted file mode 100644
index 0f74f8390a6..00000000000
--- a/app/common/state/siteState.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const assert = require('assert')
-const {makeImmutable, isMap, isList} = require('./immutableUtil')
-
-const validateState = function (state) {
- state = makeImmutable(state)
- assert.ok(isMap(state), 'state must be an Immutable.Map')
- assert.ok(isList(state.get('sites')), 'state must contain an Immutable.List of sites')
- return state
-}
-
-const siteState = {
- getSites: (state) => {
- state = validateState(state)
- return state.get('sites')
- }
-}
-
-module.exports = siteState
diff --git a/app/common/state/tabState.js b/app/common/state/tabState.js
index 4cc5ece7b24..3518a664dc0 100644
--- a/app/common/state/tabState.js
+++ b/app/common/state/tabState.js
@@ -13,7 +13,7 @@ const windowState = require('./windowState')
const { makeImmutable, isMap, isList } = require('./immutableUtil')
// this file should eventually replace frameStateUtil
const frameStateUtil = require('../../../js/state/frameStateUtil')
-const {isLocationBookmarked} = require('../../../js/state/siteUtil')
+const bookmarkUtil = require('../lib/bookmarkUtil')
const validateId = function (propName, id) {
assert.ok(id, `${propName} cannot be null`)
@@ -239,6 +239,13 @@ const tabState = {
return state.get('tabs').filter((tab) => !!tab.get('pinned'))
},
+ isTabPinned: (state, tabId) => {
+ state = validateState(state)
+ tabId = validateId('tabId', tabId)
+ const tab = tabState.getByTabId(state, tabId)
+ return tab != null ? !!tab.get('pinned') : false
+ },
+
getNonPinnedTabs: (state) => {
state = validateState(state)
return state.get('tabs').filter((tab) => !tab.get('pinned'))
@@ -448,7 +455,7 @@ const tabState = {
}
const frameLocation = action.getIn(['frame', 'location'])
- const frameBookmarked = isLocationBookmarked(state, frameLocation)
+ const frameBookmarked = bookmarkUtil.isLocationBookmarked(state, frameLocation)
const frameValue = action.get('frame').set('bookmarked', frameBookmarked)
tabValue = tabValue.set('frame', makeImmutable(frameValue))
return tabState.updateTabValue(state, tabValue)
diff --git a/app/importer.js b/app/importer.js
index b88c47d7fac..1af8fda354f 100644
--- a/app/importer.js
+++ b/app/importer.js
@@ -10,17 +10,29 @@ const importer = electron.importer
const dialog = electron.dialog
const BrowserWindow = electron.BrowserWindow
const session = electron.session
-const siteUtil = require('../js/state/siteUtil')
-const AppStore = require('../js/stores/appStore')
+
+// Store
+const appStore = require('../js/stores/appStore')
+
+// State
+const tabState = require('./common/state/tabState')
+const bookmarksState = require('./common/state/bookmarksState')
+const bookmarkFoldersState = require('./common/state/bookmarkFoldersState')
+
+// Constants
const siteTags = require('../js/constants/siteTags')
-const appActions = require('../js/actions/appActions')
const messages = require('../js/constants/messages')
const settings = require('../js/constants/settings')
-const getSetting = require('../js/settings').getSetting
+
+// Actions
+const appActions = require('../js/actions/appActions')
+
+// Utils
+const {getSetting} = require('../js/settings')
const locale = require('./locale')
const tabMessageBox = require('./browser/tabMessageBox')
const {makeImmutable} = require('./common/state/immutableUtil')
-const tabState = require('./common/state/tabState')
+const bookmarkFoldersUtil = require('./common/lib/bookmarkFoldersUtil')
var isImportingBookmarks = false
var hasBookmarks
@@ -33,10 +45,8 @@ exports.init = () => {
exports.importData = (selected) => {
if (selected.get('favorites')) {
isImportingBookmarks = true
- const sites = AppStore.getState().get('sites')
- hasBookmarks = sites.find(
- (site) => siteUtil.isBookmark(site) || siteUtil.isFolder(site)
- )
+ const state = appStore.getState()
+ hasBookmarks = bookmarksState.getBookmarks(state).size > 0 || bookmarkFoldersState.getFolders(state).size > 0
}
if (selected !== undefined) {
importer.importData(selected.toJS())
@@ -45,10 +55,8 @@ exports.importData = (selected) => {
exports.importHTML = (selected) => {
isImportingBookmarks = true
- const sites = AppStore.getState().get('sites')
- hasBookmarks = sites.find(
- (site) => siteUtil.isBookmark(site) || siteUtil.isFolder(site)
- )
+ const state = appStore.getState()
+ hasBookmarks = bookmarksState.getBookmarks(state).size > 0 || bookmarkFoldersState.getFolders(state).size > 0
const files = dialog.showOpenDialog({
properties: ['openFile'],
filters: [{
@@ -69,7 +77,7 @@ importer.on('update-supported-browsers', (e, detail) => {
}
})
-importer.on('add-history-page', (e, history, visitSource) => {
+importer.on('add-history-page', (e, history) => {
let sites = []
for (let i = 0; i < history.length; ++i) {
const site = {
@@ -79,12 +87,13 @@ importer.on('add-history-page', (e, history, visitSource) => {
}
sites.push(site)
}
- appActions.addSite(makeImmutable(sites))
+ appActions.addHistorySite(makeImmutable(sites))
})
importer.on('add-homepage', (e, detail) => {
})
+// TODO do we need creation time?
const getParentFolderId = (path, pathMap, sites, topLevelFolderId, nextFolderIdObject) => {
const pathLen = path.length
if (!pathLen) {
@@ -96,7 +105,7 @@ const getParentFolderId = (path, pathMap, sites, topLevelFolderId, nextFolderIdO
parentFolderId = nextFolderIdObject.id++
pathMap[parentFolder] = parentFolderId
const folder = {
- customTitle: parentFolder,
+ title: parentFolder,
folderId: parentFolderId,
parentFolderId: getParentFolderId(path, pathMap, sites, topLevelFolderId, nextFolderIdObject),
lastAccessedTime: 0,
@@ -108,15 +117,18 @@ const getParentFolderId = (path, pathMap, sites, topLevelFolderId, nextFolderIdO
return parentFolderId
}
+// TODO refactor to the new structure after sort is implemented
importer.on('add-bookmarks', (e, bookmarks, topLevelFolder) => {
- let nextFolderId = siteUtil.getNextFolderId(AppStore.getState().get('sites'))
+ const state = appStore.getState()
+ const bookmarkFolders = bookmarkFoldersState.getFolders(state)
+ let nextFolderId = bookmarkFoldersUtil.getNextFolderId(bookmarkFolders)
let nextFolderIdObject = { id: nextFolderId }
let pathMap = {}
let sites = []
let topLevelFolderId = 0
topLevelFolderId = nextFolderIdObject.id++
sites.push({
- customTitle: siteUtil.getNextFolderName(AppStore.getState().get('sites'), topLevelFolder),
+ title: bookmarkFoldersUtil.getNextFolderName(bookmarkFolders, topLevelFolder),
folderId: topLevelFolderId,
parentFolderId: 0,
lastAccessedTime: 0,
@@ -130,7 +142,7 @@ importer.on('add-bookmarks', (e, bookmarks, topLevelFolder) => {
const folderId = nextFolderIdObject.id++
pathMap[bookmarks[i].title] = folderId
const folder = {
- customTitle: bookmarks[i].title,
+ title: bookmarks[i].title,
folderId: folderId,
parentFolderId: parentFolderId,
lastAccessedTime: 0,
@@ -141,7 +153,6 @@ importer.on('add-bookmarks', (e, bookmarks, topLevelFolder) => {
} else {
const site = {
title: bookmarks[i].title,
- customTitle: bookmarks[i].title,
location: bookmarks[i].url,
parentFolderId: parentFolderId,
lastAccessedTime: 0,
@@ -178,6 +189,7 @@ importer.on('add-favicons', (e, detail) => {
return site
}
})
+ // TODO what should be done here? update all bookmarks with new fav icons?
appActions.addSite(sites)
})
@@ -208,7 +220,7 @@ importer.on('add-cookies', (e, cookies) => {
})
const getActiveTabId = () => {
- return tabState.getActiveTabId(AppStore.getState())
+ return tabState.getActiveTabId(appStore.getState())
}
const showImportWarning = function () {
diff --git a/app/index.js b/app/index.js
index 2ee37e47f22..262fd96253a 100644
--- a/app/index.js
+++ b/app/index.js
@@ -159,6 +159,7 @@ app.on('ready', () => {
appActions.networkDisconnected()
})
+ // TODO what should we do here
loadAppStatePromise.then((initialState) => {
// Do this after loading the state
// For tests we always want to load default app state
@@ -301,7 +302,7 @@ app.on('ready', () => {
})
ipcMain.on(messages.EXPORT_BOOKMARKS, () => {
- BookmarksExporter.showDialog(appStore.getState().get('sites'))
+ BookmarksExporter.showDialog(appStore.getState())
})
// DO NOT TO THIS LIST - see above
@@ -330,7 +331,7 @@ app.on('ready', () => {
})
process.on(messages.EXPORT_BOOKMARKS, () => {
- BookmarksExporter.showDialog(appStore.getState().get('sites'))
+ BookmarksExporter.showDialog(appStore.getState())
})
ready = true
diff --git a/app/renderer/components/bookmarks/addEditBookmarkFolder.js b/app/renderer/components/bookmarks/addEditBookmarkFolder.js
new file mode 100644
index 00000000000..36e6b78da52
--- /dev/null
+++ b/app/renderer/components/bookmarks/addEditBookmarkFolder.js
@@ -0,0 +1,103 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const React = require('react')
+const Immutable = require('immutable')
+const {StyleSheet, css} = require('aphrodite/no-important')
+
+// Components
+const ReduxComponent = require('../reduxComponent')
+const Dialog = require('../common/dialog')
+const AddEditBookmarkFolderForm = require('./addEditBookmarkFolderForm')
+const {CommonFormBookmarkHanger} = require('../common/commonForm')
+
+// State
+const bookmarkFoldersState = require('../../../common/state/bookmarkFoldersState')
+const bookmarksState = require('../../../common/state/bookmarksState')
+
+// Actions
+const windowActions = require('../../../../js/actions/windowActions')
+
+// Utils
+const cx = require('../../../../js/lib/classSet')
+const bookmarkFoldersUtil = require('../../../common/lib/bookmarkFoldersUtil')
+
+// Styles
+const globalStyles = require('../styles/global')
+
+class AddEditBookmarkFolder extends React.Component {
+ constructor (props) {
+ super(props)
+ this.onClose = this.onClose.bind(this)
+ this.onClick = this.onClick.bind(this)
+ }
+
+ onClose () {
+ windowActions.onBookmarkFolderClose()
+ }
+
+ onClick (e) {
+ e.stopPropagation()
+ }
+
+ mergeProps (state, ownProps) {
+ const currentWindow = state.get('currentWindow')
+ const bookmarkDetail = currentWindow.get('bookmarkFolderDetail', Immutable.Map())
+ const folderDetails = bookmarkDetail.get('folderDetail') || Immutable.Map()
+ const editMode = bookmarkDetail.has('editKey')
+
+ const props = {}
+ // used in renderer
+ props.heading = editMode
+ ? 'bookmarkFolderEditing'
+ : 'bookmarkFolderAdding'
+ props.parentFolderId = folderDetails.get('parentFolderId')
+ props.partitionNumber = folderDetails.get('partitionNumber')
+ props.folderName = folderDetails.get('title')
+ props.isFolderNameValid = bookmarkFoldersUtil.isFolderNameValid(folderDetails.get('title'))
+ props.folders = bookmarkFoldersState.getFoldersWithoutKey(state, folderDetails.get('folderId')) // TODO (nejc) improve, primitives only
+ props.editKey = bookmarkDetail.get('editKey', null)
+ props.closestKey = bookmarkDetail.get('closestKey', null)
+ props.hasBookmarks = bookmarksState.getBookmarks(state).size > 0 || bookmarkFoldersState.getFolders(state).size > 0
+
+ return props
+ }
+
+ render () {
+ return
+ }
+}
+
+const styles = StyleSheet.create({
+ // Copied from commonForm.js
+ commonFormSection: {
+ // PR #7985
+ margin: `${globalStyles.spacing.dialogInsideMargin} 30px`
+ },
+ commonFormTitle: {
+ color: globalStyles.color.braveOrange,
+ fontSize: '1.2em'
+ }
+})
+
+module.exports = ReduxComponent.connect(AddEditBookmarkFolder)
diff --git a/app/renderer/components/bookmarks/addEditBookmarkFolderForm.js b/app/renderer/components/bookmarks/addEditBookmarkFolderForm.js
new file mode 100644
index 00000000000..614daa610f3
--- /dev/null
+++ b/app/renderer/components/bookmarks/addEditBookmarkFolderForm.js
@@ -0,0 +1,235 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const React = require('react')
+const Immutable = require('immutable')
+const {StyleSheet, css} = require('aphrodite/no-important')
+
+// Components
+const BrowserButton = require('../common/browserButton')
+const {
+ CommonFormSection,
+ CommonFormDropdown,
+ CommonFormButtonWrapper,
+ commonFormStyles
+} = require('../common/commonForm')
+
+// Actions
+const appActions = require('../../../../js/actions/appActions')
+const windowActions = require('../../../../js/actions/windowActions')
+
+// Constants
+const KeyCodes = require('../../../common/constants/keyCodes')
+const settings = require('../../../../js/constants/settings')
+
+// Utils
+const UrlUtil = require('../../../../js/lib/urlutil')
+const {getSetting} = require('../../../../js/settings')
+const bookmarkFoldersUtil = require('../../../common/lib/bookmarkFoldersUtil')
+
+// Styles
+const globalStyles = require('../styles/global')
+const commonStyles = require('../styles/commonStyles')
+
+class AddEditBookmarkFolderForm extends React.Component {
+ constructor (props) {
+ super(props)
+ this.onNameChange = this.onNameChange.bind(this)
+ this.onParentFolderChange = this.onParentFolderChange.bind(this)
+ this.onKeyDown = this.onKeyDown.bind(this)
+ this.onClose = this.onClose.bind(this)
+ this.onSave = this.onSave.bind(this)
+ this.onFolderRemove = this.onFolderRemove.bind(this)
+ this.state = {
+ title: props.folderName,
+ parentFolderId: props.parentFolderId,
+ isDisabled: props.isDisabled
+ }
+ }
+
+ componentDidMount () {
+ setImmediate(() => {
+ this.folderName.select()
+ })
+ }
+
+ onKeyDown (e) {
+ switch (e.keyCode) {
+ case KeyCodes.ENTER:
+ this.onSave()
+ break
+ case KeyCodes.ESC:
+ this.onClose()
+ break
+ }
+ }
+
+ onClose () {
+ windowActions.onBookmarkFolderClose()
+ }
+
+ updateButtonStatus (newValue) {
+ if (newValue !== this.state.isDisabled) {
+ this.setState({
+ isDisabled: newValue
+ })
+ }
+ }
+
+ onNameChange (e) {
+ let title = e.target.value
+
+ this.setState({
+ title: title
+ })
+
+ this.updateButtonStatus(!bookmarkFoldersUtil.isFolderNameValid(title))
+ }
+
+ onParentFolderChange (e) {
+ this.setState({
+ parentFolderId: ~~e.target.value
+ })
+ }
+
+ onSave () {
+ // First check if the title of the bookmarkDetail is set
+ if (this.state.isDisabled) {
+ return false
+ }
+
+ // show bookmark if hidden
+ if (!this.props.hasBookmarks && !getSetting(settings.SHOW_BOOKMARKS_TOOLBAR)) {
+ appActions.changeSetting(settings.SHOW_BOOKMARKS_TOOLBAR, true)
+ }
+
+ let data = Immutable.fromJS({
+ parentFolderId: this.state.parentFolderId
+ })
+
+ if (this.props.editKey != null) {
+ data = data.set('folderId', this.props.editKey)
+ }
+
+ // handle title input
+ let title = this.state.title
+ if (typeof title === 'string' && UrlUtil.isURL(title)) {
+ const punycodeUrl = UrlUtil.getPunycodeUrl(title)
+ if (punycodeUrl.replace(/\/$/, '') !== title) {
+ title = punycodeUrl
+ }
+ }
+ data = data.set('title', title)
+
+ if (this.props.editKey != null) {
+ appActions.editBookmarkFolder(data, this.props.editKey)
+ } else {
+ appActions.addBookmarkFolder(data, this.props.closestKey)
+ }
+
+ this.onClose()
+ }
+
+ onFolderRemove () {
+ appActions.removeBookmarkFolder(Immutable.fromJS({
+ parentFolderId: this.props.parentFolderId,
+ partitionNumber: this.props.partitionNumber,
+ folderId: this.props.editKey
+ }))
+ this.onClose()
+ }
+
+ render () {
+ return
+
+
+
+
+
+
+
+ {
+ this.props.folders.map((folder) => )
+ }
+
+
+
+
+
+ {
+ this.props.editKey != null
+ ?
+ :
+ }
+
+
+
+ }
+}
+
+const styles = StyleSheet.create({
+ bookmarkHanger__label: {
+ display: 'block',
+ marginBottom: `calc(${globalStyles.spacing.dialogInsideMargin} / 3)`
+ },
+ bookmarkHanger__marginRow: {
+ marginTop: `calc(${globalStyles.spacing.dialogInsideMargin} / 2)`
+ },
+
+ bookmark__sectionWrapper: {
+ display: 'flex',
+ flexFlow: 'column nowrap'
+ }
+})
+
+module.exports = AddEditBookmarkFolderForm
diff --git a/app/renderer/components/bookmarks/addEditBookmarkForm.js b/app/renderer/components/bookmarks/addEditBookmarkForm.js
index a744a8d50f4..2dd64b19ea4 100644
--- a/app/renderer/components/bookmarks/addEditBookmarkForm.js
+++ b/app/renderer/components/bookmarks/addEditBookmarkForm.js
@@ -22,7 +22,6 @@ const windowActions = require('../../../../js/actions/windowActions')
// Constants
const KeyCodes = require('../../../common/constants/keyCodes')
-const siteTags = require('../../../../js/constants/siteTags')
const settings = require('../../../../js/constants/settings')
// Utils
@@ -45,7 +44,7 @@ class AddEditBookmarkForm extends React.Component {
this.onSave = this.onSave.bind(this)
this.onRemoveBookmark = this.onRemoveBookmark.bind(this)
this.state = {
- title: props.bookmarkName,
+ title: props.title,
location: props.location,
parentFolderId: props.parentFolderId,
isDisabled: props.isDisabled
@@ -87,8 +86,6 @@ class AddEditBookmarkForm extends React.Component {
this.setState({
title: title
})
-
- this.updateButtonStatus(!isBookmarkNameValid(title, this.state.location, this.props.isFolder))
}
onLocationChange (e) {
@@ -98,7 +95,7 @@ class AddEditBookmarkForm extends React.Component {
location: location
})
- this.updateButtonStatus(!isBookmarkNameValid(this.state.title, location, this.props.isFolder))
+ this.updateButtonStatus(!isBookmarkNameValid(location))
}
onParentFolderChange (e) {
@@ -118,16 +115,10 @@ class AddEditBookmarkForm extends React.Component {
appActions.changeSetting(settings.SHOW_BOOKMARKS_TOOLBAR, true)
}
- const tag = this.props.isFolder ? siteTags.BOOKMARK_FOLDER : siteTags.BOOKMARK
let data = Immutable.fromJS({
- parentFolderId: this.state.parentFolderId,
- title: this.props.currentTitle
+ parentFolderId: this.state.parentFolderId
})
- if (this.props.isFolder && this.props.editKey != null) {
- data = data.set('folderId', this.props.editKey)
- }
-
// handle title input
let title = this.state.title
if (typeof title === 'string' && UrlUtil.isURL(title)) {
@@ -136,10 +127,7 @@ class AddEditBookmarkForm extends React.Component {
title = punycodeUrl
}
}
-
- if (this.props.currentTitle !== title || !title) {
- data = data.set('customTitle', title || 0)
- }
+ data = data.set('title', title)
// handle location input
let location = this.state.location
@@ -152,23 +140,16 @@ class AddEditBookmarkForm extends React.Component {
data = data.set('location', location)
if (this.props.editKey != null) {
- appActions.editBookmark(data, this.props.editKey, tag)
+ appActions.editBookmark(data, this.props.editKey)
} else {
- appActions.addBookmark(data, tag, this.props.closestKey)
+ appActions.addBookmark(data, this.props.closestKey)
}
this.onClose()
}
onRemoveBookmark () {
- const tag = this.props.isFolder ? siteTags.BOOKMARK_FOLDER : siteTags.BOOKMARK
- // TODO check if you need to add folderId as prop or you can use editKey
- appActions.removeSite(Immutable.fromJS({
- parentFolderId: this.props.parentFolderId,
- location: this.props.location,
- partitionNumber: this.props.partitionNumber,
- folderId: this.props.isFolder ? this.props.editKey : null
- }), tag)
+ appActions.removeBookmark(this.props.editKey)
this.onClose()
}
@@ -203,7 +184,7 @@ class AddEditBookmarkForm extends React.Component {
{
- !this.props.isFolder && !this.props.isAdded
+ !this.props.isAdded
?
siteUtil.isBookmark(site) || siteUtil.isFolder(site)
- )
+ props.hasBookmarks = bookmarksState.getBookmarks(state).size > 0 || bookmarkFoldersState.getFolders(state).size > 0
return props
}
@@ -132,14 +123,12 @@ class AddEditBookmarkHanger extends React.Component {
[css(styles.commonFormTitle)]: true
})} data-l10n-id={this.props.heading} />
0) {
Array.from(e.dataTransfer.items).forEach((item) => {
- item.getAsString((name) => appActions.addSite({ location: item.type, title: name }, siteTags.BOOKMARK))
+ item.getAsString((name) => appActions.addBookmark(Immutable.fromJS({
+ location: item.type,
+ title: name
+ })))
})
return
}
@@ -92,7 +94,7 @@ class BookmarksToolbar extends React.Component {
.map((x) => x.trim())
.filter((x) => !x.startsWith('#') && x.length > 0)
.forEach((url) =>
- appActions.addSite({ location: url }, siteTags.BOOKMARK))
+ appActions.addBookmark(Immutable.fromJS({ location: url })))
}
onDragEnter (e) {
diff --git a/app/renderer/components/frame/frame.js b/app/renderer/components/frame/frame.js
index 5ad3ef64eff..b40b10d73f0 100644
--- a/app/renderer/components/frame/frame.js
+++ b/app/renderer/components/frame/frame.js
@@ -624,7 +624,8 @@ class Frame extends React.Component {
// with setTitle. We either need to delay this call until the title is
// or add a way to update it
setTimeout(() => {
- appActions.addSite(siteUtil.getDetailFromFrame(this.frame))
+ // TODO add history site
+ appActions.addHistorySite(siteUtil.getDetailFromFrame(this.frame))
}, 250)
}
@@ -668,7 +669,7 @@ class Frame extends React.Component {
url: e.validatedURL
})
appActions.loadURLRequested(this.props.tabId, 'about:error')
- appActions.removeSite(siteUtil.getDetailFromFrame(this.frame))
+ appActions.removeHistorySite(siteUtil.getDetailFromFrame(this.frame))
} else if (isAborted(e.errorCode)) {
// just stay put
} else if (provisionLoadFailure) {
diff --git a/app/renderer/components/main/main.js b/app/renderer/components/main/main.js
index 5504295141d..c93ce69aed8 100644
--- a/app/renderer/components/main/main.js
+++ b/app/renderer/components/main/main.js
@@ -33,6 +33,7 @@ const WidevinePanel = require('./widevinePanel')
const AutofillAddressPanel = require('../autofill/autofillAddressPanel')
const AutofillCreditCardPanel = require('../autofill/autofillCreditCardPanel')
const AddEditBookmarkHanger = require('../bookmarks/addEditBookmarkHanger')
+const AddEditBookmarkFolder = require('../bookmarks/addEditBookmarkFolder')
const LoginRequired = require('./loginRequired')
const ReleaseNotes = require('./releaseNotes')
const BookmarksToolbar = require('../bookmarks/bookmarksToolbar')
@@ -534,21 +535,22 @@ class Main extends React.Component {
props.isFullScreen = activeFrame.get('isFullScreen', false)
props.isMaximized = isMaximized() || isFullScreen()
props.captionButtonsVisible = isWindows
- props.showContextMenu = !!currentWindow.get('contextMenuDetail')
- props.showPopupWindow = !!currentWindow.get('popupWindowDetail')
+ props.showContextMenu = currentWindow.has('contextMenuDetail')
+ props.showPopupWindow = currentWindow.has('popupWindowDetail')
props.showSiteInfo = currentWindow.getIn(['ui', 'siteInfo', 'isVisible']) &&
!isSourceAboutUrl(activeFrame.get('location'))
props.showBravery = shieldState.braveShieldsEnabled(activeFrame) &&
!!currentWindow.get('braveryPanelDetail')
- props.showClearData = !!currentWindow.getIn(['ui', 'isClearBrowsingDataPanelVisible'])
- props.showImportData = !!currentWindow.get('importBrowserDataDetail')
+ props.showClearData = currentWindow.hasIn(['ui', 'isClearBrowsingDataPanelVisible'])
+ props.showImportData = currentWindow.has('importBrowserDataDetail')
props.showWidevine = currentWindow.getIn(['widevinePanelDetail', 'shown']) && !isLinux
- props.showAutoFillAddress = !!currentWindow.get('autofillAddressDetail')
- props.showAutoFillCC = !!currentWindow.get('autofillCreditCardDetail')
+ props.showAutoFillAddress = currentWindow.has('autofillAddressDetail')
+ props.showAutoFillCC = currentWindow.has('autofillCreditCardDetail')
props.showLogin = !!loginRequiredDetails
- props.showBookmarkHanger = currentWindow.get('bookmarkDetail') &&
+ props.showBookmarkHanger = currentWindow.has('bookmarkDetail') &&
!currentWindow.getIn(['bookmarkDetail', 'isBookmarkHanger'])
- props.showNoScript = currentWindow.getIn(['ui', 'noScriptInfo', 'isVisible']) &&
+ props.showBookmarkFolderDialog = currentWindow.has('bookmarkFolderDetail')
+ props.showNoScript = currentWindow.hasIn(['ui', 'noScriptInfo', 'isVisible']) &&
siteUtil.getOrigin(activeFrame.get('location'))
props.showReleaseNotes = currentWindow.getIn(['ui', 'releaseNotes', 'isVisible'])
props.showCheckDefault = isFocused() && defaultBrowserState.shouldDisplayDialog(state)
@@ -657,6 +659,11 @@ class Main extends React.Component {
?
: null
}
+ {
+ this.props.showBookmarkFolderDialog
+ ?
+ : null
+ }
{
this.props.showNoScript
?
diff --git a/app/renderer/components/navigation/urlBar.js b/app/renderer/components/navigation/urlBar.js
index 8c8c2bc5b68..93d674942c0 100644
--- a/app/renderer/components/navigation/urlBar.js
+++ b/app/renderer/components/navigation/urlBar.js
@@ -167,7 +167,8 @@ class UrlBar extends React.Component {
if (e.shiftKey) {
const selectedIndex = this.props.urlbarLocationSuffix.length > 0 ? 1 : this.props.selectedIndex
if (selectedIndex !== undefined) {
- appActions.removeSite({ location: this.props.suggestionLocation })
+ // TODO double check if this is correct
+ appActions.removeHistorySite({ location: this.props.suggestionLocation })
}
} else {
this.hideAutoComplete()
diff --git a/app/renderer/components/tabs/content/closeTabIcon.js b/app/renderer/components/tabs/content/closeTabIcon.js
index 577350c99cc..34bc48fa5bb 100644
--- a/app/renderer/components/tabs/content/closeTabIcon.js
+++ b/app/renderer/components/tabs/content/closeTabIcon.js
@@ -50,8 +50,9 @@ class CloseTabIcon extends React.Component {
mergeProps (state, ownProps) {
const currentWindow = state.get('currentWindow')
const frameKey = ownProps.frameKey
- const isPinnedTab = frameStateUtil.isPinned(currentWindow, frameKey)
const frame = frameStateUtil.getFrameByKey(currentWindow, frameKey) || Immutable.Map()
+ const tabId = frame.get('tabId', tabState.TAB_ID_NONE)
+ const isPinnedTab = tabState.isTabPinned(state, tabId)
const props = {}
// used in renderer
@@ -64,7 +65,7 @@ class CloseTabIcon extends React.Component {
// used in functions
props.frameKey = frameKey
props.fixTabWidth = ownProps.fixTabWidth
- props.tabId = frame.get('tabId', tabState.TAB_ID_NONE)
+ props.tabId = tabId
props.hasFrame = !frame.isEmpty()
return props
diff --git a/app/renderer/components/tabs/content/favIcon.js b/app/renderer/components/tabs/content/favIcon.js
index 354088de059..181300b4fb7 100644
--- a/app/renderer/components/tabs/content/favIcon.js
+++ b/app/renderer/components/tabs/content/favIcon.js
@@ -12,6 +12,7 @@ const TabIcon = require('./tabIcon')
// State
const tabContentState = require('../../../../common/state/tabContentState')
+const tabState = require('../../../../common/state/tabState')
// Utils
const frameStateUtil = require('../../../../../js/state/frameStateUtil')
@@ -34,12 +35,13 @@ class Favicon extends React.Component {
const frameKey = ownProps.frameKey
const frame = frameStateUtil.getFrameByKey(currentWindow, frameKey) || Immutable.Map()
const isTabLoading = tabContentState.isTabLoading(currentWindow, frameKey)
+ const tabId = frame.get('tabId', tabState.TAB_ID_NONE)
const props = {}
// used in renderer
props.isTabLoading = isTabLoading
props.favicon = !isTabLoading && frame.get('icon')
- props.isPinnedTab = frameStateUtil.isPinned(currentWindow, frameKey)
+ props.isPinnedTab = tabState.isTabPinned(state, tabId)
props.tabIconColor = tabContentState.getTabIconColor(currentWindow, frameKey)
props.isNarrowestView = tabContentState.isNarrowestView(currentWindow, frameKey)
diff --git a/app/renderer/components/tabs/pinnedTabs.js b/app/renderer/components/tabs/pinnedTabs.js
index 5615a69b1b5..4a9cc6b81b4 100644
--- a/app/renderer/components/tabs/pinnedTabs.js
+++ b/app/renderer/components/tabs/pinnedTabs.js
@@ -18,7 +18,6 @@ const windowActions = require('../../../../js/actions/windowActions')
const windowStore = require('../../../../js/stores/windowStore')
// Constants
-const siteTags = require('../../../../js/constants/siteTags')
const dragTypes = require('../../../../js/constants/dragTypes')
// Utils
@@ -26,6 +25,7 @@ const siteUtil = require('../../../../js/state/siteUtil')
const dnd = require('../../../../js/dnd')
const dndData = require('../../../../js/dndData')
const frameStateUtil = require('../../../../js/state/frameStateUtil')
+const pinnedSitesUtil = require('../../../common/lib/pinnedSitesUtil')
const {isIntermediateAboutPage} = require('../../../../js/lib/appUrlUtil')
class PinnedTabs extends React.Component {
@@ -58,12 +58,14 @@ class PinnedTabs extends React.Component {
if (!sourceDragData.get('pinnedLocation')) {
appActions.tabPinned(sourceDragData.get('tabId'), true)
} else {
- const sourceDetails = siteUtil.getDetailFromFrame(sourceDragData, siteTags.PINNED)
+ const sourceDetails = pinnedSitesUtil.getDetailFromFrame(sourceDragData)
const droppedOnFrame = this.dropFrame(droppedOnTab.props.frameKey)
- const destinationDetails = siteUtil.getDetailFromFrame(droppedOnFrame, siteTags.PINNED)
- appActions.moveSite(siteUtil.getSiteKey(sourceDetails),
+ const destinationDetails = pinnedSitesUtil.getDetailFromFrame(droppedOnFrame)
+ appActions.onPinnedTabReorder(
+ siteUtil.getSiteKey(sourceDetails),
siteUtil.getSiteKey(destinationDetails),
- isLeftSide)
+ isLeftSide
+ )
}
}
}, 0)
diff --git a/app/renderer/components/tabs/tab.js b/app/renderer/components/tabs/tab.js
index ebac3a874b5..d7a85e975c6 100644
--- a/app/renderer/components/tabs/tab.js
+++ b/app/renderer/components/tabs/tab.js
@@ -225,6 +225,7 @@ class Tab extends React.Component {
const partition = typeof frame.get('partitionNumber') === 'string'
? frame.get('partitionNumber').replace(/^partition-/i, '')
: frame.get('partitionNumber')
+ const tabId = frame.get('tabId', tabState.TAB_ID_NONE)
const props = {}
// used in renderer
@@ -234,7 +235,7 @@ class Tab extends React.Component {
props.notificationBarActive = notificationBarActive
props.isActive = frameStateUtil.isFrameKeyActive(currentWindow, props.frameKey)
props.tabWidth = currentWindow.getIn(['ui', 'tabs', 'fixTabWidth'])
- props.isPinnedTab = frameStateUtil.isPinned(currentWindow, props.frameKey)
+ props.isPinnedTab = tabState.isTabPinned(state, tabId)
props.canPlayAudio = tabContentState.canPlayAudio(currentWindow, props.frameKey)
props.themeColor = tabContentState.getThemeColor(currentWindow, props.frameKey)
props.isNarrowView = tabContentState.isNarrowView(currentWindow, props.frameKey)
@@ -256,7 +257,7 @@ class Tab extends React.Component {
props.totalTabs = state.get('tabs').size
props.dragData = state.getIn(['dragData', 'type']) === dragTypes.TAB && state.get('dragData')
props.hasTabInFullScreen = tabContentState.hasTabInFullScreen(currentWindow)
- props.tabId = frame.get('tabId', tabState.TAB_ID_NONE)
+ props.tabId = tabId
return props
}
diff --git a/app/renderer/reducers/contextMenuReducer.js b/app/renderer/reducers/contextMenuReducer.js
index 36f3795b6a6..47528fc0331 100644
--- a/app/renderer/reducers/contextMenuReducer.js
+++ b/app/renderer/reducers/contextMenuReducer.js
@@ -237,18 +237,18 @@ const addFolderMenuItem = (closestDestinationDetail, isParent) => {
return {
label: locale.translation('addFolder'),
click: () => {
- let siteDetail = Immutable.fromJS({ tags: [siteTags.BOOKMARK_FOLDER] })
let closestKey = null
+ let folderDetails = Immutable.Map()
if (closestDestinationDetail) {
closestKey = siteUtil.getSiteKey(closestDestinationDetail)
if (isParent) {
- siteDetail = siteDetail.set('parentFolderId', (closestDestinationDetail.get('folderId') || closestDestinationDetail.get('parentFolderId')))
+ folderDetails = folderDetails.set('parentFolderId', (closestDestinationDetail.get('folderId') || closestDestinationDetail.get('parentFolderId')))
}
}
- windowActions.addBookmark(siteDetail, closestKey)
+ windowActions.addBookmarkFolder(folderDetails, closestKey)
}
}
}
@@ -273,6 +273,7 @@ const openAllInNewTabsMenuItem = (folderDetail) => {
}
}
+// TODO refactor it
const siteDetailTemplateInit = (state, siteKey) => {
let isHistoryEntry = false
let multipleHistoryEntries = false
@@ -359,21 +360,24 @@ const siteDetailTemplateInit = (state, siteKey) => {
label: locale.translation(isFolder ? 'editFolder' : 'editBookmark'),
click: () => {
const editKey = siteUtil.getSiteKey(siteDetail)
+ // TODO use correct action
windowActions.editBookmark(false, editKey)
}
},
CommonMenu.separatorMenuItem)
}
+ // TODO sync with contexMenu file
template.push(
{
label: locale.translation(deleteLabel),
click: () => {
- if (Immutable.List.isList(siteDetail)) {
+ console.log(deleteTag)
+ /* if (Immutable.List.isList(siteDetail)) {
siteDetail.forEach((site) => appActions.removeSite(site, deleteTag))
} else {
appActions.removeSite(siteDetail, deleteTag)
- }
+ } */
}
})
}
@@ -383,7 +387,7 @@ const siteDetailTemplateInit = (state, siteKey) => {
template.push(CommonMenu.separatorMenuItem)
}
template.push(
- addBookmarkMenuItem('addBookmark', siteUtil.getDetailFromFrame(activeFrame, siteTags.BOOKMARK), siteDetail, true),
+ addBookmarkMenuItem('addBookmark', bookmarkUtil.getDetailFromFrame(activeFrame), siteDetail, true),
addFolderMenuItem(siteDetail, true))
}
@@ -400,11 +404,10 @@ const onSiteDetailMenu = (state, siteKey) => {
return state
}
+// TODO refactor
const showBookmarkFolderInit = (state, parentBookmarkFolderKey) => {
const appState = appStoreRenderer.state
- const allBookmarkItems = siteUtil.getBookmarks(appState.get('sites', Immutable.List()))
- const parentBookmarkFolder = appState.getIn(['sites', parentBookmarkFolderKey])
- const items = siteUtil.filterSitesRelativeTo(allBookmarkItems, parentBookmarkFolder)
+ const items = bookmarkUtil.getBookmarksByParentId(appState, parentBookmarkFolderKey)
if (items.size === 0) {
return [{
l10nLabelId: 'emptyFolderItem',
@@ -426,6 +429,7 @@ const showBookmarkFolderInit = (state, parentBookmarkFolderKey) => {
return bookmarkItemsInit(state, items)
}
+// TODO refactor
const bookmarkItemsInit = (state, items) => {
const activeFrame = frameStateUtil.getActiveFrame(state) || Immutable.Map()
const showFavicon = bookmarkUtil.showFavicon()
@@ -441,7 +445,7 @@ const bookmarkItemsInit = (state, items) => {
const templateItem = {
bookmark: site,
draggable: true,
- label: site.get('customTitle', site.get('title', site.get('location'))),
+ label: site.get('title', site.get('location')),
icon: showFavicon ? site.get('favicon') : undefined,
faIcon,
contextMenu: function () {
@@ -453,7 +457,7 @@ const bookmarkItemsInit = (state, items) => {
dragStart: function (e) {
dnd.onDragStart(dragTypes.BOOKMARK, Immutable.fromJS({
location: site.get('location'),
- title: site.get('customTitle', site.get('title')),
+ title: site.get('title'),
bookmarkKey: siteKey
}), e)
},
@@ -481,6 +485,7 @@ const bookmarkItemsInit = (state, items) => {
return menuUtil.sanitizeTemplateItems(template)
}
+// TODO refactor
const onMoreBookmarksMenu = (state, action) => {
action = validateAction(action)
state = validateState(state)
diff --git a/app/renderer/reducers/frameReducer.js b/app/renderer/reducers/frameReducer.js
index cfe38fcc3d6..a10fac339eb 100644
--- a/app/renderer/reducers/frameReducer.js
+++ b/app/renderer/reducers/frameReducer.js
@@ -10,7 +10,6 @@ const Immutable = require('immutable')
const appConstants = require('../../../js/constants/appConstants')
const windowConstants = require('../../../js/constants/windowConstants')
const config = require('../../../js/constants/config')
-const siteTags = require('../../../js/constants/siteTags')
// Actions
const appActions = require('../../../js/actions/appActions')
@@ -20,7 +19,7 @@ const frameStateUtil = require('../../../js/state/frameStateUtil')
const {getCurrentWindowId} = require('../currentWindow')
const {getSourceAboutUrl, getSourceMagnetUrl} = require('../../../js/lib/appUrlUtil')
const {isURL, isPotentialPhishingUrl, getUrlFromInput} = require('../../../js/lib/urlutil')
-const siteUtil = require('../../../js/state/siteUtil')
+const bookmarkUtil = require('../../common/lib/bookmarkUtil')
const setFullScreen = (state, action) => {
const index = frameStateUtil.getIndexByTabId(state, action.tabId)
@@ -229,8 +228,8 @@ const frameReducer = (state, action, immutableAction) => {
// TODO make this an appAction that gets the bookmark data from tabState
const frameProps = frameStateUtil.getFrameByTabId(state, action.tabId)
if (frameProps) {
- const bookmark = siteUtil.getDetailFromFrame(frameProps, siteTags.BOOKMARK)
- appActions.addSite(bookmark, siteTags.BOOKMARK)
+ const bookmark = bookmarkUtil.getDetailFromFrame(frameProps)
+ appActions.addBookmark(bookmark)
}
break
}
diff --git a/app/sessionStore.js b/app/sessionStore.js
index 40307409ab6..fda23dccbf5 100644
--- a/app/sessionStore.js
+++ b/app/sessionStore.js
@@ -21,34 +21,20 @@ const UpdateStatus = require('../js/constants/updateStatus')
const settings = require('../js/constants/settings')
const downloadStates = require('../js/constants/downloadStates')
const siteUtil = require('../js/state/siteUtil')
-const { topSites, pinnedTopSites } = require('../js/data/newTabData')
-const { defaultSiteSettingsList } = require('../js/data/siteSettingsList')
+const {pinnedTopSites} = require('../js/data/newTabData')
+const {defaultSiteSettingsList} = require('../js/data/siteSettingsList')
const sessionStorageVersion = 1
const filtering = require('./filtering')
const autofill = require('./autofill')
const {navigatableTypes} = require('../js/lib/appUrlUtil')
const Channel = require('./channel')
-const { makeImmutable } = require('./common/state/immutableUtil')
+const {makeImmutable} = require('./common/state/immutableUtil')
const tabState = require('./common/state/tabState')
const windowState = require('./common/state/windowState')
const getSetting = require('../js/settings').getSetting
const sessionStorageName = `session-store-${sessionStorageVersion}`
-const getTopSiteMap = () => {
- if (Array.isArray(topSites) && topSites.length) {
- let siteMap = {}
- let order = 0
- topSites.forEach((site) => {
- let key = siteUtil.getSiteKey(Immutable.fromJS(site))
- site.order = order++
- siteMap[key] = site
- })
- return siteMap
- }
- return {}
-}
-
const getTempStoragePath = (filename) => {
const epochTimestamp = (new Date()).getTime().toString()
filename = filename || 'tmp'
@@ -327,7 +313,7 @@ module.exports.cleanAppData = (data, isShutdown) => {
if (data.sites) {
const clearHistory = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_HISTORY) === true
if (clearHistory) {
- data.sites = siteUtil.clearHistory(Immutable.fromJS(data.sites)).toJS()
+ data.historySites = {}
if (data.about) {
delete data.about.history
delete data.about.newtab
@@ -496,6 +482,105 @@ module.exports.runPreMigrations = (data) => {
}
}
+ // TODO remove this second level additional checks
+ if (data.sites) {
+ // pinned sites
+ if (!data.pinnedSites) {
+ data.pinnedSites = {}
+ for (let key of Object.keys(data.sites)) {
+ const site = data.sites[key]
+ if (site.tags && site.tags.includes('pinned')) {
+ delete site.tags
+ data.pinnedSites[key] = site
+ }
+ }
+ }
+
+ // default sites
+ let newTab = data.about.newtab
+
+ // TODO remove this pinnedSites check
+ if (newTab && !data.pinnedSites) {
+ const ignoredSites = []
+ const pinnedSites = []
+
+ if (newTab.ignoredTopSites) {
+ for (let site of newTab.ignoredTopSites) {
+ if (site) {
+ ignoredSites.push(`${site.location}|0|0`)
+ }
+ }
+ data.about.newtab.ignoredTopSites = ignoredSites
+ }
+
+ if (newTab.pinnedTopSites) {
+ for (let site of newTab.pinnedTopSites) {
+ if (site) {
+ site.key = `${site.location}|0|0`
+ pinnedSites.push(site)
+ }
+ }
+ data.about.newtab.pinnedTopSites = pinnedSites
+ }
+
+ data.about.newtab.sites = []
+ }
+
+ // bookmark folders
+ if (!data.bookmarkFolders) {
+ data.bookmarkFolders = {}
+
+ for (let key of Object.keys(data.sites)) {
+ const folder = data.sites[key]
+ if (folder.tags && folder.tags.includes('bookmark-folder')) {
+ delete folder.tags
+
+ if (folder.customTitle) {
+ folder.title = folder.customTitle
+ delete folder.customTitle
+ }
+
+ data.bookmarkFolders[key] = folder
+ }
+ }
+ }
+
+ // bookmarks
+ if (!data.bookmarks) {
+ data.bookmarks = {}
+
+ for (let key of Object.keys(data.sites)) {
+ const bookmark = data.sites[key]
+ if (bookmark.tags && bookmark.tags.includes('bookmark')) {
+ delete bookmark.tags
+
+ if (bookmark.customTitle) {
+ bookmark.title = bookmark.customTitle
+ delete bookmark.customTitle
+ }
+
+ data.bookmarks[key] = bookmark
+ }
+ }
+ }
+ // TODO sort newly added bookmarks and folders
+
+ // history
+ if (!data.historySites) {
+ data.historySites = {}
+
+ for (let key of Object.keys(data.sites)) {
+ const site = data.sites[key]
+ if (site.lastAccessedTime || !site.tags || site.tags.length === 0) {
+ data.historySites[key] = site
+ }
+ }
+ }
+
+ // TODO enable this delete when everything is done
+ // delete data.sites
+ }
+
return data
}
@@ -632,7 +717,11 @@ module.exports.defaultAppState = () => {
lastConfirmedRecordTimestamp: 0
},
locationSiteKeysCache: undefined,
- sites: getTopSiteMap(),
+ sites: {},
+ pinnedSites: {},
+ bookmarks: {},
+ bookmarkFolders: {},
+ historySites: {},
tabs: [],
windows: [],
extensions: {},
@@ -656,7 +745,7 @@ module.exports.defaultAppState = () => {
about: {
newtab: {
gridLayoutSize: 'small',
- sites: topSites,
+ sites: [],
ignoredTopSites: [],
pinnedTopSites: pinnedTopSites
},
diff --git a/app/sync.js b/app/sync.js
index 05122a0fc51..f5ebcc05c78 100644
--- a/app/sync.js
+++ b/app/sync.js
@@ -19,13 +19,14 @@ const appActions = require('../js/actions/appActions')
const syncConstants = require('../js/constants/syncConstants')
const appDispatcher = require('../js/dispatcher/appDispatcher')
const AppStore = require('../js/stores/appStore')
-const siteUtil = require('../js/state/siteUtil')
const syncUtil = require('../js/state/syncUtil')
const syncPendState = require('./common/state/syncPendState')
const getSetting = require('../js/settings').getSetting
const settings = require('../js/constants/settings')
const extensions = require('./extensions')
const {unescapeJSONPointer} = require('./common/lib/jsonUtil')
+const bookmarkFoldersState = require('./common/state/bookmarkFoldersState')
+const bookmarksState = require('./common/state/bookmarksState')
const CATEGORY_MAP = syncUtil.CATEGORY_MAP
const CATEGORY_NAMES = Object.keys(categories)
@@ -33,7 +34,7 @@ const SYNC_ACTIONS = Object.values(syncConstants)
// Fields that should trigger a sync SEND when changed
const SYNC_FIELDS = {
- sites: ['location', 'customTitle', 'folderId', 'parentFolderId', 'tags'],
+ sites: ['location', 'title', 'folderId', 'parentFolderId', 'tags'],
siteSettings: Object.keys(syncUtil.siteSettingDefaults)
}
@@ -260,7 +261,8 @@ module.exports.onSyncReady = (isFirstRun, e) => {
sendSyncRecords(e.sender, writeActions.CREATE, [deviceRecord])
deviceIdSent = true
}
- const sites = appState.get('sites') || new Immutable.List()
+ const bookmarks = bookmarksState.getBookmarks(appState)
+ const bookmarkFolders = bookmarkFoldersState.getFolders(appState)
const seed = appState.get('seed') || new Immutable.List()
/**
@@ -273,38 +275,34 @@ module.exports.onSyncReady = (isFirstRun, e) => {
*/
const folderToObjectId = {}
const bookmarksToSync = []
- const shouldSyncBookmark = (site) => {
- if (!site) {
- return false
- }
- if (siteUtil.isSiteBookmarked(sites, site) !== true && siteUtil.isFolder(site) !== true) {
- return false
- }
+
+ const shouldSyncBookmark = (bookmark) => {
// originalSeed is set on reset to prevent synced bookmarks on a device
// from being re-synced.
- const originalSeed = site.get('originalSeed')
- if (site.get('objectId') && (!originalSeed || seed.equals(originalSeed))) {
+ const originalSeed = bookmark.get('originalSeed')
+ if (bookmark.get('objectId') && (!originalSeed || seed.equals(originalSeed))) {
return false
}
- if (folderToObjectId[site.get('folderId')]) { return false }
- return syncUtil.isSyncable('bookmark', site)
+
+ return !folderToObjectId[bookmark.get('folderId')]
}
- const syncBookmark = (site) => {
- const siteJS = site.toJS()
- const parentFolderId = site.get('parentFolderId')
+ const syncBookmark = (bookmark) => {
+ const bookmarkJS = bookmark.toJS()
+
+ const parentFolderId = bookmark.get('parentFolderId')
if (typeof parentFolderId === 'number') {
if (!folderToObjectId[parentFolderId]) {
- const folderResult = siteUtil.getFolder(sites, parentFolderId)
+ const folderResult = bookmarkFolders.get(parentFolderId)
if (folderResult) {
syncBookmark(folderResult[1])
}
}
- siteJS.parentFolderObjectId = folderToObjectId[parentFolderId]
+ bookmarkJS.parentFolderObjectId = folderToObjectId[parentFolderId]
}
- const record = syncUtil.createSiteData(siteJS, appState)
- const folderId = site.get('folderId')
+ const record = syncUtil.createSiteData(bookmarkJS, appState)
+ const folderId = bookmark.get('folderId')
if (typeof folderId === 'number') {
folderToObjectId[folderId] = record.objectId
}
@@ -313,12 +311,16 @@ module.exports.onSyncReady = (isFirstRun, e) => {
}
// Sync bookmarks that have not been synced yet.
- sites.forEach((site) => {
- if (shouldSyncBookmark(site) !== true) {
+ bookmarks.forEach((bookmark) => {
+ if (shouldSyncBookmark(bookmark) !== true) {
return
}
- syncBookmark(site)
+
+ syncBookmark(bookmark)
})
+
+ // TODO add bookamrkFolder sync as well
+
sendSyncRecords(e.sender, writeActions.CREATE, bookmarksToSync)
// Sync site settings that have not been synced yet
@@ -490,7 +492,7 @@ module.exports.init = function (appState) {
}
if (!bookmarksToolbarShown && isFirstRun) {
// syncing for the first time
- const bookmarks = siteUtil.getBookmarks(AppStore.getState().get('sites'))
+ const bookmarks = bookmarksState.getBookmarks(AppStore.getState())
if (!bookmarks.size) {
for (const record of records) {
if (record && record.objectData === 'bookmark') {
diff --git a/docs/state.md b/docs/state.md
index cf2a9e3bd44..7c77232c4ac 100644
--- a/docs/state.md
+++ b/docs/state.md
@@ -53,6 +53,28 @@ AppStore
timestamp: number
}
},
+ bookmarks: {
+ [bookmarkKey]: {
+ favicon: string, // URL of the favicon
+ location: string,
+ objectId: Array.,
+ originalSeed: Array., // bookmarks that have been synced before a sync profile reset
+ parentFolderId: number,
+ partitionNumber: number, // optionally specifies a specific session
+ skipSync: boolean,
+ title: string
+ }
+ },
+ bookmarkFolders: {
+ [folderKey]: {
+ folderId: number,
+ objectId: Array.,
+ originalSeed: Array., // only set for bookmarks that have been synced before a sync profile reset
+ parentFolderId: number, // set for bookmarks and bookmark folders only
+ skipSync: boolean, // Set for objects FETCHed by sync
+ title: string
+ }
+ },
clearBrowsingDataDefaults: {
allSiteCookies: boolean,
autocompleteData: boolean,
@@ -118,6 +140,18 @@ AppStore
flash: {
enabled: boolean // enable flash
},
+ historySites: {
+ [siteKey]: {
+ favicon: string, // URL of the favicon
+ lastAccessedTime: number, // datetime.getTime()
+ location: string,
+ objectId: Array.,
+ partitionNumber: number, // optionally specifies a specific session
+ skipSync: boolean, // Set for objects FETCHed by sync
+ title: string,
+ themeColor: string
+ }
+ },
menu: {
template: object // used on Windows and by our tests: template object with Menubar control
},
@@ -156,6 +190,13 @@ AppStore
origin: string, // origin of the form
username: string
}],
+ pinnedSites: {
+ [siteKey]: {
+ location: string,
+ title: string,
+ order: number
+ }
+ },
settings: {
// See defaults in js/constants/appConfig.js
'adblock.customRules': string, // custom rules in ABP filter syntax
@@ -230,24 +271,6 @@ AppStore
'tabs.switch-to-new-tabs': boolean, // true if newly opened tabs should be focused immediately
'tabs.tabs-per-page': number // number of tabs per tab page
},
- sites: {
- [siteKey]: {
- creationTime: number, //creation time of bookmark
- customTitle: string, // User provided title for bookmark; overrides title
- favicon: string, // URL of the favicon
- folderId: number, // set for bookmark folders only
- lastAccessedTime: number, // datetime.getTime()
- location: string,
- objectId: Array.,
- originalSeed: Array., // only set for bookmarks that have been synced before a sync profile reset
- parentFolderId: number, // set for bookmarks and bookmark folders only
- partitionNumber: number, // optionally specifies a specific session
- skipSync: boolean, // Set for objects FETCHed by sync
- tags: [string], // empty, 'bookmark', 'bookmark-folder', 'default', or 'reader'
- themeColor: string, // CSS compatible color string
- title: string
- } // folder: folderId; bookmark/history: location + partitionNumber + parentFolderId
- },
locationSiteKeyCache: {
[location]: Array. // location -> site keys
},
diff --git a/js/about/newtab.js b/js/about/newtab.js
index e9a2b83010b..a3be7ff39d9 100644
--- a/js/about/newtab.js
+++ b/js/about/newtab.js
@@ -5,26 +5,36 @@
const path = require('path')
const React = require('react')
const Immutable = require('immutable')
-const messages = require('../constants/messages')
-const HTML5Backend = require('react-dnd-html5-backend')
const {DragDropContext} = require('react-dnd')
+const HTML5Backend = require('react-dnd-html5-backend')
+
+// Components
const Stats = require('./newTabComponents/stats')
const Clock = require('./newTabComponents/clock')
const Block = require('./newTabComponents/block')
const SiteRemovalNotification = require('./newTabComponents/siteRemovalNotification')
const FooterInfo = require('./newTabComponents/footerInfo')
+const NewPrivateTab = require('./newprivatetab')
+
+// Constants
+const messages = require('../constants/messages')
+const config = require('../constants/config')
+
+// Actions
const aboutActions = require('./aboutActions')
+const windowActions = require('../actions/windowActions')
+
+// Data
+const backgrounds = require('../data/backgrounds')
+const newTabData = require('../data/newTabData')
+
+// Utils
const siteUtil = require('../state/siteUtil')
const urlutils = require('../lib/urlutil')
-const siteTags = require('../constants/siteTags')
-const config = require('../constants/config')
-const backgrounds = require('../data/backgrounds')
const {random} = require('../../app/common/lib/randomUtil')
-const NewPrivateTab = require('./newprivatetab')
-const windowActions = require('../actions/windowActions')
-
const ipc = window.chrome.ipcRenderer
+// Styles
require('../../less/about/newtab.less')
require('../../node_modules/font-awesome/css/font-awesome.css')
@@ -32,15 +42,17 @@ class NewTabPage extends React.Component {
constructor (props) {
super(props)
this.state = {
- showSiteRemovalNotification: false,
+ showNotification: false,
imageLoadFailed: false,
updatedStamp: undefined,
showEmptyPage: true,
showImages: false,
backgroundImage: undefined
}
- ipc.on(messages.NEWTAB_DATA_UPDATED, (e, newTabData) => {
- const data = Immutable.fromJS(newTabData || {})
+ this.staticData = Immutable.fromJS(newTabData.topSites)
+
+ ipc.on(messages.NEWTAB_DATA_UPDATED, (e, newData) => {
+ let data = Immutable.fromJS(newData || {})
const updatedStamp = data.getIn(['newTabDetail', 'updatedStamp'])
// Only update if the data has changed.
@@ -50,6 +62,18 @@ class NewTabPage extends React.Component {
return
}
+ const topSites = data.getIn(['newTabDetail', 'sites'], Immutable.List())
+ if (topSites.size < 18) {
+ const pinned = data.getIn(['newTabDetail', 'pinnedTopSites'], Immutable.List())
+ const ignored = data.getIn(['newTabDetail', 'ignoredTopSites'], Immutable.List())
+ const preDefined = this.staticData
+ .filter((site) => {
+ const key = site.get('key')
+ return !pinned.find(site => site.get('key') === key) && !ignored.includes(key)
+ })
+ data = data.setIn(['newTabDetail', 'sites'], topSites.concat(preDefined))
+ }
+
const showEmptyPage = !!data.get('showEmptyPage')
const showImages = !!data.get('showImages') && !showEmptyPage
this.setState({
@@ -63,83 +87,79 @@ class NewTabPage extends React.Component {
})
})
}
+
get showImages () {
return this.state.showImages && !!this.state.backgroundImage
}
+
get randomBackgroundImage () {
const image = Object.assign({}, backgrounds[Math.floor(random() * backgrounds.length)])
image.style = {backgroundImage: 'url(' + image.source + ')'}
return image
}
+
get fallbackImage () {
const image = Object.assign({}, config.newtab.fallbackImage)
const pathToImage = path.join(__dirname, '..', '..', image.source)
image.style = {backgroundImage: 'url(' + `${pathToImage}` + ')'}
return image
}
+
get topSites () {
- return this.state.newTabData.getIn(['newTabDetail', 'sites']) || Immutable.List()
+ return this.state.newTabData.getIn(['newTabDetail', 'sites'])
}
+
get pinnedTopSites () {
- return (this.state.newTabData.getIn(['newTabDetail', 'pinnedTopSites']) || Immutable.List()).setSize(18)
+ return this.state.newTabData.getIn(['newTabDetail', 'pinnedTopSites'], Immutable.List())
}
+
get ignoredTopSites () {
- return this.state.newTabData.getIn(['newTabDetail', 'ignoredTopSites']) || Immutable.List()
+ return this.state.newTabData.getIn(['newTabDetail', 'ignoredTopSites'], Immutable.List())
}
+
get gridLayoutSize () {
- return this.state.newTabData.getIn(['newTabDetail', 'gridLayoutSize']) || 'small'
+ return this.state.newTabData.getIn(['newTabDetail', 'gridLayoutSize'], 'small')
}
- isPinned (siteProps) {
- return this.pinnedTopSites.filter((site) => {
- if (!site || !site.get) return false
- return site.get('location') === siteProps.get('location') &&
- site.get('partitionNumber') === siteProps.get('partitionNumber')
- }).size > 0
+
+ isPinned (siteKey) {
+ return this.pinnedTopSites.find(site => site.get('key') === siteKey)
}
- isBookmarked (siteProps) {
- return siteUtil.isBookmark(siteProps)
+
+ isBookmarked (siteKey) {
+ // TODO add bookmark status to site that are send here
+ return siteUtil.isBookmark(null)
}
+
get gridLayout () {
const sizeToCount = {large: 18, medium: 12, small: 6}
const count = sizeToCount[this.gridLayoutSize]
- return this.topSites.take(count)
+
+ let sites = this.pinnedTopSites.take(count)
+
+ if (sites.size < count) {
+ sites = sites.concat(this.topSites.take(count - sites.size))
+ }
+
+ return sites
}
- showSiteRemovalNotification () {
+
+ showNotification () {
this.setState({
- showSiteRemovalNotification: true
+ showNotification: true
})
}
+
hideSiteRemovalNotification () {
this.setState({
- showSiteRemovalNotification: false
+ showNotification: false
})
}
- /**
- * save number of rows on store. gridsLayout starts with 3 rows (large).
- * Rows are reduced at each click and then reset to three again
- */
- onChangeGridLayout () {
- const gridLayoutSize = this.gridLayoutSize
- const changeGridSizeTo = (size) => aboutActions.setNewTabDetail({gridLayoutSize: size})
-
- if (gridLayoutSize === 'large') {
- changeGridSizeTo('medium')
- } else if (gridLayoutSize === 'medium') {
- changeGridSizeTo('small')
- } else if (gridLayoutSize === 'small') {
- changeGridSizeTo('large')
- } else {
- changeGridSizeTo('large')
- }
-
- return gridLayoutSize
- }
-
- onDraggedSite (currentId, finalId) {
+ // TODO fix this function
+ onDraggedSite (siteKey, destinationKey) {
let gridSites = this.topSites
- const currentPosition = gridSites.filter((site) => site.get('location') === currentId).get(0)
- const finalPosition = gridSites.filter((site) => site.get('location') === finalId).get(0)
+ const currentPosition = gridSites.get(siteKey)
+ const finalPosition = gridSites.get(destinationKey)
const currentPositionIndex = gridSites.indexOf(currentPosition)
const finalPositionIndex = gridSites.indexOf(finalPosition)
@@ -154,63 +174,59 @@ class NewTabPage extends React.Component {
pinnedTopSites = pinnedTopSites.splice(finalPositionIndex, 0, currentPosition)
// If site is pinned, update pinnedTopSites list
- const newTabState = {}
+ const newTabState = Immutable.Map()
if (this.isPinned(currentPosition)) {
- newTabState.pinnedTopSites = pinnedTopSites
+ newTabState.set('pinnedTopSites', pinnedTopSites)
}
- newTabState.sites = gridSites
+ newTabState.set('sites', gridSites)
// Only update if there was an actual change
- const stateDiff = Immutable.fromJS(newTabState)
const existingState = this.state.newTabData || Immutable.fromJS({})
- const proposedState = existingState.mergeIn(['newTabDetail'], stateDiff)
+ const proposedState = existingState.mergeIn(['newTabDetail'], newTabState)
if (!proposedState.isSubset(existingState)) {
- aboutActions.setNewTabDetail(stateDiff)
+ aboutActions.setNewTabDetail(newTabState)
}
}
- onToggleBookmark (siteProps) {
- const siteDetail = siteUtil.getDetailFromFrame(siteProps, siteTags.BOOKMARK)
- const editing = this.isBookmarked(siteProps)
- const key = siteUtil.getSiteKey(siteDetail)
+ onToggleBookmark (siteKey) {
+ const editing = this.isBookmarked(siteKey)
if (editing) {
- windowActions.editBookmark(false, key)
+ windowActions.editBookmark(false, siteKey)
} else {
- windowActions.onBookmarkAdded(false, key)
+ windowActions.onBookmarkAdded(false, siteKey)
}
}
- onPinnedTopSite (siteProps) {
- const currentPosition = this.topSites.filter((site) => siteProps.get('location') === site.get('location')).get(0)
- const currentPositionIndex = this.topSites.indexOf(currentPosition)
+ onPinnedTopSite (siteKey) {
+ let pinnedTopSites = this.pinnedTopSites
+ const siteProps = this.topSites.find(site => site.get('key') === siteKey)
- // If pinned, leave it null. Otherwise stores site on ignoredTopSites list, retaining the same position
- let pinnedTopSites = this.pinnedTopSites.splice(currentPositionIndex, 1, this.isPinned(siteProps) ? null : siteProps)
+ if (this.isPinned(siteKey)) {
+ pinnedTopSites = pinnedTopSites.filter(site => site.get('key') !== siteKey)
+ } else {
+ pinnedTopSites = pinnedTopSites.push(siteProps)
+ }
aboutActions.setNewTabDetail({pinnedTopSites: pinnedTopSites})
}
- onIgnoredTopSite (siteProps) {
- this.showSiteRemovalNotification()
+ onIgnoredTopSite (siteKey) {
+ this.showNotification(siteKey)
// If a pinnedTopSite is ignored, remove it from the pinned list as well
const newTabState = {}
- if (this.isPinned(siteProps)) {
- const gridSites = this.topSites
- const currentPosition = gridSites.filter((site) => siteProps.get('location') === site.get('location')).get(0)
- const currentPositionIndex = gridSites.indexOf(currentPosition)
- const pinnedTopSites = this.pinnedTopSites.splice(currentPositionIndex, 1, null)
- newTabState.pinnedTopSites = pinnedTopSites
+ if (this.isPinned(siteKey)) {
+ newTabState.pinnedTopSites = this.pinnedTopSites.filter(site => site.get('key') !== siteKey)
}
- newTabState.ignoredTopSites = this.ignoredTopSites.push(siteProps)
+ newTabState.ignoredTopSites = this.ignoredTopSites.push(siteKey)
aboutActions.setNewTabDetail(newTabState, true)
}
onUndoIgnoredTopSite () {
// Remove last List's entry
- const ignoredTopSites = this.ignoredTopSites.splice(-1, 1)
+ const ignoredTopSites = this.ignoredTopSites.pop()
aboutActions.setNewTabDetail({ignoredTopSites: ignoredTopSites}, true)
this.hideSiteRemovalNotification()
}
@@ -236,6 +252,12 @@ class NewTabPage extends React.Component {
})
}
+ getLetterFromUrl (url) {
+ const hostname = urlutils.getHostname(url.get('location'), true)
+ const name = url.get('title') || hostname || '?'
+ return name.charAt(0).toUpperCase()
+ }
+
render () {
// don't render if user prefers an empty page
if (this.state.showEmptyPage && !this.props.isIncognito) {
@@ -250,11 +272,6 @@ class NewTabPage extends React.Component {
if (!this.state.newTabData) {
return null
}
- const getLetterFromUrl = (url) => {
- const hostname = urlutils.getHostname(url.get('location'), true)
- const name = url.get('title') || hostname || '?'
- return name.charAt(0).toUpperCase()
- }
const gridLayout = this.gridLayout
const backgroundProps = {}
let gradientClassName = 'gradient'
@@ -278,24 +295,24 @@ class NewTabPage extends React.Component {
{
- this.state.showSiteRemovalNotification
+ this.state.showNotification
? (bookmark.get('parentFolderId') || 0) === (folderDetail.get('folderId') || 0) && siteUtil.isBookmark(bookmark))
+ const bookmarks = bookmarksUtil.getBookmarksByParentId(appStoreRenderer.state, folderDetail.get('folderId'))
// Only load the first 25 tabs as loaded
bookmarks
.forEach((bookmark, i) => {
if (i <= 25) {
appActions.createTabRequested(
- Object.assign(siteUtil.toCreateProperties(bookmark), {
+ Object.assign(bookmarksUtil.toCreateProperties(bookmark), {
active: false
}), getSetting(SWITCH_TO_NEW_TABS))
} else {
diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js
index 9f8c5f73149..0845201cd2b 100644
--- a/js/actions/windowActions.js
+++ b/js/actions/windowActions.js
@@ -530,6 +530,38 @@ const windowActions = {
})
},
+ /**
+ * Used for displaying bookmark folder dialog
+ * when adding bookmark site or folder
+ */
+ addBookmarkFolder: function (folderDetails, closestKey) {
+ dispatch({
+ actionType: windowConstants.WINDOW_ON_ADD_BOOKMARK_FOLDER,
+ folderDetails,
+ closestKey
+ })
+ },
+
+ /**
+ * Used for displaying bookmark folder dialog
+ * when editing bookmark site or folder
+ */
+ editBookmarkFolder: function (editKey) {
+ dispatch({
+ actionType: windowConstants.WINDOW_ON_EDIT_BOOKMARK_FOLDER,
+ editKey
+ })
+ },
+
+ /**
+ * Used for closing a bookmark dialog
+ */
+ onBookmarkFolderClose: function () {
+ dispatch({
+ actionType: windowConstants.WINDOW_ON_BOOKMARK_FOLDER_CLOSE
+ })
+ },
+
/**
* Dispatches a message to set context menu detail.
* If set, also indicates that the context menu is shown.
diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js
index f8e12793899..a69f54873d5 100644
--- a/js/constants/appConstants.js
+++ b/js/constants/appConstants.js
@@ -13,9 +13,9 @@ const appConstants = {
APP_WINDOW_UPDATED: _,
APP_TAB_CLOSED: _,
APP_TAB_UPDATED: _,
- APP_ADD_SITE: _,
+ APP_ADD_HISTORY_SITE: _,
APP_SET_STATE: _,
- APP_REMOVE_SITE: _,
+ APP_REMOVE_HISTORY_SITE: _,
APP_MOVE_SITE: _,
APP_APPLY_SITE_RECORDS: _,
APP_MERGE_DOWNLOAD_DETAIL: _, // Sets an individual download detail
@@ -144,7 +144,14 @@ const appConstants = {
APP_SPELLING_SUGGESTED: _,
APP_LEARN_SPELLING: _,
APP_FORGET_LEARNED_SPELLING: _,
- APP_SET_VERSION_INFO: _
+ APP_SET_VERSION_INFO: _,
+ APP_ON_PINNED_TAB_REORDER: _,
+ APP_ADD_BOOKMARK_FOLDER: _,
+ APP_EDIT_BOOKMARK_FOLDER: _,
+ APP_REMOVE_BOOKMARK_FOLDER: _,
+ APP_REMOVE_BOOKMARK: _,
+ APP_ADD_BOOKMARKS: _,
+ APP_ADD_BOOKMARK_FOLDERS: _
}
module.exports = mapValuesByKeys(appConstants)
diff --git a/js/constants/siteTags.js b/js/constants/siteTags.js
index 4a86ba2fa59..581635ea4e1 100644
--- a/js/constants/siteTags.js
+++ b/js/constants/siteTags.js
@@ -4,13 +4,12 @@
const mapValuesByKeys = require('../lib/functional').mapValuesByKeys
+// TODO remove
+
const _ = null
const siteTags = {
- DEFAULT: _,
BOOKMARK: _,
- BOOKMARK_FOLDER: _,
- PINNED: _,
- READING_LIST: _
+ BOOKMARK_FOLDER: _
}
module.exports = mapValuesByKeys(siteTags)
diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js
index 002f927062f..7186fbf9f51 100644
--- a/js/constants/windowConstants.js
+++ b/js/constants/windowConstants.js
@@ -111,7 +111,10 @@ const windowConstants = {
WINDOW_ON_ADD_BOOKMARK: _,
WINDOW_ON_EDIT_BOOKMARK: _,
WINDOW_ON_BOOKMARK_CLOSE: _,
- WINDOW_ON_BOOKMARK_ADDED: _
+ WINDOW_ON_BOOKMARK_ADDED: _,
+ WINDOW_ON_ADD_BOOKMARK_FOLDER: _,
+ WINDOW_ON_EDIT_BOOKMARK_FOLDER: _,
+ WINDOW_ON_BOOKMARK_FOLDER_CLOSE: _
}
module.exports = mapValuesByKeys(windowConstants)
diff --git a/js/contextMenus.js b/js/contextMenus.js
index 50079596af9..840d38caedd 100644
--- a/js/contextMenus.js
+++ b/js/contextMenus.js
@@ -33,6 +33,7 @@ const urlParse = require('../app/common/urlParse')
const {getCurrentWindow} = require('../app/renderer/currentWindow')
const extensionState = require('../app/common/state/extensionState')
const extensionActions = require('../app/common/actions/extensionActions')
+const bookmarkUtil = require('../app/common/lib/bookmarkUtil')
const {makeImmutable} = require('../app/common/state/immutableUtil')
const isDarwin = process.platform === 'darwin'
@@ -70,18 +71,18 @@ const addFolderMenuItem = (closestDestinationDetail, isParent) => {
return {
label: locale.translation('addFolder'),
click: () => {
- let siteDetail = Immutable.fromJS({ tags: [siteTags.BOOKMARK_FOLDER] })
let closestKey = null
+ let folderDetails = Immutable.Map()
if (closestDestinationDetail) {
closestKey = siteUtil.getSiteKey(closestDestinationDetail)
if (isParent) {
- siteDetail = siteDetail.set('parentFolderId', (closestDestinationDetail.get('folderId') || closestDestinationDetail.get('parentFolderId')))
+ folderDetails = folderDetails.set('parentFolderId', (closestDestinationDetail.get('folderId') || closestDestinationDetail.get('parentFolderId')))
}
}
- windowActions.addBookmark(siteDetail, closestKey)
+ windowActions.addBookmarkFolder(folderDetails, closestKey)
}
}
}
@@ -133,8 +134,7 @@ function tabsToolbarTemplateInit (bookmarkTitle, bookmarkLink, closestDestinatio
template.push(addBookmarkMenuItem('addBookmark', {
title: bookmarkTitle,
- location: bookmarkLink,
- tags: [siteTags.BOOKMARK]
+ location: bookmarkLink
}, closestDestinationDetail, isParent))
template.push(addFolderMenuItem(closestDestinationDetail, isParent))
@@ -225,6 +225,7 @@ function downloadsToolbarTemplateInit (downloadId, downloadItem) {
return menuUtil.sanitizeTemplateItems(template)
}
+// TODO refactor it
function siteDetailTemplateInit (siteDetail, activeFrame) {
let isHistoryEntry = false
let multipleHistoryEntries = false
@@ -291,7 +292,7 @@ function siteDetailTemplateInit (siteDetail, activeFrame) {
CommonMenu.separatorMenuItem)
}
} else {
- template.push(openAllInNewTabsMenuItem(appStoreRenderer.state.get('sites'), siteDetail),
+ template.push(openAllInNewTabsMenuItem(siteDetail),
CommonMenu.separatorMenuItem)
}
@@ -306,21 +307,24 @@ function siteDetailTemplateInit (siteDetail, activeFrame) {
label: locale.translation(isFolder ? 'editFolder' : 'editBookmark'),
click: () => {
const editKey = siteUtil.getSiteKey(siteDetail)
+ // TODO use correct action
windowActions.editBookmark(false, editKey)
}
},
CommonMenu.separatorMenuItem)
}
+ // TODO we need to determinate which site do we want to remove
template.push(
{
label: locale.translation(deleteLabel),
click: () => {
- if (Immutable.List.isList(siteDetail)) {
+ console.log(deleteTag)
+ /* if (Immutable.List.isList(siteDetail)) {
siteDetail.forEach((site) => appActions.removeSite(site, deleteTag))
} else {
appActions.removeSite(siteDetail, deleteTag)
- }
+ } */
}
})
}
@@ -330,7 +334,7 @@ function siteDetailTemplateInit (siteDetail, activeFrame) {
template.push(CommonMenu.separatorMenuItem)
}
template.push(
- addBookmarkMenuItem('addBookmark', siteUtil.getDetailFromFrame(activeFrame, siteTags.BOOKMARK), siteDetail, true),
+ addBookmarkMenuItem('addBookmark', bookmarkUtil.getDetailFromFrame(activeFrame), siteDetail, true),
addFolderMenuItem(siteDetail, true))
}
@@ -724,11 +728,11 @@ const openInNewTabMenuItem = (url, isPrivate, partitionNumber, openerTabId) => {
}
}
-const openAllInNewTabsMenuItem = (allSites, folderDetail) => {
+const openAllInNewTabsMenuItem = (folderDetail) => {
return {
label: locale.translation('openAllInTabs'),
click: () => {
- bookmarkActions.openBookmarksInFolder(allSites, folderDetail)
+ bookmarkActions.openBookmarksInFolder(folderDetail)
}
}
}
@@ -1009,8 +1013,7 @@ function mainTemplateInit (nodeProps, frame, tab) {
)
if (isLink) {
template.push(addBookmarkMenuItem('bookmarkLink', {
- location: nodeProps.linkURL,
- tags: [siteTags.BOOKMARK]
+ location: nodeProps.linkURL
}, false))
}
}
@@ -1024,8 +1027,7 @@ function mainTemplateInit (nodeProps, frame, tab) {
if (!isImage) {
if (isLink) {
template.push(addBookmarkMenuItem('bookmarkLink', {
- location: nodeProps.linkURL,
- tags: [siteTags.BOOKMARK]
+ location: nodeProps.linkURL
}, false))
} else {
template.push(
@@ -1055,7 +1057,7 @@ function mainTemplateInit (nodeProps, frame, tab) {
}
},
CommonMenu.separatorMenuItem,
- addBookmarkMenuItem('bookmarkPage', siteUtil.getDetailFromFrame(frame, siteTags.BOOKMARK), false))
+ addBookmarkMenuItem('bookmarkPage', bookmarkUtil.getDetailFromFrame(frame), false))
if (!isAboutPage) {
template.push({
@@ -1204,8 +1206,7 @@ function mainTemplateInit (nodeProps, frame, tab) {
template.push(
CommonMenu.separatorMenuItem,
addBookmarkMenuItem('addBookmark', {
- location: nodeProps.linkURL,
- tags: [siteTags.BOOKMARK]
+ location: nodeProps.linkURL
}),
addFolderMenuItem()
)
diff --git a/js/data/newTabData.js b/js/data/newTabData.js
index ccc0b4e286c..94431f18e7e 100644
--- a/js/data/newTabData.js
+++ b/js/data/newTabData.js
@@ -5,75 +5,64 @@
const {getBraveExtUrl} = require('../lib/appUrlUtil')
const iconPath = getBraveExtUrl('img/newtab/defaultTopSitesIcon')
-const now = 1
-
module.exports.pinnedTopSites = [
- {
- "count": 1,
- "favicon": `${iconPath}/twitter.png`,
- "lastAccessedTime": now,
- "location": "https://twitter.com/brave",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "rgb(255, 255, 255)",
- "title": "Brave Software (@brave) | Twitter"
+ {
+ 'key': 'https://twitter.com/brave/|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/twitter.png`,
+ 'location': 'https://twitter.com/brave/',
+ 'themeColor': 'rgb(255, 255, 255)',
+ 'title': 'Brave Software (@brave) | Twitter'
}
]
module.exports.topSites = [
{
- "count": 1,
- "favicon": `${iconPath}/twitter.png`,
- "lastAccessedTime": now,
- "location": "https://twitter.com/brave",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "rgb(255, 255, 255)",
- "title": "Brave Software (@brave) | Twitter"
- }, {
- "count": 1,
- "favicon": `${iconPath}/facebook.png`,
- "lastAccessedTime": now,
- "location": "https://www.facebook.com/BraveSoftware/",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "rgb(59, 89, 152)",
- "title": "Brave Software | Facebook"
- }, {
- "count": 1,
- "favicon": `${iconPath}/youtube.png`,
- "lastAccessedTime": now,
- "location": "https://www.youtube.com/bravesoftware",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "#E62117",
- "title": "Brave Browser - YouTube"
- }, {
- "count": 1,
- "favicon": `${iconPath}/brave.ico`,
- "lastAccessedTime": now,
- "location": "https://brave.com/",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "rgb(255, 255, 255)",
- "title": "Brave Software | Building a Better Web"
- }, {
- "count": 1,
- "favicon": `${iconPath}/appstore.png`,
- "lastAccessedTime": now,
- "location": "https://itunes.apple.com/app/brave-web-browser/id1052879175?mt=8",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "rgba(255, 255, 255, 1)",
- "title": "Brave Web Browser: Fast with built-in adblock on the App Store"
- }, {
- "count": 1,
- "favicon": `${iconPath}/playstore.png`,
- "lastAccessedTime": now,
- "location": "https://play.google.com/store/apps/details?id=com.brave.browser",
- "partitionNumber": 0,
- "tags": ['default'],
- "themeColor": "rgb(241, 241, 241)",
- "title": "Brave Browser: Fast AdBlock – Apps para Android no Google Play"
+ 'key': 'https://twitter.com/brave/|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/twitter.png`,
+ 'location': 'https://twitter.com/brave',
+ 'themeColor': 'rgb(255, 255, 255)',
+ 'title': 'Brave Software (@brave) | Twitter'
+ },
+ {
+ 'key': 'https://www.facebook.com/BraveSoftware/|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/facebook.png`,
+ 'location': 'https://www.facebook.com/BraveSoftware/',
+ 'themeColor': 'rgb(59, 89, 152)',
+ 'title': 'Brave Software | Facebook'
+ },
+ {
+ 'key': 'https://www.youtube.com/bravesoftware/|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/youtube.png`,
+ 'location': 'https://www.youtube.com/bravesoftware/',
+ 'themeColor': '#E62117',
+ 'title': 'Brave Browser - YouTube'
+ },
+ {
+ 'key': 'https://brave.com/|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/brave.ico`,
+ 'location': 'https://brave.com/',
+ 'themeColor': 'rgb(255, 255, 255)',
+ 'title': 'Brave Software | Building a Better Web'
+ },
+ {
+ 'key': 'https://itunes.apple.com/app/brave-web-browser/id1052879175?mt=8|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/appstore.png`,
+ 'location': 'https://itunes.apple.com/app/brave-web-browser/id1052879175?mt=8',
+ 'themeColor': 'rgba(255, 255, 255, 1)',
+ 'title': 'Brave Web Browser: Fast with built-in adblock on the App Store'
+ },
+ {
+ 'key': 'https://play.google.com/store/apps/details?id=com.brave.browser|0|0',
+ 'count': 0,
+ 'favicon': `${iconPath}/playstore.png`,
+ 'location': 'https://play.google.com/store/apps/details?id=com.brave.browser',
+ 'themeColor': 'rgb(241, 241, 241)',
+ 'title': 'Brave Browser: Fast AdBlock – Apps para Android no Google Play'
}
]
diff --git a/js/lib/importer.js b/js/lib/importer.js
index b0925cff8b2..35c5244542d 100644
--- a/js/lib/importer.js
+++ b/js/lib/importer.js
@@ -4,15 +4,14 @@
const fs = require('fs')
const appActions = require('../actions/appActions')
-const siteTags = require('../constants/siteTags')
-const siteUtil = require('../state/siteUtil')
const Immutable = require('immutable')
const appStoreRenderer = require('../stores/appStoreRenderer')
+const bookmarFoldersUtil = require('../../app/common/lib/bookmarkFoldersUtil')
/**
* Processes a single node from an exported HTML file from Firefox or Chrome
* @param {Object} parserState - the current parser state
- * @param {Object} node - The current DOM node which is being processed
+ * @param {Object} domNode - The current DOM node which is being processed
*/
function processBookmarkNode (parserState, domNode) {
switch (domNode.tagName) {
@@ -38,12 +37,11 @@ function processBookmarkNode (parserState, domNode) {
title: domNode.innerText,
folderId: parserState.nextFolderId,
parentFolderId: parserState.parentFolderId,
- lastAccessedTime: (domNode.getAttribute('LAST_MODIFIED') || domNode.getAttribute('ADD_DATE') || 0) * 1000,
- tags: [siteTags.BOOKMARK_FOLDER]
+ lastAccessedTime: (domNode.getAttribute('LAST_MODIFIED') || domNode.getAttribute('ADD_DATE') || 0) * 1000
}
parserState.lastFolderId = parserState.nextFolderId
parserState.nextFolderId++
- parserState.sites.push(folder)
+ parserState.bookmarkFolders.push(folder)
} else {
parserState.lastFolderId = 0
parserState.foundBookmarksToolbar = true
@@ -54,15 +52,14 @@ function processBookmarkNode (parserState, domNode) {
if (domNode.href.startsWith('place:')) {
break
}
- const site = {
+ const bookmarks = {
title: domNode.innerText,
location: domNode.href,
favicon: domNode.getAttribute('ICON'),
parentFolderId: parserState.parentFolderId,
- lastAccessedTime: (domNode.getAttribute('LAST_MODIFIED') || domNode.getAttribute('ADD_DATE') || 0) * 1000,
- tags: [siteTags.BOOKMARK]
+ lastAccessedTime: (domNode.getAttribute('LAST_MODIFIED') || domNode.getAttribute('ADD_DATE') || 0) * 1000
}
- parserState.sites.push(site)
+ parserState.bookmarks.push(bookmarks)
break
}
}
@@ -91,17 +88,19 @@ module.exports.importFromHTML = (path) => {
// Each window's appStoreRenderer holds a copy of the app state, but it's not
// mutable, so this is only used for getting the current list of sites.
const parserState = {
- nextFolderId: siteUtil.getNextFolderId(appStoreRenderer.state.get('sites')),
+ nextFolderId: bookmarFoldersUtil.getNextFolderId(appStoreRenderer.state.get('bookmarkFolders')),
lastFolderId: -1,
parentFolderId: -1,
- sites: []
+ bookmarks: [],
+ bookmarkFolders: []
}
// Process each of the nodes starting with the first node which is either DL or DT
processBookmarkNode(parserState, doc.querySelector('dl, dt'))
// Add the sites to the app store in the main process
- appActions.addSite(Immutable.fromJS(parserState.sites))
+ appActions.addBookmarks(Immutable.fromJS(parserState.bookmarks))
+ appActions.addBookmarkFolders(Immutable.fromJS(parserState.bookmarkFolders))
resolve({importCount: parserState.sites.length})
})
})
diff --git a/js/lib/textCalculator.js b/js/lib/textCalculator.js
index 8068219c1f4..c543d340b83 100644
--- a/js/lib/textCalculator.js
+++ b/js/lib/textCalculator.js
@@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-const ctx = document.createElement('canvas').getContext('2d')
module.exports.calculateTextWidth = (text, font = '11px Arial') => {
+ const ctx = document.createElement('canvas').getContext('2d')
ctx.font = font
return ctx.measureText(text).width
}
diff --git a/js/state/frameStateUtil.js b/js/state/frameStateUtil.js
index b71f871694e..e6a2e422142 100644
--- a/js/state/frameStateUtil.js
+++ b/js/state/frameStateUtil.js
@@ -58,6 +58,7 @@ function getSortedFrameKeys (state) {
.map(frame => frame.get('key'))
}
+// TODO check if order is preserved, could be a bug
function getPinnedFrames (state) {
return state.get('frames').filter((frame) => frame.get('pinnedLocation'))
}
diff --git a/js/state/siteUtil.js b/js/state/siteUtil.js
index 5320b15a9c7..08274c0da24 100644
--- a/js/state/siteUtil.js
+++ b/js/state/siteUtil.js
@@ -11,8 +11,7 @@ const UrlUtil = require('../lib/urlutil')
const urlParse = require('../../app/common/urlParse')
const {makeImmutable} = require('../../app/common/state/immutableUtil')
-const defaultTags = new Immutable.List([siteTags.DEFAULT])
-
+// TODO remove
const isBookmark = (tags) => {
if (!tags) {
return false
@@ -20,6 +19,7 @@ const isBookmark = (tags) => {
return tags.includes(siteTags.BOOKMARK)
}
+// TODO remove
const isBookmarkFolder = (tags) => {
if (!tags) {
return false
@@ -28,14 +28,6 @@ const isBookmarkFolder = (tags) => {
(tags && typeof tags !== 'string' && tags.includes(siteTags.BOOKMARK_FOLDER))
}
-const isPinnedTab = (tags) => {
- if (!tags) {
- return false
- }
- return tags.includes(siteTags.PINNED)
-}
-module.exports.isPinnedTab = isPinnedTab
-
const reorderSite = (sites, order) => {
sites = sites.map((site) => {
const siteOrder = site.get('order')
@@ -109,79 +101,13 @@ module.exports.getLocationFromSiteKey = function (siteKey) {
* @param siteDetail The site to check if it's in the specified tag
* @return true if the location is already bookmarked
*/
+// TODO remove this when sync is updated
module.exports.isSiteBookmarked = function (sites, siteDetail) {
const siteKey = module.exports.getSiteKey(siteDetail)
const siteTags = sites.getIn([siteKey, 'tags'])
return isBookmark(siteTags)
}
-/**
- * Checks if a location is bookmarked.
- *
- * @param state The application state Immutable map
- * @param {string} location
- * @return {boolean}
- */
-module.exports.isLocationBookmarked = function (state, location) {
- const sites = state.get('sites')
- const siteKeys = siteCache.getLocationSiteKeys(state, location)
- if (!siteKeys || siteKeys.length === 0) {
- return false
- }
- return siteKeys.some(key => {
- const site = sites.get(key)
- if (!site) {
- return false
- }
- return isBookmark(site.get('tags'))
- })
-}
-
-const getNextFolderIdItem = (sites) =>
- sites.max((siteA, siteB) => {
- const folderIdA = siteA.get('folderId')
- const folderIdB = siteB.get('folderId')
- if (folderIdA === folderIdB) {
- return 0
- }
- if (folderIdA === undefined) {
- return false
- }
- if (folderIdB === undefined) {
- return true
- }
- return folderIdA > folderIdB
- })
-
-module.exports.getNextFolderId = (sites) => {
- const defaultFolderId = 0
- if (!sites) {
- return defaultFolderId
- }
- const maxIdItem = getNextFolderIdItem(sites)
- return (maxIdItem ? (maxIdItem.get('folderId') || 0) : 0) + 1
-}
-
-module.exports.getNextFolderName = (sites, name) => {
- if (!sites) {
- return name
- }
- const site = sites.find((site) =>
- isBookmarkFolder(site.get('tags')) &&
- site.get('customTitle') === name
- )
- if (!site) {
- return name
- }
- const filenameFormat = /(.*) \((\d+)\)/
- let result = filenameFormat.exec(name)
- if (!result) {
- return module.exports.getNextFolderName(sites, name + ' (1)')
- }
- const nextNum = parseInt(result[2]) + 1
- return module.exports.getNextFolderName(sites, result[1] + ' (' + nextNum + ')')
-}
-
const mergeSiteLastAccessedTime = (oldSiteDetail, newSiteDetail, tag) => {
const newTime = newSiteDetail && newSiteDetail.get('lastAccessedTime')
const oldTime = oldSiteDetail && oldSiteDetail.get('lastAccessedTime')
@@ -199,16 +125,13 @@ const mergeSiteLastAccessedTime = (oldSiteDetail, newSiteDetail, tag) => {
// Some details can be copied from the existing siteDetail if null
// ex: parentFolderId, partitionNumber, and favicon
+// TODO remove
const mergeSiteDetails = (oldSiteDetail, newSiteDetail, tag, folderId, order) => {
let tags = (oldSiteDetail && oldSiteDetail.get('tags')) || new Immutable.List()
if (tag) {
tags = tags.toSet().add(tag).toList()
}
- const customTitle = typeof newSiteDetail.get('customTitle') === 'string'
- ? newSiteDetail.get('customTitle')
- : (newSiteDetail.get('customTitle') || (oldSiteDetail && oldSiteDetail.get('customTitle')))
-
const lastAccessedTime = mergeSiteLastAccessedTime(oldSiteDetail, newSiteDetail, tag)
let site = makeImmutable({
@@ -229,9 +152,6 @@ const mergeSiteDetails = (oldSiteDetail, newSiteDetail, tag, folderId, order) =>
if (folderId) {
site = site.set('folderId', Number(folderId))
}
- if (typeof customTitle === 'string') {
- site = site.set('customTitle', customTitle)
- }
if (newSiteDetail.get('parentFolderId') !== undefined || (oldSiteDetail && oldSiteDetail.get('parentFolderId'))) {
let parentFolderId = newSiteDetail.get('parentFolderId') !== undefined
? newSiteDetail.get('parentFolderId') : oldSiteDetail.get('parentFolderId')
@@ -273,6 +193,7 @@ const mergeSiteDetails = (oldSiteDetail, newSiteDetail, tag, folderId, order) =>
* does not to be re-uploaded
* @return The new state Immutable object
*/
+// TODO remove
module.exports.addSite = function (state, siteDetail, tag, oldKey, skipSync) {
let sites = state.get('sites')
// Get tag from siteDetail object if not passed via tag param
@@ -291,8 +212,7 @@ module.exports.addSite = function (state, siteDetail, tag, oldKey, skipSync) {
if (!oldSite && folderId) {
// Remove duplicate folder (needed for import)
const dupFolder = sites.find((site) => isBookmarkFolder(site.get('tags')) &&
- site.get('parentFolderId') === siteDetail.get('parentFolderId') &&
- site.get('customTitle') === siteDetail.get('customTitle'))
+ site.get('parentFolderId') === siteDetail.get('parentFolderId'))
if (dupFolder) {
state = module.exports.removeSite(state, dupFolder, siteTags.BOOKMARK_FOLDER, true)
}
@@ -371,6 +291,7 @@ module.exports.removeSiteByObjectId = function (state, objectId, objectData) {
* @param {Function=} syncCallback
* @return {Immutable.Map} The new state Immutable object
*/
+// TODO remove
module.exports.removeSite = function (state, siteDetail, tag, reorder = true, syncCallback) {
let sites = state.get('sites')
const key = module.exports.getSiteKey(siteDetail)
@@ -402,20 +323,11 @@ module.exports.removeSite = function (state, siteDetail, tag, reorder = true, sy
return state
}
if (isBookmark(tag)) {
- if (isPinnedTab(tags)) {
- const tags = site.get('tags').filterNot((tag) => tag === siteTags.BOOKMARK)
- site = site.set('tags', tags)
- return state.setIn(stateKey, site)
- }
if (state.get('sites').size && reorder) {
const order = state.getIn(stateKey.concat(['order']))
state = state.set('sites', reorderSite(state.get('sites'), order))
}
return state.deleteIn(['sites', key])
- } else if (isPinnedTab(tag)) {
- const tags = site.get('tags').filterNot((tag) => tag === siteTags.PINNED)
- site = site.set('tags', tags)
- return state.setIn(stateKey, site)
} else {
site = site.set('lastAccessedTime', undefined)
return state.setIn(stateKey, site)
@@ -470,6 +382,7 @@ module.exports.isMoveAllowed = (sites, sourceDetail, destinationDetail) => {
* @param disallowReparent If set to true, parent folder will not be set
* @return The new state Immutable object
*/
+// TODO remove
module.exports.moveSite = function (state, sourceKey, destinationKey, prepend,
destinationIsParent, disallowReparent) {
let sites = state.get('sites')
@@ -523,159 +436,22 @@ module.exports.moveSite = function (state, sourceKey, destinationKey, prepend,
return state.setIn(['sites', destinationSiteKey], sourceSite)
}
-module.exports.getDetailFromFrame = function (frame, tag) {
- const pinnedLocation = frame.get('pinnedLocation')
- let location = frame.get('location')
- if (pinnedLocation && pinnedLocation !== 'about:blank' && tag === siteTags.PINNED) {
- location = frame.get('pinnedLocation')
- }
-
+module.exports.getDetailFromFrame = function (frame) {
return makeImmutable({
- location,
+ location: frame.get('location'),
title: frame.get('title'),
partitionNumber: frame.get('partitionNumber'),
- tags: tag ? [tag] : [],
favicon: frame.get('icon'),
themeColor: frame.get('themeColor') || frame.get('computedThemeColor')
})
}
-const getSitesBySubkey = (sites, siteKey, tag) => {
- if (!sites || !siteKey) {
- return makeImmutable([])
- }
- const splitKey = siteKey.split('|', 2)
- const partialKey = splitKey.join('|')
- const matches = sites.filter((site, key) => {
- if (key.indexOf(partialKey) > -1 && (!tag || (tag && site.get('tags').includes(tag)))) {
- return true
- }
- return false
- })
- return matches.toList()
-}
-
-module.exports.getDetailFromTab = function (tab, tag, sites) {
- let location = tab.get('url')
- const partitionNumber = tab.get('partitionNumber')
- let parentFolderId
-
- // if site map is available, look up extra information:
- // - original url (if redirected)
- // - parent folder id
- if (sites) {
- let results = makeImmutable([])
-
- // get all sites matching URL and partition (disregarding parentFolderId)
- let siteKey = module.exports.getSiteKey(makeImmutable({location, partitionNumber}))
- results = results.merge(getSitesBySubkey(sites, siteKey, tag))
-
- // only check for provisional location if entry is not found
- if (results.size === 0) {
- // if provisional location is different, grab any results which have that URL
- // this may be different if the site was redirected
- const provisionalLocation = tab.getIn(['frame', 'provisionalLocation'])
- if (provisionalLocation && provisionalLocation !== location) {
- siteKey = module.exports.getSiteKey(makeImmutable({
- location: provisionalLocation,
- partitionNumber
- }))
- results = results.merge(getSitesBySubkey(sites, siteKey, tag))
- }
- }
-
- // update details which get returned below
- if (results.size > 0) {
- location = results.getIn([0, 'location'])
- parentFolderId = results.getIn([0, 'parentFolderId'])
- }
- }
-
- const siteDetail = {
- location: location,
- title: tab.get('title'),
- tags: tag ? [tag] : []
- }
- if (partitionNumber) {
- siteDetail.partitionNumber = partitionNumber
- }
- if (parentFolderId) {
- siteDetail.parentFolderId = parentFolderId
- }
- return Immutable.fromJS(siteDetail)
-}
-
-module.exports.getDetailFromCreateProperties = function (createProperties, tag) {
- const siteDetail = {
- location: createProperties.get('url'),
- tags: tag ? [tag] : []
- }
- if (createProperties.get('partitionNumber') !== undefined) {
- siteDetail.partitionNumber = createProperties.get('partitionNumber')
- }
- return Immutable.fromJS(siteDetail)
-}
-
-/**
- * Update the favicon URL for all entries in the state sites
- * which match a given location. Currently, there should only be
- * one match, but this will handle multiple.
- *
- * @param state The application state
- * @param location URL for the entry needing an update
- * @param favicon favicon URL
- */
-module.exports.updateSiteFavicon = function (state, location, favicon) {
- if (UrlUtil.isNotURL(location)) {
- return state
- }
- const siteKeys = siteCache.getLocationSiteKeys(state, location)
- if (!siteKeys || siteKeys.length === 0) {
- return state
- }
- siteKeys.forEach((siteKey) => {
- state = state.setIn(['sites', siteKey, 'favicon'], favicon)
- })
- return state
-}
-
-/**
- * Converts a siteDetail to createProperties format
- * @param {Object} siteDetail - A site detail as per app state
- * @return {Object} A createProperties plain JS object, not ImmutableJS
- */
-module.exports.toCreateProperties = function (siteDetail) {
- return {
- url: siteDetail.get('location'),
- partitionNumber: siteDetail.get('partitionNumber')
- }
-}
-
-/**
- * Compares 2 site details
- * @param siteDetail1 The first site detail to compare.
- * @param siteDetail2 The second site detail to compare.
- * @return true if the site details should be considered the same.
- */
-module.exports.isEquivalent = function (siteDetail1, siteDetail2) {
- const isFolder1 = module.exports.isFolder(siteDetail1)
- const isFolder2 = module.exports.isFolder(siteDetail2)
- if (isFolder1 !== isFolder2) {
- return false
- }
-
- // If they are both folders
- if (isFolder1) {
- return siteDetail1.get('folderId') === siteDetail2.get('folderId')
- }
- return siteDetail1.get('location') === siteDetail2.get('location') && siteDetail1.get('partitionNumber') === siteDetail2.get('partitionNumber')
-}
-
/**
* Determines if the site detail is a bookmark.
* @param siteDetail The site detail to check.
* @return true if the site detail has a bookmark tag.
*/
+// TODO remove
module.exports.isBookmark = function (siteDetail) {
if (siteDetail) {
return isBookmark(siteDetail.get('tags'))
@@ -688,6 +464,7 @@ module.exports.isBookmark = function (siteDetail) {
* @param siteDetail The site detail to check.
* @return true if the site detail is a folder.
*/
+// TODO remove
module.exports.isFolder = function (siteDetail) {
if (siteDetail) {
return isBookmarkFolder(siteDetail.get('tags')) && siteDetail.get('folderId') !== undefined
@@ -695,25 +472,16 @@ module.exports.isFolder = function (siteDetail) {
return false
}
-/**
- * Determines if the site detail is an imported bookmark.
- * @param siteDetail The site detail to check.
- * @return true if the site detail is a folder.
- */
-module.exports.isImportedBookmark = function (siteDetail) {
- return siteDetail.get('lastAccessedTime') === 0
-}
-
/**
* Determines if the site detail is a history entry.
* @param siteDetail The site detail to check.
* @return true if the site detail is a history entry.
*/
+// TODO remove when sync is refactored
module.exports.isHistoryEntry = function (siteDetail) {
if (siteDetail && typeof siteDetail.get('location') === 'string') {
const tags = siteDetail.get('tags')
if (siteDetail.get('location').startsWith('about:') ||
- module.exports.isDefaultEntry(siteDetail) ||
isBookmarkFolder(tags)) {
return false
}
@@ -722,63 +490,6 @@ module.exports.isHistoryEntry = function (siteDetail) {
return false
}
-/**
- * Determines if the site detail is one of default sites in about:newtab.
- * @param {Immutable.Map} siteDetail The site detail to check.
- * @returns {boolean} if the site detail is a default newtab entry.
- */
-module.exports.isDefaultEntry = function (siteDetail) {
- return Immutable.is(siteDetail.get('tags'), defaultTags) &&
- siteDetail.get('lastAccessedTime') === 1
-}
-
-/**
- * Get a folder by folderId
- * @returns {Immutable.List.} sites
- * @param {number} folderId
- * @returns {Array[, ]|undefined}
- */
-module.exports.getFolder = function (sites, folderId) {
- const entry = sites.findEntry((site, _path) => {
- return module.exports.isFolder(site) && site.get('folderId') === folderId
- })
- if (!entry) { return undefined }
- return entry
-}
-
-/**
- * Obtains an array of folders
- */
-module.exports.getFolders = function (sites, folderId, parentId, labelPrefix) {
- parentId = parentId || 0
- let folders = []
- const results = sites
- .filter(site => {
- return (site.get('parentFolderId', 0) === parentId && module.exports.isFolder(site))
- })
- .toList()
- .sort(module.exports.siteSort)
-
- const resultSize = results.size
- for (let i = 0; i < resultSize; i++) {
- const site = results.get(i)
- if (site.get('folderId') === folderId) {
- continue
- }
-
- const label = (labelPrefix || '') + (site.get('customTitle') || site.get('title'))
- folders.push({
- folderId: site.get('folderId'),
- parentFolderId: site.get('parentFolderId'),
- label
- })
- const subsites = module.exports.getFolders(sites, folderId, site.get('folderId'), (label || '') + ' / ')
- folders = folders.concat(subsites)
- }
-
- return folders
-}
-
/**
* Filters out non recent sites based on the app setting for history size.
* @param sites The application state's Immutable sites list.
@@ -793,18 +504,6 @@ module.exports.filterOutNonRecents = function (sites) {
return sitesWithTags.concat(topHistorySites)
}
-/**
- * Filters sites relative to a parent site (folder).
- * @param sites The application state's Immutable sites list.
- * @param relSite The folder to filter to.
- */
-module.exports.filterSitesRelativeTo = function (sites, relSite) {
- if (!relSite.get('folderId')) {
- return sites
- }
- return sites.filter((site) => site.get('parentFolderId') === relSite.get('folderId'))
-}
-
/**
* Clears history by
* - filtering out entries which have no tags
@@ -825,7 +524,7 @@ module.exports.clearHistory = function (sites) {
* Returns all sites that have a bookmark tag.
* @param sites The application state's Immutable sites list.
*/
-
+// TODO remove when getToolbarBookmarks is refactored
module.exports.getBookmarks = function (sites) {
if (sites) {
return sites.filter((site) => isBookmarkFolder(site.get('tags')) || isBookmark(site.get('tags')))
diff --git a/js/state/syncUtil.js b/js/state/syncUtil.js
index 2439eef3d19..b19805be659 100644
--- a/js/state/syncUtil.js
+++ b/js/state/syncUtil.js
@@ -47,7 +47,19 @@ module.exports.siteSettingDefaults = {
// Whitelist of valid browser-laptop site fields. In browser-laptop, site
// is used for both bookmarks and history sites.
-const SITE_FIELDS = ['objectId', 'location', 'title', 'customTitle', 'tags', 'favicon', 'themeColor', 'lastAccessedTime', 'creationTime', 'partitionNumber', 'folderId', 'parentFolderId']
+const SITE_FIELDS = [
+ 'objectId',
+ 'location',
+ 'title',
+ 'tags',
+ 'favicon',
+ 'themeColor',
+ 'lastAccessedTime',
+ 'creationTime',
+ 'partitionNumber',
+ 'folderId',
+ 'parentFolderId'
+]
const pickFields = (object, fields) => {
return fields.reduce((a, x) => {
@@ -293,6 +305,7 @@ module.exports.getExistingObject = (categoryName, syncRecord) => {
*/
module.exports.createSiteCache = (appState) => {
const objectsById = new Immutable.Map().withMutations(objectsById => {
+ // TODO what to do here?
appState.get('sites').forEach((site, siteKey) => {
const objectId = site.get('objectId')
if (!objectId) { return true }
@@ -461,7 +474,6 @@ module.exports.createSiteData = (site, appState) => {
const siteData = {
location: '',
title: '',
- customTitle: '',
favicon: '',
lastAccessedTime: 0,
creationTime: 0
diff --git a/js/stores/appStore.js b/js/stores/appStore.js
index 04a72501ba5..ff8fb1c6ba4 100644
--- a/js/stores/appStore.js
+++ b/js/stores/appStore.js
@@ -45,6 +45,8 @@ const extensionState = require('../../app/common/state/extensionState')
const aboutNewTabState = require('../../app/common/state/aboutNewTabState')
const aboutHistoryState = require('../../app/common/state/aboutHistoryState')
const tabState = require('../../app/common/state/tabState')
+const bookmarksState = require('../../app/common/state/bookmarksState')
+var bookmarkFoldersState = require('../../app/common/state/bookmarkFoldersState.js')
const isDarwin = process.platform === 'darwin'
const isWindows = process.platform === 'win32'
@@ -343,16 +345,6 @@ function setDefaultWindowSize () {
const appStore = new AppStore()
const emitChanges = debounce(appStore.emitChanges.bind(appStore), 5)
-/**
- * Clears out the top X non tagged sites.
- * This is debounced to every 1 minute, the cleanup is not particularly intensive
- * but there's no point to cleanup frequently.
- */
-const filterOutNonRecents = debounce(() => {
- appState = appState.set('sites', siteUtil.filterOutNonRecents(appState.get('sites')))
- emitChanges()
-}, 60 * 1000)
-
/**
* Useful for updating non-react preferences (electron properties, etc).
* Called when any settings are modified (ex: via preferences).
@@ -457,19 +449,13 @@ const handleAppAction = (action) => {
case appConstants.APP_DATA_URL_COPIED:
nativeImage.copyDataURL(action.dataURL, action.html, action.text)
break
- case appConstants.APP_ADD_SITE:
- case appConstants.APP_ADD_BOOKMARK:
- case appConstants.APP_EDIT_BOOKMARK:
- const oldSiteSize = appState.get('sites').size
- calculateTopSites(false)
+ // TODO we don't need this for bookmarks, refactor when doing history
+ case appConstants.APP_ADD_HISTORY_SITE:
+ calculateTopSites(true)
appState = aboutHistoryState.setHistory(appState, action)
- // If there was an item added then clear out the old history entries
- if (oldSiteSize !== appState.get('sites').size) {
- filterOutNonRecents()
- }
break
case appConstants.APP_APPLY_SITE_RECORDS:
- case appConstants.APP_REMOVE_SITE:
+ case appConstants.APP_REMOVE_HISTORY_SITE:
calculateTopSites(true)
appState = aboutHistoryState.setHistory(appState, action)
break
@@ -665,8 +651,8 @@ const handleAppAction = (action) => {
const clearData = defaults ? defaults.merge(temp) : temp
if (clearData.get('browserHistory')) {
- calculateTopSites(true)
- appState = aboutHistoryState.setHistory(appState)
+ appState = aboutNewTabState.clearTopSites(appState)
+ appState = aboutHistoryState.clearHistory(appState)
syncActions.clearHistory()
BrowserWindow.getAllWindows().forEach((wnd) => wnd.webContents.send(messages.CLEAR_CLOSED_FRAMES))
}
@@ -800,7 +786,7 @@ const handleAppAction = (action) => {
appState = appState.set('defaultBrowserCheckComplete', {})
break
case windowConstants.WINDOW_SET_FAVICON:
- appState = siteUtil.updateSiteFavicon(appState, action.frameProps.get('location'), action.favicon)
+ appState = bookmarksState.updateSiteFavicon(appState, action.frameProps.get('location'), action.favicon)
if (action.frameProps.get('favicon') !== action.favicon) {
calculateTopSites(false)
}
@@ -856,13 +842,22 @@ const handleAppAction = (action) => {
const syncDefault = Immutable.fromJS(sessionStore.defaultAppState().sync)
const originalSeed = appState.getIn(['sync', 'seed'])
appState = appState.set('sync', syncDefault)
- appState.get('sites').forEach((site, key) => {
- if (site.has('objectId') && syncUtil.isSyncable('bookmark', site)) {
+ bookmarksState.getBookmarks(appState).forEach((site, key) => {
+ if (site.has('objectId')) {
+ // Remember which profile this bookmark was originally synced to.
+ // Since old bookmarks should be synced when a new profile is created,
+ // we have to keep track of which profile already has these bookmarks
+ // or else the old profile may have these bookmarks duplicated. #7405
+ appState = appState.setIn(['bookmarks', key, 'originalSeed'], originalSeed)
+ }
+ })
+ bookmarkFoldersState.getFolders(appState).forEach((site, key) => {
+ if (site.has('objectId')) {
// Remember which profile this bookmark was originally synced to.
// Since old bookmarks should be synced when a new profile is created,
// we have to keep track of which profile already has these bookmarks
// or else the old profile may have these bookmarks duplicated. #7405
- appState = appState.setIn(['sites', key, 'originalSeed'], originalSeed)
+ appState = appState.setIn(['bookmarks', key, 'originalSeed'], originalSeed)
}
})
appState.setIn(['sync', 'devices'], {})
diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js
index 4e09138b53f..bd592337fa6 100644
--- a/js/stores/windowStore.js
+++ b/js/stores/windowStore.js
@@ -471,18 +471,20 @@ const doAction = (action) => {
windowState = windowState.delete('bookmarkDetail')
break
case windowConstants.WINDOW_ON_EDIT_BOOKMARK:
- const siteDetail = appStoreRenderer.state.getIn(['sites', action.editKey])
+ {
+ const siteDetail = appStoreRenderer.state.getIn(['bookmarks', action.editKey])
- windowState = windowState.setIn(['bookmarkDetail'], Immutable.fromJS({
- siteDetail: siteDetail,
- editKey: action.editKey,
- isBookmarkHanger: action.isHanger
- }))
- break
+ windowState = windowState.setIn(['bookmarkDetail'], Immutable.fromJS({
+ siteDetail: siteDetail,
+ editKey: action.editKey,
+ isBookmarkHanger: action.isHanger
+ }))
+ break
+ }
case windowConstants.WINDOW_ON_BOOKMARK_ADDED:
{
let editKey = action.editKey
- const site = appStoreRenderer.state.getIn(['sites', editKey])
+ const site = appStoreRenderer.state.getIn(['bookmarks', editKey])
let siteDetail = action.siteDetail
if (site) {
@@ -503,6 +505,25 @@ const doAction = (action) => {
}))
}
break
+ case windowConstants.WINDOW_ON_ADD_BOOKMARK_FOLDER:
+ windowState = windowState.setIn(['bookmarkFolderDetail'], Immutable.fromJS({
+ folderDetails: action.folderDetails,
+ closestKey: action.closestKey
+ }))
+ break
+ case windowConstants.WINDOW_ON_EDIT_BOOKMARK_FOLDER:
+ {
+ const folderDetails = appStoreRenderer.state.getIn(['bookmarkFolders', action.editKey])
+
+ windowState = windowState.setIn(['bookmarkFolderDetail'], Immutable.fromJS({
+ folderDetails: folderDetails,
+ editKey: action.editKey
+ }))
+ break
+ }
+ case windowConstants.WINDOW_ON_BOOKMARK_FOLDER_CLOSE:
+ windowState = windowState.delete('bookmarkFolderDetail')
+ break
case windowConstants.WINDOW_AUTOFILL_SELECTION_CLICKED:
ipc.send('autofill-selection-clicked', action.tabId, action.value, action.frontEndId, action.index)
windowState = windowState.delete('contextMenuDetail')
diff --git a/test/unit/app/common/lib/pinnedSitesUtilTest.js b/test/unit/app/common/lib/pinnedSitesUtilTest.js
new file mode 100644
index 00000000000..e2ef0ffeedf
--- /dev/null
+++ b/test/unit/app/common/lib/pinnedSitesUtilTest.js
@@ -0,0 +1,39 @@
+/* global describe, it */
+const pinnedSitesUtil = require('../../../../../app/common/lib/pinnedSitesUtil')
+const assert = require('assert')
+const Immutable = require('immutable')
+
+require('../../../braveUnit')
+
+describe('pinnedSitesUtil', () => {
+ const location = 'https://css-tricks.com/'
+ const order = 9
+ const partitionNumber = 5
+ const expectedSiteProps = Immutable.fromJS({
+ location,
+ order,
+ partitionNumber
+ })
+
+ let site = Immutable.fromJS({
+ favicon: 'https://css-tricks.com/favicon.ico',
+ lastAccessedTime: 1493560182224,
+ location: location,
+ order: order,
+ partitionNumber: partitionNumber,
+ title: 'CSS-Tricks'
+ })
+
+ describe('getPinnedSiteProps', () => {
+ it('returns object with necessary fields', () => {
+ const result = pinnedSitesUtil.getPinnedSiteProps(site)
+ assert.deepEqual(expectedSiteProps, result)
+ })
+
+ it('set partitionNumber field to 0 in case of missing this field', () => {
+ const newSite = site.delete('partitionNumber')
+ const result = pinnedSitesUtil.getPinnedSiteProps(newSite)
+ assert.equal(0, result.get('partitionNumber'))
+ })
+ })
+})
diff --git a/test/unit/app/common/lib/windowsUtilTest.js b/test/unit/app/common/lib/windowsUtilTest.js
deleted file mode 100644
index 60ece75b82b..00000000000
--- a/test/unit/app/common/lib/windowsUtilTest.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/* global describe, beforeEach, it */
-const windowsUtil = require('../../../../../app/common/lib/windowsUtil')
-const assert = require('assert')
-const Immutable = require('immutable')
-
-require('../../../braveUnit')
-
-describe('windowsUtil', () => {
- const location = 'https://css-tricks.com/'
- const order = 9
- const partitionNumber = 5
- const expectedSiteProps = Immutable.fromJS({
- location,
- order,
- partitionNumber
- })
- let site
-
- describe('getPinnedSiteProps', () => {
- beforeEach(() => {
- site = Immutable.fromJS({
- favicon: 'https://css-tricks.com/favicon.ico',
- lastAccessedTime: 1493560182224,
- location: location,
- order: order,
- partitionNumber: partitionNumber,
- title: 'CSS-Tricks'
- })
- })
- it('returns object with necessary fields', () => {
- const result = windowsUtil.getPinnedSiteProps(site)
- assert.deepEqual(expectedSiteProps, result)
- })
- it('set partitionNumber field to 0 in case of missing this field', () => {
- site = site.delete('partitionNumber')
- const result = windowsUtil.getPinnedSiteProps(site)
- assert.equal(0, result.get('partitionNumber'))
- })
- })
-})
diff --git a/test/unit/state/siteUtilTest.js b/test/unit/state/siteUtilTest.js
index f45653f3ca0..0114b9feaf8 100644
--- a/test/unit/state/siteUtilTest.js
+++ b/test/unit/state/siteUtilTest.js
@@ -1335,67 +1335,6 @@ describe('siteUtil', function () {
})
})
- describe('isEquivalent', function () {
- it('returns true if both siteDetail objects are identical', function () {
- const siteDetail1 = Immutable.fromJS({
- location: testUrl1,
- partitionNumber: 0,
- tags: [siteTags.BOOKMARK]
- })
- const siteDetail2 = Immutable.fromJS({
- location: testUrl1,
- partitionNumber: 0,
- tags: [siteTags.BOOKMARK]
- })
- assert.equal(siteUtil.isEquivalent(siteDetail1, siteDetail2), true)
- })
- it('returns false if one object is a folder and the other is not', function () {
- const siteDetail1 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK]
- })
- const siteDetail2 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK_FOLDER],
- folderId: 1
- })
- assert.equal(siteUtil.isEquivalent(siteDetail1, siteDetail2), false)
- })
- it('returns false if both are folders and have a different folderId', function () {
- const siteDetail1 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK_FOLDER],
- folderId: 0
- })
- const siteDetail2 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK_FOLDER],
- folderId: 1
- })
- assert.equal(siteUtil.isEquivalent(siteDetail1, siteDetail2), false)
- })
- it('returns false if both are bookmarks and have a different location', function () {
- const siteDetail1 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK],
- location: testUrl1
- })
- const siteDetail2 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK],
- location: 'http://example.com/'
- })
- assert.equal(siteUtil.isEquivalent(siteDetail1, siteDetail2), false)
- })
- it('returns false if both are bookmarks and have a different partitionNumber', function () {
- const siteDetail1 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK],
- location: testUrl1,
- partitionNumber: 0
- })
- const siteDetail2 = Immutable.fromJS({
- tags: [siteTags.BOOKMARK],
- location: testUrl2,
- partitionNumber: 1
- })
- assert.equal(siteUtil.isEquivalent(siteDetail1, siteDetail2), false)
- })
- })
-
describe('isFolder', function () {
it('returns true if the input is a siteDetail and has a `BOOKMARK_FOLDER` tag and a folder ID', function () {
const siteDetail = Immutable.fromJS({
@@ -1683,20 +1622,4 @@ describe('siteUtil', function () {
assert.strictEqual(siteUtil.getOrigin('http://http/test'), 'http://http')
})
})
- describe('isPinnedTab', function () {
- it('detects pinned tab site', function () {
- assert.strictEqual(siteUtil.isPinnedTab(siteTags.PINNED), true)
- assert.strictEqual(siteUtil.isPinnedTab([siteTags.PINNED]), true)
- })
- it('detects not pinned for no site tags', function () {
- assert.strictEqual(siteUtil.isPinnedTab([]), false)
- })
- it('detects not pinned for site tags which are not PINNED', function () {
- assert.strictEqual(siteUtil.isPinnedTab(siteTags.BOOKMARK), false)
- assert.strictEqual(siteUtil.isPinnedTab([siteTags.BOOKMARK]), false)
- })
- it('detects pinned when bookmarked and pinned', function () {
- assert.strictEqual(siteUtil.isPinnedTab([siteTags.PINNED, siteTags.BOOKMARK]), true)
- })
- })
})