| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview Module of functions which produce a new page state in response | 6 * @fileoverview Module of functions which produce a new page state in response |
| 7 * to an action. Reducers (in the same sense as Array.prototype.reduce) must be | 7 * to an action. Reducers (in the same sense as Array.prototype.reduce) must be |
| 8 * pure functions: they must not modify existing state objects, or make any API | 8 * pure functions: they must not modify existing state objects, or make any API |
| 9 * calls. | 9 * calls. |
| 10 */ | 10 */ |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 return selectedFolder; | 214 return selectedFolder; |
| 215 case 'finish-search': | 215 case 'finish-search': |
| 216 return null; | 216 return null; |
| 217 case 'clear-search': | 217 case 'clear-search': |
| 218 return nodes['0'].children[0]; | 218 return nodes['0'].children[0]; |
| 219 default: | 219 default: |
| 220 return selectedFolder; | 220 return selectedFolder; |
| 221 } | 221 } |
| 222 }; | 222 }; |
| 223 | 223 |
| 224 var ClosedFolderState = {}; | 224 var SidebarState = {}; |
| 225 | 225 |
| 226 /** | 226 /** |
| 227 * @param {ClosedFolderState} state | 227 * Returns the modifications for |folders| needed to show or hide all nodes |
| 228 * @param {Action} action | 228 * beneath |nodeId| that are not beneath a closed folder. |
| 229 * @param {NodeList} nodes | 229 * @param {!Array<SidebarFolder>} folders |
| 230 * @return {ClosedFolderState} | 230 * @param {number} nodeIdx |
| 231 * @param {boolean} open |
| 232 * @return {!Array<SidebarFolder>} |
| 231 */ | 233 */ |
| 232 ClosedFolderState.openAncestorsOf = function(state, action, nodes) { | 234 SidebarState.updateVisibleFoldersBelowNode = function( |
| 233 var id = action.id; | 235 folders, nodeIdx, open) { |
| 236 var node = Object.assign({}, folders[nodeIdx]); |
| 237 node.open = open; |
| 238 |
| 239 if (!node.visible) |
| 240 return {}; |
| 241 |
| 234 var modifications = {}; | 242 var modifications = {}; |
| 235 var parentId = nodes[id].parentId; | 243 var stack = [{node: node, showing: node.visible}]; |
| 236 while (parentId) { | 244 for (var i = nodeIdx + 1; i < folders.length; i++) { |
| 237 if (state[parentId]) { | 245 var n = folders[i]; |
| 238 modifications[parentId] = false; | 246 |
| 239 } | 247 // Pop off nodes deeper than the current node. |
| 240 parentId = nodes[parentId].parentId; | 248 while (stack.length && n.depth <= stack[stack.length - 1].node.depth) |
| 249 stack.pop(); |
| 250 |
| 251 if (!stack.length) |
| 252 break; |
| 253 |
| 254 var parent = stack[stack.length - 1]; |
| 255 var visible = parent.node.open && parent.showing; |
| 256 |
| 257 // Add modification if desired visibility is not the current visibility. |
| 258 if (visible != n.visible) |
| 259 modifications[i] = Object.assign({}, n, {visible: visible}); |
| 260 |
| 261 // Add node to stack if it's deeper than the current node. |
| 262 if (n.depth > parent.node.depth) |
| 263 stack.push({node: n, showing: visible}); |
| 241 } | 264 } |
| 242 | 265 |
| 243 return Object.assign({}, state, modifications); | 266 return modifications; |
| 244 }; | 267 }; |
| 245 | 268 |
| 246 /** | 269 /** |
| 247 * @param {ClosedFolderState} state | 270 * @param {SidebarState} state |
| 248 * @param {Action} action | 271 * @param {Action} action |
| 249 * @return {ClosedFolderState} | 272 * @return {SidebarState} |
| 250 */ | 273 */ |
| 251 ClosedFolderState.toggleFolderOpen = function(state, action) { | 274 SidebarState.changeFolderOpen = function(state, action) { |
| 252 var id = action.id; | 275 var newFolders = state.folders.slice(); |
| 253 var closed = !action.open; | 276 var idx = state.folderMap[action.id]; |
| 254 var modification = {}; | 277 var folder = newFolders[idx]; |
| 255 modification[id] = closed; | 278 if (folder.open == action.open) |
| 279 return state; |
| 256 | 280 |
| 257 return Object.assign({}, state, modification); | 281 newFolders[idx] = /** @type {SidebarFolder} */ ( |
| 282 Object.assign({}, folder, {open: action.open})); |
| 283 newFolders = /** @type {!Array<SidebarFolder>} */ (Object.assign( |
| 284 newFolders, |
| 285 this.updateVisibleFoldersBelowNode(state.folders, idx, action.open))); |
| 286 return /** @type {SidebarState} */ ( |
| 287 Object.assign({}, state, {folders: newFolders})); |
| 258 }; | 288 }; |
| 259 | 289 |
| 260 /** | 290 /** |
| 261 * @param {ClosedFolderState} state | 291 * @param {SidebarState} state |
| 262 * @param {Action} action | 292 * @param {Action} action |
| 263 * @param {NodeList} nodes | 293 * @param {NodeList} nodes |
| 264 * @return {ClosedFolderState} | 294 * @return {SidebarState} |
| 265 */ | 295 */ |
| 266 ClosedFolderState.updateClosedFolders = function(state, action, nodes) { | 296 SidebarState.openAncestorsOf = function(state, action, nodes) { |
| 297 var id = action.id; |
| 298 var newState = state; |
| 299 var parentId = nodes[id].parentId || ''; |
| 300 while (nodes[parentId] && nodes[parentId].parentId) { |
| 301 newState = this.changeFolderOpen( |
| 302 newState, bookmarks.actions.changeFolderOpen(parentId, true)); |
| 303 parentId = nodes[parentId].parentId || ''; |
| 304 } |
| 305 |
| 306 return newState; |
| 307 }; |
| 308 |
| 309 /** |
| 310 * @param {SidebarState} state |
| 311 * @param {Action} action |
| 312 * @return {SidebarState} |
| 313 */ |
| 314 SidebarState.removeBookmark = function(state, action) { |
| 315 var newFolders = state.folders.slice(); |
| 316 |
| 317 for (var i = 0; i < newFolders.length; i++) { |
| 318 if (action.id != newFolders[i].id) |
| 319 continue; |
| 320 |
| 321 newFolders.splice(i, 1); |
| 322 } |
| 323 |
| 324 return /** @type {SidebarState} */ (Object.assign({}, state, { |
| 325 folders: newFolders, |
| 326 folderMap: bookmarks.util.createFolderMap(newFolders), |
| 327 })); |
| 328 }; |
| 329 |
| 330 /** |
| 331 * @param {SidebarState} state |
| 332 * @param {Action} action |
| 333 * @param {NodeList} nodes |
| 334 * @return {SidebarState} |
| 335 */ |
| 336 SidebarState.updateSidebar = function(state, action, nodes) { |
| 267 switch (action.name) { | 337 switch (action.name) { |
| 338 case 'remove-bookmark': |
| 339 return this.removeBookmark(state, action); |
| 268 case 'change-folder-open': | 340 case 'change-folder-open': |
| 269 return ClosedFolderState.toggleFolderOpen(state, action); | 341 return this.changeFolderOpen(state, action); |
| 270 case 'select-folder': | 342 case 'select-folder': |
| 271 return ClosedFolderState.openAncestorsOf(state, action, nodes); | 343 return this.openAncestorsOf(state, action, nodes); |
| 272 default: | 344 default: |
| 273 return state; | 345 return state; |
| 274 }; | 346 }; |
| 275 }; | 347 }; |
| 276 | 348 |
| 277 /** | 349 /** |
| 278 * Root reducer for the Bookmarks page. This is called by the store in | 350 * Root reducer for the Bookmarks page. This is called by the store in |
| 279 * response to an action, and the return value is used to update the UI. | 351 * response to an action, and the return value is used to update the UI. |
| 280 * @param {BookmarksPageState} state | 352 * @param {BookmarksPageState} state |
| 281 * @param {Action} action | 353 * @param {Action} action |
| 282 * @return {BookmarksPageState} | 354 * @return {BookmarksPageState} |
| 283 */ | 355 */ |
| 284 function reduceAction(state, action) { | 356 function reduceAction(state, action) { |
| 285 return { | 357 return { |
| 286 nodes: NodeState.updateNodes(state.nodes, action), | 358 nodes: NodeState.updateNodes(state.nodes, action), |
| 359 search: SearchState.updateSearch(state.search, action), |
| 287 selectedFolder: SelectedFolderState.updateSelectedFolder( | 360 selectedFolder: SelectedFolderState.updateSelectedFolder( |
| 288 state.selectedFolder, action, state.nodes), | 361 state.selectedFolder, action, state.nodes), |
| 289 closedFolders: ClosedFolderState.updateClosedFolders( | |
| 290 state.closedFolders, action, state.nodes), | |
| 291 search: SearchState.updateSearch(state.search, action), | |
| 292 selection: SelectionState.updateSelection(state.selection, action), | 362 selection: SelectionState.updateSelection(state.selection, action), |
| 363 sidebar: SidebarState.updateSidebar(state.sidebar, action, state.nodes), |
| 293 }; | 364 }; |
| 294 } | 365 } |
| 295 | 366 |
| 296 return { | 367 return { |
| 297 reduceAction: reduceAction, | 368 reduceAction: reduceAction, |
| 298 ClosedFolderState: ClosedFolderState, | |
| 299 NodeState: NodeState, | 369 NodeState: NodeState, |
| 300 SearchState: SearchState, | 370 SearchState: SearchState, |
| 301 SelectedFolderState: SelectedFolderState, | 371 SelectedFolderState: SelectedFolderState, |
| 302 SelectionState: SelectionState, | 372 SelectionState: SelectionState, |
| 373 SidebarState: SidebarState, |
| 303 }; | 374 }; |
| 304 }); | 375 }); |
| OLD | NEW |