diff --git a/app/browser/api/textCalc.js b/app/browser/api/textCalc.js new file mode 100644 index 00000000000..90d827fde2c --- /dev/null +++ b/app/browser/api/textCalc.js @@ -0,0 +1,142 @@ +/* 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') + +// Actions +const appActions = require('../../../js/actions/appActions') + +// Constant +const siteTags = require('../../../js/constants/siteTags') + +// Utils +const tabs = require('../../browser/tabs') +const {makeImmutable} = require('../../common/state/immutableUtil') + +// Styles +const globalStyles = require('../../renderer/components/styles/global') + +const fontSize = globalStyles.spacing.bookmarksItemFontSize +const fontFamily = globalStyles.defaultFontFamily + +const calcText = (item, type) => { + const title = type === siteTags.BOOKMARK + ? item.get('title') || item.get('location') + : item.get('title') + + if (title && title.length === 0) { + return + } + + const param = ` + (function() { + let ctx = document.createElement('canvas').getContext('2d') + ctx.font = '${fontSize} ${fontFamily}' + const width = ctx.measureText('${title}').width + + return width + })() + ` + + tabs.executeScriptInBackground(param, (err, url, result) => { + if (err) { + throw err + } + + if (type === siteTags.BOOKMARK) { + appActions.onBookmarkWidthChanged(Immutable.fromJS([ + { + key: item.get('key'), + parentFolderId: item.get('parentFolderId'), + width: result[0] + } + ])) + } else { + appActions.onBookmarkFolderWidthChanged(Immutable.fromJS([ + { + key: item.get('key'), + parentFolderId: item.get('parentFolderId'), + width: result[0] + } + ])) + } + }) +} + +const calcTextList = (list) => { + const take = 500 + list = makeImmutable(list) + + if (list.size === 0) { + return + } + + let paramList = JSON.stringify(list.take(take)) + .replace(/'/g, '!') + .replace(/\\"/g, '!') + .replace(/\\\\/g, '//') + + const param = ` + (function() { + const ctx = document.createElement('canvas').getContext('2d') + ctx.font = '${fontSize} ${fontFamily}' + const bookmarks = [] + const folders = [] + const list = JSON.parse('${paramList}') + + list.forEach(item => { + if (item.type === '${siteTags.BOOKMARK}') { + bookmarks.push({ + key: item.key, + parentFolderId: item.parentFolderId, + width: ctx.measureText(item.title || item.location).width + }) + } else { + folders.push({ + key: item.key, + parentFolderId: item.parentFolderId, + width: ctx.measureText(item.title).width + }) + } + }) + + const result = { + bookmarks: bookmarks, + folders: folders + } + + return JSON.stringify(result) + })() + ` + + tabs.executeScriptInBackground(param, (err, url, result) => { + if (err) { + console.error('Error in executeScriptInBackground (textCalcUtil.js)') + } + + if (result[0]) { + const data = JSON.parse(result[0]) + if (data.bookmarks.length > 0) { + appActions.onBookmarkWidthChanged(Immutable.fromJS(data.bookmarks)) + } + + if (data.folders.length > 0) { + appActions.onBookmarkFolderWidthChanged(Immutable.fromJS(data.folders)) + } + } else { + console.error('Error, cant parse bookmarks in executeScriptInBackground') + } + + list = list.skip(take) + + if (list.size > 0) { + calcTextList(list) + } + }) +} + +module.exports = { + calcText, + calcTextList +} diff --git a/app/browser/menu.js b/app/browser/menu.js index a4c5cde12f8..7aad132045a 100644 --- a/app/browser/menu.js +++ b/app/browser/menu.js @@ -705,8 +705,10 @@ const doAction = (state, action) => { } case appConstants.APP_ADD_BOOKMARK: case appConstants.APP_EDIT_BOOKMARK: + case appConstants.APP_MOVE_BOOKMARK: case appConstants.APP_REMOVE_BOOKMARK: case appConstants.APP_ADD_BOOKMARK_FOLDER: + case appConstants.APP_MOVE_BOOKMARK_FOLDER: case appConstants.APP_EDIT_BOOKMARK_FOLDER: case appConstants.APP_REMOVE_BOOKMARK_FOLDER: createMenu(state) diff --git a/app/browser/reducers/bookmarkFoldersReducer.js b/app/browser/reducers/bookmarkFoldersReducer.js index 0849fcd05f4..acc9ddd4a0a 100644 --- a/app/browser/reducers/bookmarkFoldersReducer.js +++ b/app/browser/reducers/bookmarkFoldersReducer.js @@ -6,14 +6,18 @@ const Immutable = require('immutable') // State const bookmarkFoldersState = require('../../common/state/bookmarkFoldersState') +const bookmarkToolbarState = require('../../common/state/bookmarkToolbarState') // Constants const appConstants = require('../../../js/constants/appConstants') +const siteTags = require('../../../js/constants/siteTags') const {STATE_SITES} = require('../../../js/constants/stateConstants') // Utils const {makeImmutable} = require('../../common/state/immutableUtil') const syncUtil = require('../../../js/state/syncUtil') +const textCalc = require('../../browser/api/textCalc') +const bookmarkFolderUtil = require('../../common/lib/bookmarkFoldersUtil') const bookmarkFoldersReducer = (state, action, immutableAction) => { action = immutableAction || makeImmutable(action) @@ -28,13 +32,19 @@ const bookmarkFoldersReducer = (state, action, immutableAction) => { } if (Immutable.List.isList(folder)) { + let folderList = Immutable.List() action.get('folderDetails', Immutable.List()).forEach((folder) => { - state = bookmarkFoldersState.addFolder(state, folder, closestKey) - state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) + const folderDetails = bookmarkFolderUtil.buildFolder(folder, bookmarkFoldersState.getFolders(state)) + state = bookmarkFoldersState.addFolder(state, folderDetails, closestKey) + state = syncUtil.updateObjectCache(state, folderDetails, STATE_SITES.BOOKMARK_FOLDERS) + folderList = folderList.push(folderDetails) }) + textCalc.calcTextList(folderList) } else { - state = bookmarkFoldersState.addFolder(state, folder, closestKey) - state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) + const folderDetails = bookmarkFolderUtil.buildFolder(folder, bookmarkFoldersState.getFolders(state)) + state = bookmarkFoldersState.addFolder(state, folderDetails, closestKey) + state = syncUtil.updateObjectCache(state, folderDetails, STATE_SITES.BOOKMARK_FOLDERS) + textCalc.calcText(folderDetails, siteTags.BOOKMARK_FOLDER) } break } @@ -49,7 +59,8 @@ const bookmarkFoldersReducer = (state, action, immutableAction) => { state = bookmarkFoldersState.editFolder(state, key, folder) state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) - + const folderDetails = bookmarkFoldersState.getFolder(state, key) + textCalc.calcText(folderDetails, siteTags.BOOKMARK_FOLDER) break } case appConstants.APP_MOVE_BOOKMARK_FOLDER: @@ -70,6 +81,9 @@ const bookmarkFoldersReducer = (state, action, immutableAction) => { const destinationDetail = bookmarkFoldersState.getFolder(state, action.get('destinationKey')) state = syncUtil.updateObjectCache(state, destinationDetail, STATE_SITES.BOOKMARK_FOLDERS) + if (destinationDetail.get('parentFolderId') === 0 || action.get('destinationKey') === 0) { + state = bookmarkToolbarState.setToolbars(state) + } break } case appConstants.APP_REMOVE_BOOKMARK_FOLDER: @@ -82,16 +96,38 @@ const bookmarkFoldersReducer = (state, action, immutableAction) => { if (Immutable.List.isList(folderKey)) { action.get('folderKey', Immutable.List()).forEach((key) => { - const folder = state.getIn([STATE_SITES.BOOKMARK_FOLDERS, key]) + const folder = bookmarkFoldersState.getFolder(state, key) state = bookmarkFoldersState.removeFolder(state, key) state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) }) + state = bookmarkToolbarState.setToolbars(state) } else { - const folder = state.getIn([STATE_SITES.BOOKMARK_FOLDERS, folderKey]) + const folder = bookmarkFoldersState.getFolder(state, folderKey) state = bookmarkFoldersState.removeFolder(state, folderKey) state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) + if (folder.get('parentFolderId') === 0) { + state = bookmarkToolbarState.setToolbars(state) + } + } + break + } + case appConstants.APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED: + { + if (action.get('folderList', Immutable.List()).isEmpty()) { + break } + let updateToolbar = false + action.get('folderList').forEach(item => { + state = bookmarkFoldersState.setWidth(state, item.get('key'), item.get('width')) + if (item.get('parentFolderId') === 0) { + updateToolbar = true + } + }) + + if (updateToolbar) { + state = bookmarkToolbarState.setToolbars(state) + } break } } diff --git a/app/browser/reducers/bookmarkToolbarReducer.js b/app/browser/reducers/bookmarkToolbarReducer.js new file mode 100644 index 00000000000..8621487dd81 --- /dev/null +++ b/app/browser/reducers/bookmarkToolbarReducer.js @@ -0,0 +1,37 @@ +/* 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/. */ + +// Constants +const appConstants = require('../../../js/constants/appConstants') + +// State +const bookmarksState = require('../../common/state/bookmarksState') +const bookmarkFoldersState = require('../../common/state/bookmarkFoldersState') + +// Util +const {makeImmutable} = require('../../common/state/immutableUtil') +const textCalc = require('../../browser/api/textCalc') + +const bookmarkToolbarReducer = (state, action, immutableAction) => { + action = immutableAction || makeImmutable(action) + switch (action.get('actionType')) { + case appConstants.APP_SET_STATE: + { + // update session for 0.21.x version + const bookmarks = bookmarksState.getBookmarks(state) + if (bookmarks.first() && !bookmarks.first().has('width')) { + textCalc.calcTextList(bookmarks.toList()) + } + + const bookmarkFolders = bookmarkFoldersState.getFolders(state) + if (bookmarkFolders.first() && !bookmarkFolders.first().has('width')) { + textCalc.calcTextList(bookmarkFolders.toList()) + } + } + break + } + return state +} + +module.exports = bookmarkToolbarReducer diff --git a/app/browser/reducers/bookmarksReducer.js b/app/browser/reducers/bookmarksReducer.js index 867812930aa..bf9df963289 100644 --- a/app/browser/reducers/bookmarksReducer.js +++ b/app/browser/reducers/bookmarksReducer.js @@ -6,9 +6,11 @@ const Immutable = require('immutable') // State const bookmarksState = require('../../common/state/bookmarksState') +const bookmarkToolbarState = require('../../common/state/bookmarkToolbarState') // Constants const appConstants = require('../../../js/constants/appConstants') +const siteTags = require('../../../js/constants/siteTags') const {STATE_SITES} = require('../../../js/constants/stateConstants') // Utils @@ -16,6 +18,7 @@ const {makeImmutable} = require('../../common/state/immutableUtil') const syncUtil = require('../../../js/state/syncUtil') const bookmarkUtil = require('../../common/lib/bookmarkUtil') const bookmarkLocationCache = require('../../common/cache/bookmarkLocationCache') +const textCalc = require('../../browser/api/textCalc') const bookmarksReducer = (state, action, immutableAction) => { action = immutableAction || makeImmutable(action) @@ -33,13 +36,19 @@ const bookmarksReducer = (state, action, immutableAction) => { } if (Immutable.List.isList(bookmark)) { + let bookmarkList = Immutable.List() action.get('siteDetail', Immutable.List()).forEach((bookmark) => { - state = bookmarksState.addBookmark(state, bookmark, closestKey) - state = syncUtil.updateObjectCache(state, bookmark, STATE_SITES.BOOKMARKS) + const bookmarkDetail = bookmarkUtil.buildBookmark(state, bookmark) + state = bookmarksState.addBookmark(state, bookmarkDetail, closestKey) + state = syncUtil.updateObjectCache(state, bookmarkDetail, STATE_SITES.BOOKMARKS) + bookmarkList = bookmarkList.push(bookmarkDetail) }) + textCalc.calcTextList(bookmarkList) } else { - state = bookmarksState.addBookmark(state, bookmark, closestKey) - state = syncUtil.updateObjectCache(state, bookmark, STATE_SITES.BOOKMARKS) + const bookmarkDetail = bookmarkUtil.buildBookmark(state, bookmark) + state = bookmarksState.addBookmark(state, bookmarkDetail, closestKey) + state = syncUtil.updateObjectCache(state, bookmarkDetail, STATE_SITES.BOOKMARKS) + textCalc.calcText(bookmarkDetail, siteTags.BOOKMARK) } state = bookmarkUtil.updateActiveTabBookmarked(state) @@ -54,8 +63,15 @@ const bookmarksReducer = (state, action, immutableAction) => { break } - state = bookmarksState.editBookmark(state, key, bookmark) + const oldBookmark = bookmarksState.getBookmark(state, key) + if (oldBookmark.isEmpty()) { + break + } + + const bookmarkDetail = bookmarkUtil.buildEditBookmark(oldBookmark, bookmark) + state = bookmarksState.editBookmark(state, oldBookmark, bookmarkDetail) state = syncUtil.updateObjectCache(state, bookmark, STATE_SITES.BOOKMARKS) + textCalc.calcText(bookmarkDetail, siteTags.BOOKMARK) state = bookmarkUtil.updateActiveTabBookmarked(state) break @@ -78,6 +94,10 @@ const bookmarksReducer = (state, action, immutableAction) => { const destinationDetail = bookmarksState.getBookmark(state, action.get('destinationKey')) state = syncUtil.updateObjectCache(state, destinationDetail, STATE_SITES.BOOKMARKS) + + if (destinationDetail.get('parentFolderId') === 0 || action.get('destinationKey') === 0) { + state = bookmarkToolbarState.setToolbars(state) + } break } case appConstants.APP_REMOVE_BOOKMARK: @@ -91,12 +111,36 @@ const bookmarksReducer = (state, action, immutableAction) => { action.get('bookmarkKey', Immutable.List()).forEach((key) => { state = bookmarksState.removeBookmark(state, key) }) + state = bookmarkToolbarState.setToolbars(state) } else { + const bookmark = bookmarksState.getBookmark(state, bookmarkKey) state = bookmarksState.removeBookmark(state, bookmarkKey) + if (bookmark.get('parentFolderId') === 0) { + state = bookmarkToolbarState.setToolbars(state) + } } state = bookmarkUtil.updateActiveTabBookmarked(state) break } + case appConstants.APP_ON_BOOKMARK_WIDTH_CHANGED: + { + if (action.get('bookmarkList', Immutable.List()).isEmpty()) { + break + } + + let updateToolbar = false + action.get('bookmarkList').forEach(item => { + state = bookmarksState.setWidth(state, item.get('key'), item.get('width')) + if (item.get('parentFolderId') === 0) { + updateToolbar = true + } + }) + + if (updateToolbar) { + state = bookmarkToolbarState.setToolbars(state) + } + break + } } return state diff --git a/app/browser/reducers/windowsReducer.js b/app/browser/reducers/windowsReducer.js index 394b0fff60a..d93fbc4fa45 100644 --- a/app/browser/reducers/windowsReducer.js +++ b/app/browser/reducers/windowsReducer.js @@ -7,9 +7,15 @@ const path = require('path') const assert = require('assert') const Immutable = require('immutable') + +// Constants const appConstants = require('../../../js/constants/appConstants') const windowConstants = require('../../../js/constants/windowConstants') + +// State const windowState = require('../../common/state/windowState') + +// Utils const windows = require('../windows') const sessionStoreShutdown = require('../../sessionStoreShutdown') const {makeImmutable, isImmutable} = require('../../common/state/immutableUtil') @@ -341,8 +347,14 @@ const windowsReducer = (state, action, immutableAction) => { windows.cleanupWindow(windowId) break case appConstants.APP_WINDOW_CREATED: - state = windowState.maybeCreateWindow(state, action) - break + case appConstants.APP_WINDOW_RESIZED: + { + const bookmarkToolbarState = require('../../common/state/bookmarkToolbarState') + state = windowState.maybeCreateWindow(state, action) + const windowId = action.getIn(['windowValue', 'windowId'], windowState.WINDOW_ID_NONE) + state = bookmarkToolbarState.setToolbar(state, windowId) + break + } case appConstants.APP_TAB_STRIP_EMPTY: windows.closeWindow(action.get('windowId')) break diff --git a/app/browser/tabs.js b/app/browser/tabs.js index 41e986e264d..5affb1fd8ea 100644 --- a/app/browser/tabs.js +++ b/app/browser/tabs.js @@ -337,6 +337,29 @@ const createNavigationState = (navigationHandle, controller) => { }) } +let backgroundProcess = null +let backgroundProcessTimer = null +/** + * Execute script in the browser tab + * @param win{object} - window in which we want to execute script + * @param debug{boolean} - would you like to close window or not + * @param script{string} - script that you want to execute + * @param cb{function} - function that we call after script is completed + */ +const runScript = (win, debug, script, cb) => { + win.webContents.executeScriptInTab(config.braveExtensionId, script, {}, (err, url, result) => { + cb(err, url, result) + if (!debug) { + backgroundProcessTimer = setTimeout(() => { + if (backgroundProcess) { + win.close() + backgroundProcess = null + } + }, 2 * 60 * 1000) // 2 min + } + }) +} + const api = { init: (state, action) => { process.on('open-url-from-tab', (e, source, targetUrl, disposition) => { @@ -765,20 +788,32 @@ const api = { }) }, - executeScriptInBackground: (script, cb) => { - const win = new BrowserWindow({ - show: false, - webPreferences: { - partition: 'default' - } - }) - win.webContents.on('did-finish-load', (e) => { - win.webContents.executeScriptInTab(config.braveExtensionId, script, {}, (err, url, result) => { - cb(err, url, result) - setImmediate(() => win.close()) + /** + * Execute script in the background browser window + * @param script{string} - script that we want to run + * @param cb{function} - function that we want to call when script is done + * @param debug{boolean} - would you like to keep browser window when script is done + */ + executeScriptInBackground: (script, cb, debug = false) => { + if (backgroundProcessTimer) { + clearTimeout(backgroundProcessTimer) + } + + if (backgroundProcess === null) { + backgroundProcess = new BrowserWindow({ + show: debug, + webPreferences: { + partition: 'default' + } }) - }) - win.loadURL('about:blank') + + backgroundProcess.webContents.on('did-finish-load', () => { + runScript(backgroundProcess, debug, script, cb) + }) + backgroundProcess.loadURL('about:blank') + } else { + runScript(backgroundProcess, debug, script, cb) + } }, moveTo: (state, tabId, frameOpts, browserOpts, toWindowId) => { diff --git a/app/browser/windows.js b/app/browser/windows.js index 9ebb3ad535e..fe9f2d8a777 100644 --- a/app/browser/windows.js +++ b/app/browser/windows.js @@ -16,7 +16,6 @@ const messages = require('../../js/constants/messages') const settings = require('../../js/constants/settings') const windowState = require('../common/state/windowState') const pinnedSitesState = require('../common/state/pinnedSitesState') -const windowActions = require('../../js/actions/windowActions') // TODO(bridiver) - set window uuid let currentWindows = {} @@ -56,8 +55,14 @@ const getWindowValue = (windowId) => { const updateWindow = (windowId, updateDefault = false) => { const windowValue = getWindowValue(windowId) if (windowValue) { - appActions.windowUpdated(windowValue, updateDefault) - windowActions.onWindowUpdate(windowId, windowValue) + appActions.windowUpdated(windowValue, updateDefault, windowId) + } +} + +const onWindowResize = (windowId) => { + const windowValue = getWindowValue(windowId) + if (windowValue) { + appActions.onWindowResize(windowValue, windowId) } } @@ -107,6 +112,7 @@ const api = { let windowId = -1 const updateWindowMove = debounce(updateWindow, 100) const updateWindowDebounce = debounce(updateWindow, 5) + const onWindowResizeDebounce = debounce(onWindowResize, 5) win.once('initialized', () => { windowId = win.id @@ -181,7 +187,7 @@ const api = { LocalShortcuts.register(win) - appActions.windowCreated(windowValue) + appActions.windowCreated(windowValue, windowId) }) win.once('closed', () => { }) @@ -211,7 +217,7 @@ const api = { updateWindowDebounce(windowId) }) win.on('resize', () => { - updateWindowDebounce(windowId, true) + onWindowResizeDebounce(windowId) }) win.on('move', () => { updateWindowMove(windowId, true) diff --git a/app/common/lib/bookmarkFoldersUtil.js b/app/common/lib/bookmarkFoldersUtil.js index 8debb82caf1..9d8d4578cee 100644 --- a/app/common/lib/bookmarkFoldersUtil.js +++ b/app/common/lib/bookmarkFoldersUtil.js @@ -2,6 +2,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const siteTags = require('../../../js/constants/siteTags') +const {makeImmutable} = require('../state/immutableUtil') const isFolderNameValid = (title) => { return title != null && title.trim().length > 0 @@ -26,7 +27,7 @@ const getNextFolderIdItem = (folders) => { } const getNextFolderId = (folders) => { - const defaultFolderId = 0 + const defaultFolderId = 1 if (!folders) { return defaultFolderId } @@ -117,11 +118,32 @@ const isMoveAllowed = (folderList, sourceDetail, destinationDetail) => { return true } +const buildFolder = (folderDetails, folders) => { + folderDetails = makeImmutable(folderDetails) + let key = folderDetails.get('folderId') + + if (!folderDetails.has('folderId')) { + key = getNextFolderId(folders) + } + + return makeImmutable({ + title: folderDetails.get('title'), + folderId: Number(key), + key: key.toString(), + parentFolderId: ~~folderDetails.get('parentFolderId', 0), + partitionNumber: Number(folderDetails.get('partitionNumber', 0)), + objectId: folderDetails.get('objectId', null), + type: siteTags.BOOKMARK_FOLDER, + skipSync: folderDetails.get('skipSync', null) + }) +} + module.exports = { isFolderNameValid, getNextFolderId, getNextFolderName, isFolder, getKey, - isMoveAllowed + isMoveAllowed, + buildFolder } diff --git a/app/common/lib/bookmarkToolbarUtil.js b/app/common/lib/bookmarkToolbarUtil.js new file mode 100644 index 00000000000..872338cd6e2 --- /dev/null +++ b/app/common/lib/bookmarkToolbarUtil.js @@ -0,0 +1,93 @@ +/* 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') + +// Utils +const bookmarkUtil = require('../lib/bookmarkUtil') +const bookmarkFoldersUtil = require('../lib/bookmarkFoldersUtil') + +// Styles +const globalStyles = require('../../renderer/components/styles/global') +const {iconSize} = require('../../../js/constants/config') +const maxWidth = parseInt(globalStyles.spacing.bookmarksItemMaxWidth, 10) +const padding = parseInt(globalStyles.spacing.bookmarksItemPadding, 10) * 2 +const itemMargin = parseInt(globalStyles.spacing.bookmarksItemMargin, 10) +const toolbarPadding = parseInt(globalStyles.spacing.bookmarksToolbarPadding) +const overflowButtonWidth = parseInt(globalStyles.spacing.bookmarksToolbarOverflowButtonWidth, 10) +const chevronMargin = parseInt(globalStyles.spacing.bookmarksItemChevronMargin) +const chevronFontSize = parseInt(globalStyles.spacing.bookmarksItemChevronFontSize) +const chevronWidth = chevronMargin + chevronFontSize + +const getBookmarkKeys = (width, bookmarks) => { + if (bookmarks == null) { + return { + toolbar: Immutable.List(), + other: Immutable.List() + } + } + + let widthAccountedFor = 0 + + const onlyText = bookmarkUtil.showOnlyText() + const textAndFavicon = bookmarkUtil.showTextAndFavicon() + const onlyFavicon = bookmarkUtil.showOnlyFavicon() + + // No margin for show only fav icons + const margin = onlyFavicon ? 0 : (itemMargin * 2) + const maximumBookmarksToolbarWidth = width - overflowButtonWidth + + widthAccountedFor += toolbarPadding + + // Loop through until we fill up the entire bookmark toolbar width + let i = 0 + for (let item of bookmarks) { + let iconWidth + const isFolder = bookmarkFoldersUtil.isFolder(item) + + if (onlyText) { + iconWidth = 0 + } else if (textAndFavicon || isFolder) { + iconWidth = iconSize + itemMargin + } else if (onlyFavicon) { + iconWidth = iconSize + } + + let extraWidth = 0 + + if (onlyText) { + extraWidth = padding + item.get('width') + } else if (textAndFavicon) { + extraWidth = padding + iconWidth + item.get('width') + } else if (onlyFavicon) { + extraWidth = padding + iconWidth + + if (isFolder) { + extraWidth += item.get('width') + } + } + + if (isFolder) { + extraWidth += chevronWidth + } + + extraWidth = Math.min(extraWidth, maxWidth) + widthAccountedFor += extraWidth + margin + + if (widthAccountedFor >= maximumBookmarksToolbarWidth) { + break + } + + i++ + } + + return { + toolbar: bookmarks.take(i).map((item) => item.get('key')).toList(), + other: bookmarks.skip(i).take(100).map((item) => item.get('key')).toList() + } +} + +module.exports = { + getBookmarkKeys +} diff --git a/app/common/lib/bookmarkUtil.js b/app/common/lib/bookmarkUtil.js index 1fd21d9fdaa..05a3a089ca2 100644 --- a/app/common/lib/bookmarkUtil.js +++ b/app/common/lib/bookmarkUtil.js @@ -6,24 +6,21 @@ const Immutable = require('immutable') // State const bookmarksState = require('../state/bookmarksState') -const bookmarkFoldersState = require('../state/bookmarkFoldersState') const tabState = require('../state/tabState') +const historyState = require('../state/historyState') // Constants const dragTypes = require('../../../js/constants/dragTypes') const {bookmarksToolbarMode} = require('../constants/settingsEnums') const settings = require('../../../js/constants/settings') const siteTags = require('../../../js/constants/siteTags') +const newTabData = require('../../../js/data/newTabData') // Utils const bookmarkLocationCache = require('../cache/bookmarkLocationCache') -const {calculateTextWidth} = require('../../../js/lib/textCalculator') -const {iconSize} = require('../../../js/constants/config') const {getSetting} = require('../../../js/settings') const UrlUtil = require('../../../js/lib/urlutil') - -// Styles -const globalStyles = require('../../renderer/components/styles/global') +const {makeImmutable} = require('../state/immutableUtil') const bookmarkHangerHeading = (editMode, isAdded) => { if (isAdded) { @@ -67,113 +64,6 @@ const getDNDBookmarkData = (state, bookmarkKey) => { return data.get('draggingOverKey') === bookmarkKey ? data : Immutable.Map() } -let oldBookmarks -let oldBookmarkOrderCache -let oldFolders -let lastValue -let lastWidth -const getToolbarBookmarks = (state) => { - const windowWidth = window.innerWidth - const allBookmarks = bookmarksState.getBookmarks(state) - const bookmarkOrderCache = bookmarksState.getBookmarkOrder(state) - const allFolders = bookmarkFoldersState.getFolders(state) - if ( - allBookmarks === oldBookmarks && - bookmarkOrderCache === oldBookmarkOrderCache && - allFolders === oldFolders && - lastWidth === windowWidth && - lastValue - ) { - return lastValue - } - - oldBookmarks = allBookmarks - oldBookmarkOrderCache = bookmarkOrderCache - oldFolders = allFolders - lastWidth = windowWidth - - const bookmarks = bookmarksState.getBookmarksWithFolders(state) - let widthAccountedFor = 0 - - const onlyText = showOnlyText() - const textAndFavicon = showTextAndFavicon() - const onlyFavicon = showOnlyFavicon() - - const maxWidth = parseInt(globalStyles.spacing.bookmarksItemMaxWidth, 10) - const padding = parseInt(globalStyles.spacing.bookmarksItemPadding, 10) * 2 - const bookmarkItemMargin = parseInt(globalStyles.spacing.bookmarksItemMargin, 10) * 2 - const fontSize = parseInt(globalStyles.spacing.bookmarksItemFontSize) - const fontFamily = globalStyles.defaultFontFamily - const chevronMargin = parseInt(globalStyles.spacing.bookmarksItemChevronMargin) - const chevronFontSize = parseInt(globalStyles.spacing.bookmarksItemChevronFontSize) - const chevronWidth = chevronMargin + chevronFontSize - - // No margin for show only favicons - const margin = onlyFavicon ? 0 : bookmarkItemMargin - - // Toolbar padding is only on the left - const toolbarPadding = parseInt(globalStyles.spacing.bookmarksToolbarPadding) - - const overflowButtonWidth = parseInt(globalStyles.spacing.bookmarksToolbarOverflowButtonWidth, 10) - const maximumBookmarksToolbarWidth = windowWidth - overflowButtonWidth - - widthAccountedFor += toolbarPadding - - // Loop through until we fill up the entire bookmark toolbar width - let i = 0 - for (let bookmark of bookmarks) { - let iconWidth - - if (onlyText) { - iconWidth = 0 - } else if (textAndFavicon || bookmark.get('folderId')) { - iconWidth = iconSize + parseInt(globalStyles.spacing.bookmarksItemMargin, 10) - } else if (onlyFavicon) { - iconWidth = iconSize - } - - const currentChevronWidth = bookmark.get('folderId') ? chevronWidth : 0 - const text = bookmark.get('title') || bookmark.get('location') - let extraWidth - - if (onlyText) { - extraWidth = padding + calculateTextWidth(text, `${fontSize} ${fontFamily}`) - - if (bookmark.get('folderId')) { - extraWidth += currentChevronWidth - } - } else if (textAndFavicon) { - extraWidth = padding + iconWidth + calculateTextWidth(text, `${fontSize} ${fontFamily}`) + currentChevronWidth - } else if (onlyFavicon) { - extraWidth = padding + iconWidth + currentChevronWidth - - if (bookmark.get('folderId')) { - extraWidth += calculateTextWidth(text, `${fontSize} ${fontFamily}`) - } - } - - extraWidth = Math.min(extraWidth, maxWidth) - widthAccountedFor += extraWidth + margin - - if (widthAccountedFor >= maximumBookmarksToolbarWidth) { - widthAccountedFor -= extraWidth + margin - i-- - - break - } - - i++ - } - - lastValue = { - visibleBookmarks: bookmarks.take(i).map((item) => item.get('key')).toList(), - // Show at most 100 items in the overflow menu - hiddenBookmarks: bookmarks.skip(i).take(100).map((item) => item.get('key')).toList() - } - - return lastValue -} - const getDetailFromFrame = (frame) => { if (frame == null) { return null @@ -264,18 +154,87 @@ const getKey = (siteDetail) => { return null } +const buildBookmark = (state, bookmarkDetail) => { + bookmarkDetail = makeImmutable(bookmarkDetail) + let location + if (bookmarkDetail.has('location')) { + location = UrlUtil.getLocationIfPDF(bookmarkDetail.get('location')) + bookmarkDetail = bookmarkDetail.set('location', location) + } + + const key = getKey(bookmarkDetail) + const historyKey = key.slice(0, -2) + let dataItem = historyState.getSite(state, historyKey) + + if (dataItem.isEmpty()) { + // check if we have data in tabs + const tab = tabState.getActiveTab(state) || Immutable.Map() + const activeLocation = tab.get('url') || tab.getIn(['frame', 'location']) + + if (!tab.isEmpty() && bookmarkDetail.get('location') === activeLocation) { + dataItem = makeImmutable({ + partitionNumber: tab.getIn(['frame', 'partitionNumber'], 0), + favicon: tab.getIn(['frame', 'icon']), + themeColor: tab.getIn(['frame', 'themeColor']) + }) + } else { + // check if bookmark is in top sites + const topSites = Immutable.fromJS(newTabData.topSites.concat(newTabData.pinnedTopSites)) + const topSite = topSites.find(site => site.get('location') === bookmarkDetail.get('location')) || Immutable.Map() + + if (!topSite.isEmpty()) { + dataItem = topSite + } + } + } + + return makeImmutable({ + title: bookmarkDetail.get('title', ''), + location: bookmarkDetail.get('location'), + parentFolderId: ~~bookmarkDetail.get('parentFolderId', 0), + partitionNumber: Number(dataItem.get('partitionNumber', 0)), + objectId: bookmarkDetail.get('objectId', null), + favicon: dataItem.get('favicon'), + themeColor: dataItem.get('themeColor'), + type: siteTags.BOOKMARK, + key: key, + skipSync: bookmarkDetail.get('skipSync', null), + width: 0 + }) +} + +const buildEditBookmark = (oldBookmark, bookmarkDetail) => { + let newBookmark = oldBookmark.merge(bookmarkDetail) + + let location + if (newBookmark.has('location')) { + location = UrlUtil.getLocationIfPDF(newBookmark.get('location')) + newBookmark = newBookmark.set('location', location) + } + + const newKey = getKey(newBookmark) + if (newKey === null) { + return oldBookmark + } + + return newBookmark.set('key', newKey) +} + module.exports = { bookmarkHangerHeading, isBookmarkNameValid, showOnlyFavicon, showFavicon, getDNDBookmarkData, - getToolbarBookmarks, getDetailFromFrame, isLocationBookmarked, toCreateProperties, isBookmark, updateTabBookmarked, updateActiveTabBookmarked, - getKey + getKey, + showOnlyText, + showTextAndFavicon, + buildBookmark, + buildEditBookmark } diff --git a/app/common/lib/historyUtil.js b/app/common/lib/historyUtil.js index 7a1bfc6e2e5..2145c5c61fe 100644 --- a/app/common/lib/historyUtil.js +++ b/app/common/lib/historyUtil.js @@ -78,7 +78,7 @@ const prepareHistoryEntry = (siteDetail) => { objectId: siteDetail.get('objectId', null), title: siteDetail.get('title'), location: siteDetail.get('location'), - partitionNumber: ~~siteDetail.get('partitionNumber', 0), + partitionNumber: Number(siteDetail.get('partitionNumber', 0)), count: 1, themeColor: siteDetail.get('themeColor'), favicon: siteDetail.get('favicon', siteDetail.get('icon')), @@ -98,8 +98,8 @@ const mergeSiteDetails = (oldDetail, newDetail) => { objectId, title: newDetail.get('title'), location: newDetail.get('location'), - partitionNumber: ~~newDetail.get('partitionNumber', 0), - count: ~~oldDetail.get('count', 0) + 1 + partitionNumber: Number(newDetail.get('partitionNumber', 0)), + count: Number(oldDetail.get('count', 0)) + 1 }) const themeColor = newDetail.has('themeColor') ? newDetail.get('themeColor') : oldDetail.get('themeColor') diff --git a/app/common/state/bookmarkFoldersState.js b/app/common/state/bookmarkFoldersState.js index 49046a08a03..618e7cc2938 100644 --- a/app/common/state/bookmarkFoldersState.js +++ b/app/common/state/bookmarkFoldersState.js @@ -49,27 +49,9 @@ const bookmarkFoldersState = { addFolder: (state, folderDetails, destinationKey) => { state = validateState(state) - folderDetails = makeImmutable(folderDetails) - let folders = bookmarkFoldersState.getFolders(state) - let key = folderDetails.get('folderId') - if (!folderDetails.has('folderId')) { - key = bookmarkFoldersUtil.getNextFolderId(folders) - } - - const newFolder = makeImmutable({ - title: folderDetails.get('title'), - folderId: ~~key, - key: key.toString(), - parentFolderId: ~~folderDetails.get('parentFolderId', 0), - partitionNumber: ~~folderDetails.get('partitionNumber', 0), - objectId: folderDetails.get('objectId', null), - type: siteTags.BOOKMARK_FOLDER, - skipSync: folderDetails.get('skipSync', null) - }) - - state = state.setIn([STATE_SITES.BOOKMARK_FOLDERS, key.toString()], newFolder) - state = bookmarkOrderCache.addFolderToCache(state, newFolder.get('parentFolderId'), key, destinationKey) + state = state.setIn([STATE_SITES.BOOKMARK_FOLDERS, folderDetails.get('key')], folderDetails) + state = bookmarkOrderCache.addFolderToCache(state, folderDetails.get('parentFolderId'), folderDetails.get('key'), destinationKey) return state }, @@ -184,6 +166,10 @@ const bookmarkFoldersState = { append ) return state + }, + + setWidth: (state, key, width) => { + return state.setIn([STATE_SITES.BOOKMARK_FOLDERS, key, 'width'], parseFloat(width)) } } diff --git a/app/common/state/bookmarkToolbarState.js b/app/common/state/bookmarkToolbarState.js new file mode 100644 index 00000000000..9b10bcc577e --- /dev/null +++ b/app/common/state/bookmarkToolbarState.js @@ -0,0 +1,72 @@ +/* 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') + +// State +const bookmarksState = require('./bookmarksState') +const windowState = require('./windowState') + +// Utils +const {makeImmutable, isList, isMap} = require('./immutableUtil') +const bookmarkToolbarUtil = require('../lib/bookmarkToolbarUtil') + +const validateState = function (state) { + state = makeImmutable(state) + assert.ok(isMap(state), 'state must be an Immutable.Map') + assert.ok(isList(state.get('windows'), 'state must contain an Immutable.List of windows')) + return state +} + +const bookmarkToolbarState = { + setToolbars: (state) => { + validateState(state) + const bookmarks = bookmarksState.getBookmarksWithFolders(state, 0) + + state.get('windows').forEach((item, index) => { + const width = state.getIn(['windows', index, 'width']) + const data = bookmarkToolbarUtil.getBookmarkKeys(width, bookmarks) + + if (!state.hasIn(['windows', index])) { + return state + } + + state = state + .setIn(['windows', index, 'bookmarksToolbar', 'toolbar'], data.toolbar) + .setIn(['windows', index, 'bookmarksToolbar', 'other'], data.other) + }) + + return state + }, + + setToolbar: (state, windowId) => { + validateState(state) + const bookmarks = bookmarksState.getBookmarksWithFolders(state, 0) + const windowIndex = windowState.getWindowIndexByWindowId(state, windowId) + + if (!state.hasIn(['windows', windowIndex])) { + return state + } + + const width = state.getIn(['windows', windowIndex, 'width']) + const data = bookmarkToolbarUtil.getBookmarkKeys(width, bookmarks) + + return state + .setIn(['windows', windowIndex, 'bookmarksToolbar', 'toolbar'], data.toolbar) + .setIn(['windows', windowIndex, 'bookmarksToolbar', 'other'], data.other) + }, + + getToolbar: (state, windowId) => { + const index = windowState.getWindowIndexByWindowId(state, windowId) + return state.getIn(['windows', index, 'bookmarksToolbar', 'toolbar'], Immutable.List()) + }, + + getOther: (state, windowId) => { + const index = windowState.getWindowIndexByWindowId(state, windowId) + return state.getIn(['windows', index, 'bookmarksToolbar', 'other'], Immutable.List()) + } +} + +module.exports = bookmarkToolbarState diff --git a/app/common/state/bookmarksState.js b/app/common/state/bookmarksState.js index 7bee24e27e7..949ca247748 100644 --- a/app/common/state/bookmarksState.js +++ b/app/common/state/bookmarksState.js @@ -9,13 +9,10 @@ const Immutable = require('immutable') const settings = require('../../../js/constants/settings') const siteTags = require('../../../js/constants/siteTags') const {STATE_SITES} = require('../../../js/constants/stateConstants') -const newTabData = require('../../../js/data/newTabData') // State -const historyState = require('./historyState') const bookmarkOrderCache = require('../cache/bookmarkOrderCache') const bookmarkFoldersState = require('./bookmarkFoldersState') -const tabState = require('./tabState') // Actions const syncActions = require('../../../js/actions/syncActions') @@ -82,99 +79,36 @@ const bookmarksState = { addBookmark: (state, bookmarkDetail, destinationKey) => { state = validateState(state) - const bookmarkUtil = require('../lib/bookmarkUtil') - - bookmarkDetail = makeImmutable(bookmarkDetail) - let location - if (bookmarkDetail.has('location')) { - location = UrlUtil.getLocationIfPDF(bookmarkDetail.get('location')) - bookmarkDetail = bookmarkDetail.set('location', location) - } - - const key = bookmarkUtil.getKey(bookmarkDetail) - const historyKey = key.slice(0, -2) - let dataItem = historyState.getSite(state, historyKey) - - if (dataItem.isEmpty()) { - // check if we have data in tabs - const tab = tabState.getActiveTab(state) || Immutable.Map() - const activeLocation = tab.get('url') || tab.getIn(['frame', 'location']) - - if (!tab.isEmpty() && bookmarkDetail.get('location') === activeLocation) { - dataItem = makeImmutable({ - partitionNumber: tab.getIn(['frame', 'partitionNumber'], 0), - favicon: tab.getIn(['frame', 'icon']), - themeColor: tab.getIn(['frame', 'themeColor']) - }) - } else { - // check if bookmark is in top sites - const topSites = Immutable.fromJS(newTabData.topSites.concat(newTabData.pinnedTopSites)) - const topSite = topSites.find(site => site.get('location') === bookmarkDetail.get('location')) || Immutable.Map() - - if (!topSite.isEmpty()) { - dataItem = topSite - } - } - } - - let bookmark = makeImmutable({ - title: bookmarkDetail.get('title', ''), - location: bookmarkDetail.get('location'), - parentFolderId: ~~bookmarkDetail.get('parentFolderId', 0), - partitionNumber: ~~dataItem.get('partitionNumber', 0), - objectId: bookmarkDetail.get('objectId', null), - favicon: dataItem.get('favicon'), - themeColor: dataItem.get('themeColor'), - type: siteTags.BOOKMARK, - key: key, - skipSync: bookmarkDetail.get('skipSync', null) - }) - + const key = bookmarkDetail.get('key') if (key === null) { return state } if (!state.hasIn([STATE_SITES.BOOKMARKS, key])) { - state = bookmarkLocationCache.addCacheKey(state, location, key) - state = bookmarkOrderCache.addBookmarkToCache(state, bookmark.get('parentFolderId'), key, destinationKey) + state = bookmarkLocationCache.addCacheKey(state, bookmarkDetail.get('location'), key) + state = bookmarkOrderCache.addBookmarkToCache(state, bookmarkDetail.get('parentFolderId'), key, destinationKey) } - state = state.setIn([STATE_SITES.BOOKMARKS, key], bookmark) + state = state.setIn([STATE_SITES.BOOKMARKS, key], bookmarkDetail) return state }, - editBookmark: (state, editKey, bookmarkDetail) => { + editBookmark: (state, oldBookmark, bookmarkDetail) => { state = validateState(state) - const bookmarkUtil = require('../lib/bookmarkUtil') - - const oldBookmark = bookmarksState.getBookmark(state, editKey) - - if (oldBookmark.isEmpty()) { - return state - } - let newBookmark = oldBookmark.merge(bookmarkDetail) - - let location - if (newBookmark.has('location')) { - location = UrlUtil.getLocationIfPDF(newBookmark.get('location')) - newBookmark = newBookmark.set('location', location) - } - const newKey = bookmarkUtil.getKey(newBookmark) - if (newKey === null) { - return state - } + const newKey = bookmarkDetail.get('key') + const editKey = oldBookmark.get('key') if (editKey !== newKey) { state = state.deleteIn([STATE_SITES.BOOKMARKS, editKey]) state = bookmarkOrderCache.removeCacheKey(state, oldBookmark.get('parentFolderId'), editKey) - state = bookmarkOrderCache.addBookmarkToCache(state, newBookmark.get('parentFolderId'), newKey) - newBookmark = newBookmark.set('key', newKey) + state = bookmarkOrderCache.addBookmarkToCache(state, bookmarkDetail.get('parentFolderId'), newKey) + bookmarkDetail = bookmarkDetail.set('key', newKey) } - state = state.setIn([STATE_SITES.BOOKMARKS, newKey], newBookmark) + state = state.setIn([STATE_SITES.BOOKMARKS, newKey], bookmarkDetail) state = bookmarkLocationCache.removeCacheKey(state, oldBookmark.get('location'), editKey) - state = bookmarkLocationCache.addCacheKey(state, location, newKey) + state = bookmarkLocationCache.addCacheKey(state, bookmarkDetail.get('location'), newKey) return state }, @@ -313,6 +247,10 @@ const bookmarksState = { const cache = bookmarkOrderCache.getBookmarksByParentId(state, folderKey) return cache.map((item) => bookmarksState.getBookmark(state, item.get('key'))) + }, + + setWidth: (state, key, width) => { + return state.setIn([STATE_SITES.BOOKMARKS, key, 'width'], parseFloat(width)) } } diff --git a/app/renderer/components/bookmarks/bookmarksToolbar.js b/app/renderer/components/bookmarks/bookmarksToolbar.js index 79c81e65e44..c9b66ddd92e 100644 --- a/app/renderer/components/bookmarks/bookmarksToolbar.js +++ b/app/renderer/components/bookmarks/bookmarksToolbar.js @@ -18,6 +18,7 @@ const windowActions = require('../../../../js/actions/windowActions') // State const windowState = require('../../../common/state/windowState') +const bookmarkToolbarState = require('../../../common/state/bookmarkToolbarState') // Constants const dragTypes = require('../../../../js/constants/dragTypes') @@ -33,6 +34,7 @@ const isWindows = require('../../../common/lib/platformUtil').isWindows() const frameStateUtil = require('../../../../js/state/frameStateUtil') const bookmarkUtil = require('../../../common/lib/bookmarkUtil') const {elementHasDataset} = require('../../../../js/lib/eventUtil') +const {getCurrentWindowId} = require('../../currentWindow') // Styles const globalStyles = require('../styles/global') @@ -147,7 +149,7 @@ class BookmarksToolbar extends React.Component { mergeProps (state, ownProps) { const currentWindow = state.get('currentWindow') const activeFrame = frameStateUtil.getActiveFrame(currentWindow) || Immutable.Map() - const bookmarks = bookmarkUtil.getToolbarBookmarks(state) + const currentWindowId = getCurrentWindowId() const props = {} // used in renderer @@ -155,8 +157,8 @@ class BookmarksToolbar extends React.Component { props.showFavicon = bookmarkUtil.showFavicon() props.shouldAllowWindowDrag = windowState.shouldAllowWindowDrag(state, currentWindow, activeFrame, isFocused(state)) && !isWindows - props.visibleBookmarks = bookmarks.visibleBookmarks - props.hiddenBookmarks = bookmarks.hiddenBookmarks + props.visibleBookmarks = bookmarkToolbarState.getToolbar(state, currentWindowId) + props.hiddenBookmarks = bookmarkToolbarState.getOther(state, currentWindowId) // used in other functions props.activeFrameKey = activeFrame.get('key') diff --git a/docs/state.md b/docs/state.md index a844668dfe6..5e51d830c95 100644 --- a/docs/state.md +++ b/docs/state.md @@ -63,7 +63,8 @@ AppStore parentFolderId: number, partitionNumber: number, // optionally specifies a specific session skipSync: boolean, - title: string + title: string, + width: float // bookmark text width } }, bookmarkFolders: { @@ -74,7 +75,8 @@ AppStore 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 + title: string, + width: float // bookmark folder text width } }, cache: { @@ -391,6 +393,10 @@ AppStore }, windows: [{ // persistent properties + bookmarksToolbar: { + toolbar: Array, // bookmark and folder keys that we want to display + other: Array // bookmark and folder keys that we display in more menu (limited to 100) + }, focused: boolean, height: number, left: number, diff --git a/js/actions/appActions.js b/js/actions/appActions.js index 2551fbf22df..93a494b6e42 100644 --- a/js/actions/appActions.js +++ b/js/actions/appActions.js @@ -62,18 +62,24 @@ const appActions = { }) }, - windowCreated: function (windowValue) { + windowCreated: function (windowValue, windowId) { dispatch({ actionType: appConstants.APP_WINDOW_CREATED, - windowValue + windowValue, + queryInfo: { + windowId + } }) }, - windowUpdated: function (windowValue, updateDefault) { + windowUpdated: function (windowValue, updateDefault, windowId) { dispatch({ actionType: appConstants.APP_WINDOW_UPDATED, windowValue, - updateDefault + updateDefault, + queryInfo: { + windowId + } }) }, @@ -1646,6 +1652,43 @@ const appActions = { destinationKey, prepend }) + }, + + /** + * Dispatches a message that bookmark calculation was done + * @param bookmarkList {Object} - Object is a list of bookmarks with key, width and parentFolderId as a property + */ + onBookmarkWidthChanged: function (bookmarkList) { + dispatch({ + actionType: appConstants.APP_ON_BOOKMARK_WIDTH_CHANGED, + bookmarkList + }) + }, + + /** + * Dispatches a message that bookmark calculation was done + * @param folderList {Object} - Object is a list of folders with key, width and parentFolderId as a property + */ + onBookmarkFolderWidthChanged: function (folderList) { + dispatch({ + actionType: appConstants.APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED, + folderList + }) + }, + + /** + * Dispatches a message that window was resized + * @param windowValue - window properties + * @param windowId - id of the window that we want to update + */ + onWindowResize: function (windowValue, windowId) { + dispatch({ + actionType: appConstants.APP_WINDOW_RESIZED, + windowValue, + queryInfo: { + windowId + } + }) } } diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js index 12efd70375e..b25af0c3b7e 100644 --- a/js/actions/windowActions.js +++ b/js/actions/windowActions.js @@ -1189,16 +1189,6 @@ const windowActions = { bookmarkKey, type }) - }, - - onWindowUpdate: function (windowId, windowValue) { - dispatch({ - actionType: windowConstants.WINDOW_ON_WINDOW_UPDATE, - queryInfo: { - windowId - }, - windowValue - }) } } diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js index 201d4fbb34b..36e646766df 100644 --- a/js/constants/appConstants.js +++ b/js/constants/appConstants.js @@ -154,7 +154,10 @@ const appConstants = { APP_MOVE_BOOKMARK_FOLDER: _, APP_MOVE_BOOKMARK: _, APP_ENABLE_PEPPER_MENU: _, - APP_INSPECT_ELEMENT: _ + APP_INSPECT_ELEMENT: _, + APP_ON_BOOKMARK_WIDTH_CHANGED: _, + APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED: _, + APP_WINDOW_RESIZED: _ } module.exports = mapValuesByKeys(appConstants) diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js index 34241d7861d..141ae5eaeea 100644 --- a/js/constants/windowConstants.js +++ b/js/constants/windowConstants.js @@ -108,8 +108,7 @@ const windowConstants = { WINDOW_ON_BOOKMARK_ADDED: _, WINDOW_ON_ADD_BOOKMARK_FOLDER: _, WINDOW_ON_EDIT_BOOKMARK_FOLDER: _, - WINDOW_ON_BOOKMARK_FOLDER_CLOSE: _, - WINDOW_ON_WINDOW_UPDATE: _ + WINDOW_ON_BOOKMARK_FOLDER_CLOSE: _ } module.exports = mapValuesByKeys(windowConstants) diff --git a/js/lib/textCalculator.js b/js/lib/textCalculator.js deleted file mode 100644 index c543d340b83..00000000000 --- a/js/lib/textCalculator.js +++ /dev/null @@ -1,9 +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/. */ - -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/stores/appStore.js b/js/stores/appStore.js index 91626bdd9a9..35f7fa77b42 100644 --- a/js/stores/appStore.js +++ b/js/stores/appStore.js @@ -34,6 +34,7 @@ const profiles = require('../../app/browser/profiles') const {zoomLevel} = require('../../app/common/constants/toolbarUserInterfaceScale') const {HrtimeLogger} = require('../../app/common/lib/logUtil') const platformUtil = require('../../app/common/lib/platformUtil') +const urlUtil = require('../lib/urlutil') // state helpers const {makeImmutable} = require('../../app/common/state/immutableUtil') @@ -45,7 +46,7 @@ const tabState = require('../../app/common/state/tabState') const bookmarksState = require('../../app/common/state/bookmarksState') const bookmarkFoldersState = require('../../app/common/state/bookmarkFoldersState') const historyState = require('../../app/common/state/historyState') -const urlUtil = require('../lib/urlutil') +const bookmarkToolbarState = require('../../app/common/state/bookmarkToolbarState') // Only used internally const CHANGE_EVENT = 'app-state-change' @@ -152,7 +153,11 @@ function handleChangeSettingAction (state, settingKey, settingValue) { }) state = state.setIn(['settings', settingKey], homeArray.join('|')) + break } + case settings.BOOKMARKS_TOOLBAR_MODE: + state = bookmarkToolbarState.setToolbars(state) + break } return state @@ -196,6 +201,7 @@ const handleAppAction = (action) => { require('../../app/browser/reducers/updatesReducer'), require('../../app/browser/reducers/topSitesReducer'), require('../../app/browser/reducers/braverySettingsReducer'), + require('../../app/browser/reducers/bookmarkToolbarReducer'), require('../../app/ledger').doAction, require('../../app/browser/menu') ] diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index b5205ff7576..7df5505bdb1 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -772,6 +772,10 @@ const doAction = (action) => { windowState = windowState.set('windowInfo', oldInfo.merge(windowValue)) break } + case appConstants.APP_WINDOW_UPDATED: + case appConstants.APP_WINDOW_RESIZED: + windowState = windowState.set('windowInfo', action.windowValue) + break default: break } diff --git a/test/bookmark-components/bookmarksTest.js b/test/bookmark-components/bookmarksTest.js index 6bbcb95a173..34379511264 100644 --- a/test/bookmark-components/bookmarksTest.js +++ b/test/bookmark-components/bookmarksTest.js @@ -317,7 +317,7 @@ describe('bookmark tests', function () { .activateURLMode() .waitForVisible(navigatorBookmarked) }) - it('on new active tabs', function * () { + it('on new non-active tabs', function * () { yield this.app.client .waitForVisible(navigatorNotBookmarked) .newTab({ url: this.page1Url, active: false }) diff --git a/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js b/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js index d58fdd573b7..f22238fccbe 100644 --- a/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js +++ b/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js @@ -7,6 +7,8 @@ const mockery = require('mockery') const Immutable = require('immutable') const assert = require('assert') const sinon = require('sinon') +const fakeElectron = require('../../../lib/fakeElectron') +const fakeAdBlock = require('../../../lib/fakeAdBlock') const appConstants = require('../../../../../js/constants/appConstants') const siteTags = require('../../../../../js/constants/siteTags') @@ -14,9 +16,19 @@ const {STATE_SITES} = require('../../../../../js/constants/stateConstants') require('../../../braveUnit') describe('bookmarkFoldersReducer unit test', function () { - let bookmarkFoldersReducer, bookmarkFoldersState + let bookmarkFoldersReducer, bookmarkFoldersState, bookmarkToolbarState const state = Immutable.fromJS({ + windows: [ + { + windowId: 1, + width: 80, + bookmarksToolbar: { + toolbar: [], + other: [] + } + } + ], bookmarks: {}, bookmarkFolders: {}, cache: { @@ -25,6 +37,16 @@ describe('bookmarkFoldersReducer unit test', function () { }) const stateWithData = Immutable.fromJS({ + windows: [ + { + windowId: 1, + width: 80, + bookmarksToolbar: { + toolbar: [], + other: [] + } + } + ], bookmarks: {}, bookmarkFolders: { '1': { @@ -44,6 +66,15 @@ describe('bookmarkFoldersReducer unit test', function () { partitionNumber: 0, objectId: null, type: siteTags.BOOKMARK_FOLDER + }, + '80': { + title: 'folder80', + folderId: 80, + key: '80', + parentFolderId: 69, + partitionNumber: 0, + objectId: null, + type: siteTags.BOOKMARK_FOLDER } }, cache: { @@ -59,19 +90,35 @@ describe('bookmarkFoldersReducer unit test', function () { order: 1, type: siteTags.BOOKMARK_FOLDER } + ], + '69': [ + { + key: '80', + order: 0, + type: siteTags.BOOKMARK_FOLDER + } ] } } }) + const fakeTextCalc = { + calcText: () => true, + calcTextList: () => true + } + before(function () { mockery.enable({ warnOnReplace: false, warnOnUnregistered: false, useCleanCache: true }) + mockery.registerMock('electron', fakeElectron) + mockery.registerMock('ad-block', fakeAdBlock) + mockery.registerMock('../../browser/api/textCalc', fakeTextCalc) bookmarkFoldersReducer = require('../../../../../app/browser/reducers/bookmarkFoldersReducer') bookmarkFoldersState = require('../../../../../app/common/state/bookmarkFoldersState') + bookmarkToolbarState = require('../../../../../app/common/state/bookmarkToolbarState') }) after(function () { @@ -79,23 +126,27 @@ describe('bookmarkFoldersReducer unit test', function () { }) describe('APP_ADD_BOOKMARK_FOLDER', function () { - let spy + let spy, spyCalc afterEach(function () { spy.restore() + spyCalc.restore() }) it('null case', function () { spy = sinon.spy(bookmarkFoldersState, 'addFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarkFoldersReducer(state, { actionType: appConstants.APP_ADD_BOOKMARK_FOLDER }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(state, newState) }) it('folder data is map (single folder)', function () { spy = sinon.spy(bookmarkFoldersState, 'addFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarkFoldersReducer(state, { actionType: appConstants.APP_ADD_BOOKMARK_FOLDER, folderDetails: { @@ -126,11 +177,13 @@ describe('bookmarkFoldersReducer unit test', function () { ] })) assert.equal(spy.calledOnce, true) + assert.equal(spyCalc.calledOnce, true) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) it('folder data is list (multiple folders)', function () { spy = sinon.spy(bookmarkFoldersState, 'addFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcTextList') const newState = bookmarkFoldersReducer(state, { actionType: appConstants.APP_ADD_BOOKMARK_FOLDER, folderDetails: [ @@ -201,38 +254,45 @@ describe('bookmarkFoldersReducer unit test', function () { ] })) assert.equal(spy.callCount, 3) + assert.equal(spyCalc.callCount, 1) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) }) describe('APP_EDIT_BOOKMARK_FOLDER', function () { - let spy + let spy, spyCalc afterEach(function () { spy.restore() + spyCalc.restore() }) it('null case', function () { spy = sinon.spy(bookmarkFoldersState, 'editFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_EDIT_BOOKMARK_FOLDER }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(stateWithData, newState) }) it('folder data is missing', function () { spy = sinon.spy(bookmarkFoldersState, 'editFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_EDIT_BOOKMARK_FOLDER, editKey: '1' }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(stateWithData, newState) }) it('folder key is missing', function () { spy = sinon.spy(bookmarkFoldersState, 'editFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_EDIT_BOOKMARK_FOLDER, folderDetails: { @@ -241,11 +301,13 @@ describe('bookmarkFoldersReducer unit test', function () { } }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(stateWithData, newState) }) it('folder data is correct', function () { spy = sinon.spy(bookmarkFoldersState, 'editFolder') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_EDIT_BOOKMARK_FOLDER, folderDetails: { @@ -256,69 +318,94 @@ describe('bookmarkFoldersReducer unit test', function () { }) const expectedState = stateWithData.setIn([STATE_SITES.BOOKMARK_FOLDERS, '1', 'title'], 'folder1 new') assert.equal(spy.calledOnce, true) + assert.equal(spyCalc.calledOnce, true) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) }) describe('APP_MOVE_BOOKMARK_FOLDER', function () { - let spy + let spy, spyToolbar afterEach(function () { spy.restore() + spyToolbar.restore() }) it('null case', function () { spy = sinon.spy(bookmarkFoldersState, 'moveFolder') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarkFoldersReducer(state, { actionType: appConstants.APP_MOVE_BOOKMARK_FOLDER }) assert.equal(spy.notCalled, true) + assert.equal(spyToolbar.notCalled, true) assert.deepEqual(state, newState) }) it('check if move is working', function () { spy = sinon.spy(bookmarkFoldersState, 'moveFolder') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_MOVE_BOOKMARK_FOLDER, folderKey: '1', destinationKey: '69', append: true }) - const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '0'], Immutable.fromJS([ - { - key: '69', - order: 0, - type: siteTags.BOOKMARK_FOLDER - }, - { - key: '1', - order: 1, - type: siteTags.BOOKMARK_FOLDER - } - ])) + const expectedState = stateWithData + .setIn(['cache', 'bookmarkOrder', '0'], Immutable.fromJS([ + { + key: '69', + order: 0, + type: siteTags.BOOKMARK_FOLDER + }, + { + key: '1', + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ])) + .setIn(['windows', 0, 'bookmarksToolbar', 'toolbar'], Immutable.fromJS([ + '69', + '1' + ])) assert.equal(spy.calledOnce, true) + assert.equal(spyToolbar.calledOnce, true) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) + + it('destination key is not on bookmark toolbar', function () { + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + bookmarkFoldersReducer(stateWithData, { + actionType: appConstants.APP_MOVE_BOOKMARK_FOLDER, + folderKey: '1', + destinationKey: '80' + }) + assert.equal(spyToolbar.notCalled, true) + }) }) describe('APP_REMOVE_BOOKMARK_FOLDER', function () { - let spy + let spy, spyToolbar afterEach(function () { spy.restore() + spyToolbar.restore() }) it('null case', function () { spy = sinon.spy(bookmarkFoldersState, 'removeFolder') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarkFoldersReducer(state, { actionType: appConstants.APP_REMOVE_BOOKMARK_FOLDER }) assert.equal(spy.notCalled, true) + assert.equal(spyToolbar.notCalled, true) assert.deepEqual(state, newState) }) it('folder key is list (multiple folders)', function () { spy = sinon.spy(bookmarkFoldersState, 'removeFolder') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_REMOVE_BOOKMARK_FOLDER, folderKey: [ @@ -326,12 +413,14 @@ describe('bookmarkFoldersReducer unit test', function () { '69' ] }) - assert.equal(spy.callCount, 2) + assert.equal(spy.callCount, 3) + assert.equal(spyToolbar.calledOnce, true) assert.deepEqual(newState.toJS(), state.toJS()) }) it('folder key is map (single folder)', function () { spy = sinon.spy(bookmarkFoldersState, 'removeFolder') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_REMOVE_BOOKMARK_FOLDER, folderKey: '1' @@ -345,7 +434,89 @@ describe('bookmarkFoldersReducer unit test', function () { } ])) .deleteIn([STATE_SITES.BOOKMARK_FOLDERS, '1']) + .setIn(['windows', 0, 'bookmarksToolbar', 'toolbar'], Immutable.fromJS([ + '69' + ])) assert.equal(spy.calledOnce, true) + assert.equal(spyToolbar.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED', function () { + let spy, spyToolbar + + afterEach(function () { + spy.restore() + spyToolbar.restore() + }) + + it('null case', function () { + spy = sinon.spy(bookmarkFoldersState, 'setWidth') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + const newState = bookmarkFoldersReducer(state, { + actionType: appConstants.APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED + }) + assert.equal(spy.notCalled, true) + assert.equal(spyToolbar.notCalled, true) + assert.deepEqual(state, newState) + }) + + it('we update multiple items', function () { + spy = sinon.spy(bookmarkFoldersState, 'setWidth') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + const newState = bookmarkFoldersReducer(stateWithData, { + actionType: appConstants.APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED, + folderList: Immutable.fromJS([ + { + key: '1', + width: 10, + parentFolderId: 0 + }, + { + key: '69', + width: 15, + parentFolderId: 0 + }, + { + key: '80', + width: 20, + parentFolderId: 69 + } + ]) + }) + assert.equal(spy.callCount, 3) + assert.equal(spyToolbar.calledOnce, true) + const expectedState = stateWithData + .setIn([STATE_SITES.BOOKMARK_FOLDERS, '1', 'width'], 10) + .setIn([STATE_SITES.BOOKMARK_FOLDERS, '69', 'width'], 15) + .setIn([STATE_SITES.BOOKMARK_FOLDERS, '80', 'width'], 20) + .setIn(['windows', 0, 'bookmarksToolbar', 'toolbar'], Immutable.fromJS([ + '1' + ])) + .setIn(['windows', 0, 'bookmarksToolbar', 'other'], Immutable.fromJS([ + '69' + ])) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + + it('we update one and dont trigger toolbar update (parentFolderId is not 0)', function () { + spy = sinon.spy(bookmarkFoldersState, 'setWidth') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + const newState = bookmarkFoldersReducer(stateWithData, { + actionType: appConstants.APP_ON_BOOKMARK_FOLDER_WIDTH_CHANGED, + folderList: Immutable.fromJS([ + { + key: '80', + width: 20, + parentFolderId: 69 + } + ]) + }) + assert.equal(spy.callCount, 1) + assert.equal(spyToolbar.notCalled, true) + const expectedState = stateWithData + .setIn([STATE_SITES.BOOKMARK_FOLDERS, '80', 'width'], 20) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) }) diff --git a/test/unit/app/browser/reducers/bookmarkToolbarReducerTest.js b/test/unit/app/browser/reducers/bookmarkToolbarReducerTest.js new file mode 100644 index 00000000000..b1aa1fef7a0 --- /dev/null +++ b/test/unit/app/browser/reducers/bookmarkToolbarReducerTest.js @@ -0,0 +1,140 @@ +/* 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/. */ + +/* global describe, it, before, after, afterEach */ +const mockery = require('mockery') +const Immutable = require('immutable') +const assert = require('assert') +const sinon = require('sinon') +const fakeElectron = require('../../../lib/fakeElectron') +const fakeAdBlock = require('../../../lib/fakeAdBlock') + +const appConstants = require('../../../../../js/constants/appConstants') +const siteTags = require('../../../../../js/constants/siteTags') +const {STATE_SITES} = require('../../../../../js/constants/stateConstants') +require('../../../braveUnit') + +describe('bookmarkToolbarReducer unit test', function () { + let bookmarkToolbarReducer + + const fakeTextCalc = { + calcTextList: () => true + } + + const stateWithData = Immutable.fromJS({ + windows: [], + bookmarks: { + 'https://brave.com/|0|0': { + favicon: undefined, + title: 'Brave', + location: 'https://brave.com/', + key: 'https://brave.com/|0|0', + parentFolderId: 0, + partitionNumber: 0, + objectId: null, + themeColor: undefined, + type: siteTags.BOOKMARK + }, + 'https://brianbondy.com/|0|1': { + favicon: undefined, + title: 'Clifton', + location: 'https://clifton.io/', + key: 'https://clifton.io/|0|1', + parentFolderId: 1, + partitionNumber: 0, + objectId: null, + themeColor: undefined, + type: siteTags.BOOKMARK + } + }, + bookmarkFolders: { + '1': { + title: 'folder1', + folderId: 1, + key: '1', + parentFolderId: 0, + partitionNumber: 0, + objectId: null, + type: siteTags.BOOKMARK_FOLDER + } + }, + cache: { + bookmarkOrder: { + '0': [ + { + key: 'https://brave.com/|0|0', + order: 0, + type: siteTags.BOOKMARK + }, + { + key: '1', + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ], + '1': [ + { + key: 'https://brianbondy.com/|0|1', + order: 0, + type: siteTags.BOOKMARK + } + ] + }, + bookmarkLocation: { + 'https://brave.com/': [ + 'https://brave.com/|0|0' + ], + 'https://brianbondy.com/': [ + 'https://brianbondy.com/|0|1' + ] + } + }, + historySites: {}, + tabs: [] + }) + + before(function () { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + mockery.registerMock('electron', fakeElectron) + mockery.registerMock('ad-block', fakeAdBlock) + mockery.registerMock('../../browser/api/textCalc', fakeTextCalc) + bookmarkToolbarReducer = require('../../../../../app/browser/reducers/bookmarkToolbarReducer') + }) + + after(function () { + mockery.disable() + }) + + describe('APP_SET_STATE', function () { + let spyCalc + + afterEach(function () { + spyCalc.restore() + }) + + it('we are upgrading from version 0.20 to 0.21', function () { + spyCalc = sinon.spy(fakeTextCalc, 'calcTextList') + bookmarkToolbarReducer(stateWithData, { + actionType: appConstants.APP_SET_STATE + }) + assert.equal(spyCalc.callCount, 2) + }) + + it('we are on version 0.21', function () { + spyCalc = sinon.spy(fakeTextCalc, 'calcTextList') + const newState = stateWithData + .setIn([STATE_SITES.BOOKMARKS, 'https://brave.com/|0|0', 'width'], 10) + .setIn([STATE_SITES.BOOKMARKS, 'https://brianbondy.com/|0|1', 'width'], 10) + .setIn([STATE_SITES.BOOKMARK_FOLDERS, '1', 'width'], 10) + bookmarkToolbarReducer(newState, { + actionType: appConstants.APP_SET_STATE + }) + assert.equal(spyCalc.notCalled, true) + }) + }) +}) diff --git a/test/unit/app/browser/reducers/bookmarksReducerTest.js b/test/unit/app/browser/reducers/bookmarksReducerTest.js index 7f9ebaf6f2d..7d7ff821acf 100644 --- a/test/unit/app/browser/reducers/bookmarksReducerTest.js +++ b/test/unit/app/browser/reducers/bookmarksReducerTest.js @@ -7,16 +7,27 @@ const mockery = require('mockery') const Immutable = require('immutable') const assert = require('assert') const sinon = require('sinon') +const fakeElectron = require('../../../lib/fakeElectron') +const fakeAdBlock = require('../../../lib/fakeAdBlock') const appConstants = require('../../../../../js/constants/appConstants') const siteTags = require('../../../../../js/constants/siteTags') require('../../../braveUnit') describe('bookmarksReducer unit test', function () { - let bookmarksReducer, bookmarksState, bookmarkLocationCache + let bookmarksReducer, bookmarksState, bookmarkLocationCache, bookmarkToolbarState const state = Immutable.fromJS({ - windows: [], + windows: [ + { + windowId: 1, + width: 100, + bookmarksToolbar: { + toolbar: [], + other: [] + } + } + ], bookmarks: {}, bookmarkFolders: {}, cache: { @@ -28,7 +39,16 @@ describe('bookmarksReducer unit test', function () { }) const stateWithData = Immutable.fromJS({ - windows: [], + windows: [ + { + windowId: 1, + width: 100, + bookmarksToolbar: { + toolbar: [], + other: [] + } + } + ], bookmarks: { 'https://brave.com/|0|0': { favicon: undefined, @@ -51,6 +71,17 @@ describe('bookmarksReducer unit test', function () { objectId: null, themeColor: undefined, type: siteTags.BOOKMARK + }, + 'https://brianbondy.com/|0|1': { + favicon: undefined, + title: 'Clifton', + location: 'https://clifton.io/', + key: 'https://clifton.io/|0|1', + parentFolderId: 1, + partitionNumber: 0, + objectId: null, + themeColor: undefined, + type: siteTags.BOOKMARK } }, bookmarkFolders: {}, @@ -67,6 +98,13 @@ describe('bookmarksReducer unit test', function () { order: 1, type: siteTags.BOOKMARK } + ], + '1': [ + { + key: 'https://brianbondy.com/|0|1', + order: 0, + type: siteTags.BOOKMARK + } ] }, bookmarkLocation: { @@ -75,6 +113,9 @@ describe('bookmarksReducer unit test', function () { ], 'https://clifton.io/': [ 'https://clifton.io/|0|0' + ], + 'https://brianbondy.com/': [ + 'https://brianbondy.com/|0|1' ] } }, @@ -82,15 +123,24 @@ describe('bookmarksReducer unit test', function () { tabs: [] }) + const fakeTextCalc = { + calcText: () => true, + calcTextList: () => true + } + before(function () { mockery.enable({ warnOnReplace: false, warnOnUnregistered: false, useCleanCache: true }) + mockery.registerMock('electron', fakeElectron) + mockery.registerMock('ad-block', fakeAdBlock) + mockery.registerMock('../../browser/api/textCalc', fakeTextCalc) bookmarksReducer = require('../../../../../app/browser/reducers/bookmarksReducer') bookmarksState = require('../../../../../app/common/state/bookmarksState') bookmarkLocationCache = require('../../../../../app/common/cache/bookmarkLocationCache') + bookmarkToolbarState = require('../../../../../app/common/state/bookmarkToolbarState') }) after(function () { @@ -116,23 +166,27 @@ describe('bookmarksReducer unit test', function () { }) describe('APP_ADD_BOOKMARK', function () { - let spy + let spy, spyCalc afterEach(function () { spy.restore() + spyCalc.restore() }) it('null case', function () { spy = sinon.spy(bookmarksState, 'addBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarksReducer(state, { actionType: appConstants.APP_ADD_BOOKMARK }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(state, newState) }) it('bookmark data is map (single bookmark)', function () { spy = sinon.spy(bookmarksState, 'addBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarksReducer(state, { actionType: appConstants.APP_ADD_BOOKMARK, siteDetail: { @@ -153,7 +207,8 @@ describe('bookmarksReducer unit test', function () { themeColor: undefined, title: 'Clifton', type: siteTags.BOOKMARK, - key: 'https://clifton.io/|0|0' + key: 'https://clifton.io/|0|0', + width: 0 } })) .setIn(['cache', 'bookmarkLocation'], Immutable.fromJS({ @@ -171,11 +226,13 @@ describe('bookmarksReducer unit test', function () { ] })) assert.equal(spy.calledOnce, true) + assert.equal(spyCalc.calledOnce, true) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) it('bookmark data is list (multiple bookmarks)', function () { spy = sinon.spy(bookmarksState, 'addBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcTextList') const newState = bookmarksReducer(state, { actionType: appConstants.APP_ADD_BOOKMARK, siteDetail: [ @@ -204,7 +261,8 @@ describe('bookmarksReducer unit test', function () { themeColor: undefined, title: 'Clifton', type: siteTags.BOOKMARK, - key: 'https://clifton.io/|0|0' + key: 'https://clifton.io/|0|0', + width: 0 }, 'https://brianbondy.com/|0|0': { favicon: undefined, @@ -216,7 +274,8 @@ describe('bookmarksReducer unit test', function () { themeColor: undefined, title: 'Bondy', type: siteTags.BOOKMARK, - key: 'https://brianbondy.com/|0|0' + key: 'https://brianbondy.com/|0|0', + width: 0 } })) .setIn(['cache', 'bookmarkLocation'], Immutable.fromJS({ @@ -242,38 +301,45 @@ describe('bookmarksReducer unit test', function () { ] })) assert.equal(spy.callCount, 2) + assert.equal(spyCalc.callCount, 1) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) }) describe('APP_EDIT_BOOKMARK', function () { - let spy + let spy, spyCalc afterEach(function () { spy.restore() + spyCalc.restore() }) it('null case', function () { spy = sinon.spy(bookmarksState, 'editBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarksReducer(state, { actionType: appConstants.APP_EDIT_BOOKMARK }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(state, newState) }) it('bookmark data is missing', function () { spy = sinon.spy(bookmarksState, 'editBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarksReducer(state, { actionType: appConstants.APP_EDIT_BOOKMARK, editKey: 'https://clifton.io|0|0' }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(state, newState) }) it('bookmark key is missing', function () { spy = sinon.spy(bookmarksState, 'editBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarksReducer(state, { actionType: appConstants.APP_EDIT_BOOKMARK, siteDetail: { @@ -282,11 +348,13 @@ describe('bookmarksReducer unit test', function () { } }) assert.equal(spy.notCalled, true) + assert.equal(spyCalc.notCalled, true) assert.deepEqual(state, newState) }) it('bookmark data is correct', function () { spy = sinon.spy(bookmarksState, 'editBookmark') + spyCalc = sinon.spy(fakeTextCalc, 'calcText') const newState = bookmarksReducer(stateWithData, { actionType: appConstants.APP_EDIT_BOOKMARK, siteDetail: { @@ -297,28 +365,33 @@ describe('bookmarksReducer unit test', function () { const expectedState = stateWithData .setIn(['bookmarks', 'https://clifton.io/|0|0', 'title'], 'Bondy') assert.equal(spy.calledOnce, true) + assert.equal(spyCalc.calledOnce, true) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) }) describe('APP_MOVE_BOOKMARK', function () { - let spy + let spy, spyToolbar afterEach(function () { spy.restore() + spyToolbar.restore() }) it('null case', function () { spy = sinon.spy(bookmarksState, 'moveBookmark') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarksReducer(state, { actionType: appConstants.APP_MOVE_BOOKMARK }) assert.equal(spy.notCalled, true) + assert.equal(spyToolbar.notCalled, true) assert.deepEqual(state, newState) }) it('data is correct', function () { spy = sinon.spy(bookmarksState, 'moveBookmark') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarksReducer(stateWithData, { actionType: appConstants.APP_MOVE_BOOKMARK, bookmarkKey: 'https://clifton.io/|0|0', @@ -338,29 +411,48 @@ describe('bookmarksReducer unit test', function () { type: siteTags.BOOKMARK } ])) + .setIn(['windows', 0, 'bookmarksToolbar', 'toolbar'], Immutable.fromJS([ + 'https://clifton.io/|0|0', + 'https://brave.com/|0|0' + ])) assert.equal(spy.calledOnce, true) + assert.equal(spyToolbar.calledOnce, true) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) + + it('destination key is not on bookmark toolbar', function () { + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + bookmarksReducer(stateWithData, { + actionType: appConstants.APP_MOVE_BOOKMARK, + bookmarkKey: 'https://clifton.io/|0|0', + destinationKey: 'https://brianbondy.com/|0|1' + }) + assert.equal(spyToolbar.notCalled, true) + }) }) describe('APP_REMOVE_BOOKMARK', function () { - let spy + let spy, spyToolbar afterEach(function () { spy.restore() + spyToolbar.restore() }) it('null case', function () { spy = sinon.spy(bookmarksState, 'removeBookmark') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarksReducer(state, { actionType: appConstants.APP_REMOVE_BOOKMARK }) assert.equal(spy.notCalled, true) + assert.equal(spyToolbar.notCalled, true) assert.deepEqual(state, newState) }) it('check if delete is working', function () { spy = sinon.spy(bookmarksState, 'removeBookmark') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') const newState = bookmarksReducer(stateWithData, { actionType: appConstants.APP_REMOVE_BOOKMARK, bookmarkKey: 'https://clifton.io/|0|0' @@ -375,7 +467,87 @@ describe('bookmarksReducer unit test', function () { ])) .deleteIn(['bookmarks', 'https://clifton.io/|0|0']) .deleteIn(['cache', 'bookmarkLocation', 'https://clifton.io/']) + .setIn(['windows', 0, 'bookmarksToolbar', 'toolbar'], Immutable.fromJS([ + 'https://brave.com/|0|0' + ])) assert.equal(spy.calledOnce, true) + assert.equal(spyToolbar.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('APP_ON_BOOKMARK_WIDTH_CHANGED', function () { + let spy, spyToolbar + + afterEach(function () { + spy.restore() + spyToolbar.restore() + }) + + it('null case', function () { + spy = sinon.spy(bookmarksState, 'setWidth') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + const newState = bookmarksReducer(state, { + actionType: appConstants.APP_ON_BOOKMARK_WIDTH_CHANGED + }) + assert.equal(spy.notCalled, true) + assert.equal(spyToolbar.notCalled, true) + assert.deepEqual(state, newState) + }) + + it('we update multiple items', function () { + spy = sinon.spy(bookmarksState, 'setWidth') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + const newState = bookmarksReducer(stateWithData, { + actionType: appConstants.APP_ON_BOOKMARK_WIDTH_CHANGED, + bookmarkList: Immutable.fromJS([ + { + key: 'https://brave.com/|0|0', + width: 10, + parentFolderId: 0 + }, + { + key: 'https://clifton.io/|0|0', + width: 15, + parentFolderId: 0 + }, + { + key: 'https://brianbondy.com/|0|1', + width: 20, + parentFolderId: 69 + } + ]) + }) + assert.equal(spy.callCount, 3) + assert.equal(spyToolbar.calledOnce, true) + const expectedState = stateWithData + .setIn(['bookmarks', 'https://brave.com/|0|0', 'width'], 10) + .setIn(['bookmarks', 'https://clifton.io/|0|0', 'width'], 15) + .setIn(['bookmarks', 'https://brianbondy.com/|0|1', 'width'], 20) + .setIn(['windows', 0, 'bookmarksToolbar', 'toolbar'], Immutable.fromJS([ + 'https://brave.com/|0|0', + 'https://clifton.io/|0|0' + ])) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + + it('we update one and trigger toolbar update', function () { + spy = sinon.spy(bookmarksState, 'setWidth') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbars') + const newState = bookmarksReducer(stateWithData, { + actionType: appConstants.APP_ON_BOOKMARK_WIDTH_CHANGED, + bookmarkList: Immutable.fromJS([ + { + key: 'https://brianbondy.com/|0|1', + width: 20, + parentFolderId: 1 + } + ]) + }) + assert.equal(spy.callCount, 1) + assert.equal(spyToolbar.notCalled, true) + const expectedState = stateWithData + .setIn(['bookmarks', 'https://brianbondy.com/|0|1', 'width'], 20) assert.deepEqual(newState.toJS(), expectedState.toJS()) }) }) diff --git a/test/unit/app/browser/reducers/windowReducerTest.js b/test/unit/app/browser/reducers/windowReducerTest.js new file mode 100644 index 00000000000..a50751484bf --- /dev/null +++ b/test/unit/app/browser/reducers/windowReducerTest.js @@ -0,0 +1,89 @@ +/* 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/. */ + +/* global describe, it, before, after, afterEach */ +const mockery = require('mockery') +const sinon = require('sinon') +const Immutable = require('immutable') +const assert = require('assert') +const fakeAdBlock = require('../../../lib/fakeAdBlock') + +const appConstants = require('../../../../../js/constants/appConstants') +require('../../../braveUnit') + +describe('windowsReducer unit test', function () { + let windowsReducer, bookmarkToolbarState + const fakeElectron = Object.assign({}, require('../../../lib/fakeElectron')) + + const fakeWindowState = { + maybeCreateWindow: (state, action) => state + } + + const state = Immutable.fromJS({ + windows: [], + defaultWindowParams: {}, + bookmarks: {} + }) + + before(function () { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + mockery.registerMock('electron', fakeElectron) + mockery.registerMock('ad-block', fakeAdBlock) + mockery.registerMock('../../common/state/windowState', fakeWindowState) + windowsReducer = require('../../../../../app/browser/reducers/windowsReducer') + bookmarkToolbarState = require('../../../../../app/common/state/bookmarkToolbarState') + }) + + after(function () { + mockery.disable() + }) + + describe('APP_WINDOW_CREATED', function () { + let spy, spyToolbar + + afterEach(function () { + spy.restore() + spyToolbar.restore() + }) + + it('check if functions are called', function () { + spy = sinon.spy(fakeWindowState, 'maybeCreateWindow') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbar') + windowsReducer(state, { + actionType: appConstants.APP_WINDOW_CREATED, + windowValue: Immutable.fromJS({ + windowId: 1 + }) + }) + assert.equal(spy.calledOnce, true) + assert.equal(spyToolbar.calledOnce, true) + }) + }) + + describe('APP_WINDOW_RESIZED', function () { + let spy, spyToolbar + + afterEach(function () { + spy.restore() + spyToolbar.restore() + }) + + it('check if functions are called', function () { + spy = sinon.spy(fakeWindowState, 'maybeCreateWindow') + spyToolbar = sinon.spy(bookmarkToolbarState, 'setToolbar') + windowsReducer(state, { + actionType: appConstants.APP_WINDOW_RESIZED, + windowValue: Immutable.fromJS({ + windowId: 1 + }) + }) + assert.equal(spy.calledOnce, true) + assert.equal(spyToolbar.calledOnce, true) + }) + }) +}) diff --git a/test/unit/app/common/lib/bookmarkFoldersUtilTest.js b/test/unit/app/common/lib/bookmarkFoldersUtilTest.js index d8f3167ac29..2abdba9d9ab 100644 --- a/test/unit/app/common/lib/bookmarkFoldersUtilTest.js +++ b/test/unit/app/common/lib/bookmarkFoldersUtilTest.js @@ -29,7 +29,7 @@ describe('bookmarkFoldersUtil unit test', function () { describe('getNextFolderId', function () { it('null check', function () { const id = bookmarkFoldersUtil.getNextFolderId() - assert.equal(id, 0) + assert.equal(id, 1) }) it('folders list is empty', function () { @@ -195,4 +195,66 @@ describe('bookmarkFoldersUtil unit test', function () { assert.equal(valid, true) }) }) + + describe('buildFolder', function () { + it('uses all default values', function () { + const folderDetails = Immutable.fromJS({ + title: 'Brave' + }) + const folder = bookmarkFoldersUtil.buildFolder(folderDetails) + const expectedFolder = { + title: 'Brave', + folderId: 1, + key: '1', + parentFolderId: 0, + partitionNumber: 0, + objectId: null, + type: siteTags.BOOKMARK_FOLDER, + skipSync: null + } + assert.deepEqual(folder.toJS(), expectedFolder) + }) + + it('folderId is provided', function () { + const folderDetails = Immutable.fromJS({ + title: 'Brave', + folderId: 3 + }) + const folder = bookmarkFoldersUtil.buildFolder(folderDetails) + const expectedFolder = { + title: 'Brave', + folderId: 3, + key: '3', + parentFolderId: 0, + partitionNumber: 0, + objectId: null, + type: siteTags.BOOKMARK_FOLDER, + skipSync: null + } + assert.deepEqual(folder.toJS(), expectedFolder) + }) + + it('all values are provided', function () { + const folderDetails = Immutable.fromJS({ + title: 'Brave', + folderId: 3, + parentFolderId: 1, + partitionNumber: 1, + objectId: [1], + skipSync: true + }) + const folder = bookmarkFoldersUtil.buildFolder(folderDetails) + const expectedFolder = { + title: 'Brave', + folderId: 3, + key: '3', + parentFolderId: 1, + partitionNumber: 1, + objectId: [1], + type: siteTags.BOOKMARK_FOLDER, + skipSync: true + } + assert.deepEqual(folder.toJS(), expectedFolder) + }) + }) }) diff --git a/test/unit/app/common/lib/bookmarkToolbarUtilTest.js b/test/unit/app/common/lib/bookmarkToolbarUtilTest.js new file mode 100644 index 00000000000..a0750357f1f --- /dev/null +++ b/test/unit/app/common/lib/bookmarkToolbarUtilTest.js @@ -0,0 +1,94 @@ +/* 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/. */ + +/* global describe, it, before, after */ +const assert = require('assert') +const mockery = require('mockery') +const sinon = require('sinon') +const Immutable = require('immutable') +const siteTags = require('../../../../../js/constants/siteTags') + +require('../../../braveUnit') + +describe('bookmarkToolbarUtil unit test', function () { + let bookmarkToolbarUtil, bookmarkUtil + + const generateBookmarks = (num) => { + return Immutable.fromJS(new Array(num).fill().map((_, i) => { + return { + type: siteTags.BOOKMARK, + title: `Bookmark ${i}`, + key: `bookmark-${i}|0|0`, + width: 10 * i + } + })) + } + + const generateBookmarksKeys = (num, skip = 0) => { + return Immutable.fromJS(new Array(num).fill().map((_, i) => { + const id = skip + i + return `bookmark-${id}|0|0` + })) + } + + before(function () { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + bookmarkToolbarUtil = require('../../../../../app/common/lib/bookmarkToolbarUtil') + bookmarkUtil = require('../../../../../app/common/lib/bookmarkUtil') + }) + + after(function () { + mockery.disable() + }) + + describe('getToolbarBookmarks', function () { + let showOnlyText + + before(function () { + showOnlyText = sinon.stub(bookmarkUtil, 'showOnlyText', () => true) + }) + + after(function () { + showOnlyText.restore() + }) + + it('null scenario', function () { + assert.deepEqual(bookmarkToolbarUtil.getBookmarkKeys(), { + toolbar: Immutable.List(), + other: Immutable.List() + }) + }) + + it('we only have bookmark for the toolbar', function () { + const bookmarks = generateBookmarks(5) + + assert.deepEqual(bookmarkToolbarUtil.getBookmarkKeys(500, bookmarks), { + toolbar: generateBookmarksKeys(5), + other: Immutable.List() + }) + }) + + it('we have bookmarks for toolbar and other', function () { + const bookmarks = generateBookmarks(50) + + assert.deepEqual(bookmarkToolbarUtil.getBookmarkKeys(500, bookmarks), { + toolbar: generateBookmarksKeys(8), + other: generateBookmarksKeys(42, 8) + }) + }) + + it('other limit is set to 100', function () { + const bookmarks = generateBookmarks(500) + + assert.deepEqual(bookmarkToolbarUtil.getBookmarkKeys(500, bookmarks), { + toolbar: generateBookmarksKeys(8), + other: generateBookmarksKeys(100, 8) + }) + }) + }) +}) diff --git a/test/unit/app/common/lib/bookmarkUtilTest.js b/test/unit/app/common/lib/bookmarkUtilTest.js index 317ea19d7c0..f5fde0be313 100644 --- a/test/unit/app/common/lib/bookmarkUtilTest.js +++ b/test/unit/app/common/lib/bookmarkUtilTest.js @@ -95,7 +95,12 @@ describe('bookmarkUtil unit test', function () { url: 'https://brave.com/', title: 'Brave', active: true, - bookmarked: false + bookmarked: false, + frame: { + partitionNumber: 2, + icon: 'ico', + themeColor: '#FFF' + } }], tabsInternal: { index: { @@ -200,6 +205,34 @@ describe('bookmarkUtil unit test', function () { }) }) + describe('showOnlyText', function () { + it('BOOKMARKS_TOOLBAR_MODE is TEXT_ONLY', function () { + settingDefaultValue = bookmarksToolbarMode.TEXT_ONLY + const result = bookmarkUtil.showOnlyText() + assert.equal(result, true) + }) + + it('BOOKMARKS_TOOLBAR_MODE is not TEXT_ONLY', function () { + settingDefaultValue = bookmarksToolbarMode.FAVICONS_ONLY + const result = bookmarkUtil.showOnlyText() + assert.equal(result, false) + }) + }) + + describe('showTextAndFavicon', function () { + it('BOOKMARKS_TOOLBAR_MODE is TEXT_AND_FAVICONS', function () { + settingDefaultValue = bookmarksToolbarMode.TEXT_AND_FAVICONS + const result = bookmarkUtil.showTextAndFavicon() + assert.equal(result, true) + }) + + it('BOOKMARKS_TOOLBAR_MODE is not TEXT_AND_FAVICONS', function () { + settingDefaultValue = bookmarksToolbarMode.TEXT_ONLY + const result = bookmarkUtil.showTextAndFavicon() + assert.equal(result, false) + }) + }) + describe('getDNDBookmarkData', function () { it('dragOverData is missing', function () { const state = makeImmutable({ @@ -446,4 +479,154 @@ describe('bookmarkUtil unit test', function () { }) }) }) + + describe('buildBookmark', function () { + it('use only defaults', function () { + const bookmark = Immutable.fromJS({ + title: 'Brave', + location: 'https://brave.com' + }) + + const expectedResult = { + title: 'Brave', + location: 'https://brave.com', + parentFolderId: 0, + partitionNumber: 0, + objectId: null, + favicon: undefined, + themeColor: undefined, + type: siteTags.BOOKMARK, + key: 'https://brave.com|0|0', + skipSync: null, + width: 0 + } + + assert.deepEqual(bookmarkUtil.buildBookmark(state, bookmark).toJS(), expectedResult) + }) + + it('bookmark data is in history', function () { + const newState = state + .setIn(['historySites', 'https://brave.com|0'], Immutable.fromJS({ + partitionNumber: 1, + favicon: 'icon', + themeColor: '#000' + })) + + const bookmark = Immutable.fromJS({ + title: 'Brave', + location: 'https://brave.com' + }) + + const expectedResult = { + title: 'Brave', + location: 'https://brave.com', + parentFolderId: 0, + partitionNumber: 1, + objectId: null, + favicon: 'icon', + themeColor: '#000', + type: siteTags.BOOKMARK, + key: 'https://brave.com|0|0', + skipSync: null, + width: 0 + } + + assert.deepEqual(bookmarkUtil.buildBookmark(newState, bookmark).toJS(), expectedResult) + }) + + it('bookmark data is in active tab', function () { + const bookmark = Immutable.fromJS({ + title: 'Brave', + location: 'https://brave.com/' + }) + + const expectedResult = { + title: 'Brave', + location: 'https://brave.com/', + parentFolderId: 0, + partitionNumber: 2, + objectId: null, + favicon: 'ico', + themeColor: '#FFF', + type: siteTags.BOOKMARK, + key: 'https://brave.com/|0|0', + skipSync: null, + width: 0 + } + + assert.deepEqual(bookmarkUtil.buildBookmark(stateWithData, bookmark).toJS(), expectedResult) + }) + + it('bookmark data is in topSites', function () { + const bookmark = Immutable.fromJS({ + title: 'Brave', + location: 'https://www.facebook.com/BraveSoftware/' + }) + + const expectedResult = { + title: 'Brave', + location: 'https://www.facebook.com/BraveSoftware/', + parentFolderId: 0, + partitionNumber: 0, + objectId: null, + favicon: 'chrome-extension://mnojpmjdmbbfmejpflffifhffcmidifd/img/newtab/defaultTopSitesIcon/facebook.png', + themeColor: 'rgb(59, 89, 152)', + type: siteTags.BOOKMARK, + key: 'https://www.facebook.com/BraveSoftware/|0|0', + skipSync: null, + width: 0 + } + + assert.deepEqual(bookmarkUtil.buildBookmark(stateWithData, bookmark).toJS(), expectedResult) + }) + }) + + describe('buildEditBookmark', function () { + it('bookmarkDetail is null', function () { + const bookmark = Immutable.fromJS({ + title: 'Brave', + type: siteTags.BOOKMARK + }) + assert.deepEqual(bookmarkUtil.buildEditBookmark(bookmark).toJS(), bookmark.toJS()) + }) + + it('old and new are merged, but key is the same', function () { + const oldBookmark = Immutable.fromJS({ + title: 'Brave', + location: 'http://brave.com', + type: siteTags.BOOKMARK, + parentFolderId: 0, + key: 'http://brave.com|0|0' + }) + + const newBookmark = Immutable.fromJS({ + title: 'Brave 1', + location: 'http://brave.com', + type: siteTags.BOOKMARK, + parentFolderId: 0 + }) + + const expectedBookmark = newBookmark.set('key', oldBookmark.get('key')) + assert.deepEqual(bookmarkUtil.buildEditBookmark(oldBookmark, newBookmark).toJS(), expectedBookmark.toJS()) + }) + + it('old and new data is merged and new key is generated', function () { + const oldBookmark = Immutable.fromJS({ + title: 'Brave', + location: 'http://brave.com', + type: siteTags.BOOKMARK, + parentFolderId: 0 + }) + + const newBookmark = Immutable.fromJS({ + title: 'Brave 1', + location: 'http://new.brave.com', + type: siteTags.BOOKMARK, + parentFolderId: 1 + }) + + const expectedBookmark = newBookmark.set('key', 'http://new.brave.com|0|1') + assert.deepEqual(bookmarkUtil.buildEditBookmark(oldBookmark, newBookmark).toJS(), expectedBookmark.toJS()) + }) + }) }) diff --git a/test/unit/app/common/state/bookmarkToolbarStateTest.js b/test/unit/app/common/state/bookmarkToolbarStateTest.js new file mode 100644 index 00000000000..7e31463bfd2 --- /dev/null +++ b/test/unit/app/common/state/bookmarkToolbarStateTest.js @@ -0,0 +1,158 @@ +/* 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/. */ + +/* global before, after, describe, it */ +const Immutable = require('immutable') +const assert = require('assert') +const mockery = require('mockery') + +describe('bookmarkToolbarState unit test', function () { + let bookmarkToolbarState + + const state = Immutable.fromJS({ + windows: [], + bookmarks: {}, + bookmarkFolders: {}, + cache: { + bookmarkOrder: {}, + bookmarkLocation: {} + }, + historySites: {}, + tabs: [] + }) + + const stateWithData = Immutable.fromJS({ + windows: [ + { + windowId: 1 + }, + { + windowId: 2 + } + ], + bookmarks: {}, + bookmarkFolders: {}, + cache: { + bookmarkOrder: {}, + bookmarkLocation: {} + }, + historySites: {}, + tabs: [] + }) + + const stateWithToolbar = Immutable.fromJS({ + windows: [ + { + windowId: 1, + bookmarksToolbar: { + toolbar: ['1'], + other: ['2'] + } + } + ], + bookmarks: {}, + bookmarkFolders: {}, + cache: { + bookmarkOrder: {}, + bookmarkLocation: {} + }, + historySites: {}, + tabs: [] + }) + + before(function () { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + mockery.registerMock('../lib/bookmarkToolbarUtil', { + getBookmarkKeys: () => { + return { + toolbar: Immutable.fromJS(['1']), + other: Immutable.fromJS(['2']) + } + } + }) + bookmarkToolbarState = require('../../../../../app/common/state/bookmarkToolbarState') + }) + + after(function () { + mockery.disable() + }) + + describe('setToolbars', function () { + it('null case', function () { + const newState = bookmarkToolbarState.setToolbars(state) + assert.deepEqual(newState, state) + }) + + it('set data', function () { + const newState = bookmarkToolbarState.setToolbars(stateWithData) + const expectedState = stateWithData + .setIn(['windows', 0, 'bookmarksToolbar'], Immutable.fromJS({ + 'other': [ + '2' + ], + 'toolbar': [ + '1' + ] + })) + .setIn(['windows', 1, 'bookmarksToolbar'], Immutable.fromJS({ + 'other': [ + '2' + ], + 'toolbar': [ + '1' + ] + })) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('setToolbar', function () { + it('null case', function () { + const newState = bookmarkToolbarState.setToolbar(state, 1) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('set data', function () { + const newState = bookmarkToolbarState.setToolbar(stateWithData, 2) + const expectedState = stateWithData + .setIn(['windows', 1, 'bookmarksToolbar'], Immutable.fromJS({ + 'other': [ + '2' + ], + 'toolbar': [ + '1' + ] + })) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('getToolbar', function () { + it('null case', function () { + const newState = bookmarkToolbarState.getToolbar(state, 1) + assert.deepEqual(newState, Immutable.List()) + }) + + it('return data', function () { + const newState = bookmarkToolbarState.getToolbar(stateWithToolbar, 1) + assert.deepEqual(newState, Immutable.fromJS(['1'])) + }) + }) + + describe('getOther', function () { + it('null case', function () { + const newState = bookmarkToolbarState.getOther(state, 1) + assert.deepEqual(newState, Immutable.List()) + }) + + it('return data', function () { + const newState = bookmarkToolbarState.getOther(stateWithToolbar, 1) + assert.deepEqual(newState, Immutable.fromJS(['2'])) + }) + }) +})