| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview Module of functions which produce a new page state in response |
| * to an action. Reducers (in the same sense as Array.prototype.reduce) must be |
| * pure functions: they must not modify existing state objects, or make any API |
| * calls. |
| */ |
| |
| cr.define('app_management', function() { |
| const AppState = {}; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {Object} action |
| * @return {AppMap} |
| */ |
| AppState.addApp = function(apps, action) { |
| assert(!apps[action.app.id]); |
| |
| const newAppEntry = {}; |
| newAppEntry[action.app.id] = action.app; |
| return Object.assign({}, apps, newAppEntry); |
| }; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {Object} action |
| * @return {AppMap} |
| */ |
| AppState.changeApp = function(apps, action) { |
| assert(apps[action.update.id]); |
| |
| const changedAppEntry = {}; |
| changedAppEntry[action.update.id] = action.update; |
| return Object.assign({}, apps, changedAppEntry); |
| }; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {Object} action |
| * @return {AppMap} |
| */ |
| AppState.removeApp = function(apps, action) { |
| assert(apps[action.id]); |
| |
| delete apps[action.id]; |
| return Object.assign({}, apps); |
| }; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {Object} action |
| * @return {AppMap} |
| */ |
| AppState.updateApps = function(apps, action) { |
| switch (action.name) { |
| case 'add-app': |
| return AppState.addApp(apps, action); |
| case 'change-app': |
| return AppState.changeApp(apps, action); |
| case 'remove-app': |
| return AppState.removeApp(apps, action); |
| default: |
| return apps; |
| } |
| }; |
| |
| const CurrentPageState = {}; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {Object} action |
| * @return {Page} |
| */ |
| CurrentPageState.changePage = function(apps, action) { |
| if (action.pageType === PageType.DETAIL && apps[action.id]) { |
| return { |
| pageType: PageType.DETAIL, |
| selectedAppId: action.id, |
| }; |
| } else if (action.pageType === PageType.NOTIFICATIONS) { |
| return { |
| pageType: PageType.NOTIFICATIONS, |
| selectedAppId: null, |
| }; |
| } else { |
| return { |
| pageType: PageType.MAIN, |
| selectedAppId: null, |
| }; |
| } |
| }; |
| |
| /** |
| * TODO(ceciliani) Delete search page type and navigate router by calculating |
| * if there is search.term. |
| * @param {Object} action |
| * @return {Page} |
| */ |
| CurrentPageState.changeForSearch = function(action) { |
| if (action.term) { |
| return { |
| pageType: PageType.SEARCH, |
| selectedAppId: null, |
| }; |
| } else { |
| return { |
| pageType: PageType.MAIN, |
| selectedAppId: null, |
| }; |
| } |
| }; |
| /** |
| * @param {Page} currentPage |
| * @param {Object} action |
| * @return {Page} |
| */ |
| CurrentPageState.removeApp = function(currentPage, action) { |
| if (currentPage.pageType === PageType.DETAIL && |
| currentPage.selectedAppId === action.id) { |
| return { |
| pageType: PageType.MAIN, |
| selectedAppId: null, |
| }; |
| } else { |
| return currentPage; |
| } |
| }; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {Page} currentPage |
| * @param {Object} action |
| * @return {Page} |
| */ |
| CurrentPageState.updateCurrentPage = function(apps, currentPage, action) { |
| switch (action.name) { |
| case 'start-search': |
| return CurrentPageState.changeForSearch(action); |
| case 'clear-search': |
| return CurrentPageState.changeForSearch(action); |
| case 'change-page': |
| return CurrentPageState.changePage(apps, action); |
| case 'remove-app': |
| return CurrentPageState.removeApp(currentPage, action); |
| default: |
| return currentPage; |
| } |
| }; |
| |
| const SearchState = {}; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {SearchState} search |
| * @param {Object} action |
| * @return {SearchState} |
| */ |
| SearchState.startSearch = function(apps, search, action) { |
| const results = []; |
| for (const app of Object.values(apps)) { |
| if (app.title.includes(action.term)) { |
| results.push(app); |
| } |
| } |
| return /** @type {SearchState} */ (Object.assign({}, search, { |
| term: action.term, |
| results: results, |
| })); |
| }; |
| |
| /** @return {SearchState} */ |
| SearchState.clearSearch = function() { |
| return { |
| term: null, |
| results: null, |
| }; |
| }; |
| |
| /** |
| * @param {AppMap} apps |
| * @param {SearchState} search |
| * @param {Object} action |
| * @return {SearchState} |
| */ |
| SearchState.updateSearch = function(apps, search, action) { |
| switch (action.name) { |
| case 'start-search': |
| return SearchState.startSearch(apps, search, action); |
| case 'clear-search': |
| return SearchState.clearSearch(); |
| default: |
| return search; |
| } |
| }; |
| |
| /** |
| * Root reducer for the App Management page. This is called by the store in |
| * response to an action, and the return value is used to update the UI. |
| * @param {!AppManagementPageState} state |
| * @param {Object} action |
| * @return {!AppManagementPageState} |
| */ |
| function reduceAction(state, action) { |
| return { |
| apps: AppState.updateApps(state.apps, action), |
| search: SearchState.updateSearch(state.apps, state.search, action), |
| currentPage: CurrentPageState.updateCurrentPage( |
| state.apps, state.currentPage, action), |
| }; |
| } |
| |
| return { |
| reduceAction: reduceAction, |
| AppState: AppState, |
| CurrentPageState: CurrentPageState, |
| SearchState: SearchState, |
| }; |
| }); |