chromium/chrome/browser/resources/ash/settings/common/app_management/reducers.ts

// Copyright 2018 The Chromium Authors
// 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.
 */

import {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
import {assertNotReached} from 'chrome://resources/js/assert.js';

import {AddAppAction, AppManagementActions, ChangeAppAction, RemoveAppAction} from './actions.js';
import {AppManagementPageState, AppMap} from './store.js';

function addApp(apps: AppMap, action: AddAppAction): AppMap {
  if (apps[action.app.id]) {
    const stringifyApp = (app: App): string => {
      return `id: ${app.id}, type: ${app.type}, install source: ${
          app.installReason} title: ${app.title}`;
    };
    const errorMessage = `Attempted to add an app that already exists.
                          New app: ${stringifyApp(action.app)}.
                          Old app: ${stringifyApp(apps[action.app.id])}.`;
    assertNotReached(errorMessage);
  }
  return {...apps, [action.app.id]: action.app};
}

function changeApp(apps: AppMap, action: ChangeAppAction): AppMap {
  // If the app doesn't exist, that means that the app that has been changed
  // does not need to be shown in App Management.
  if (!apps[action.app.id]) {
    return apps;
  }
  return {...apps, [action.app.id]: action.app};
}

function removeApp(apps: AppMap, action: RemoveAppAction): AppMap {
  if (!apps.hasOwnProperty(action.id)) {
    return apps;
  }

  delete apps[action.id];
  return {...apps};
}

export function updateApps(apps: AppMap, action: AppManagementActions): AppMap {
  switch (action.name) {
    case 'add-app':
      return addApp(apps, action as AddAppAction);
    case 'change-app':
      return changeApp(apps, action as ChangeAppAction);
    case 'remove-app':
      return removeApp(apps, action as RemoveAppAction);
    default:
      return apps;
  }
}

function updateSelectedAppId(
    selectedAppId: string|null, action: AppManagementActions): string|null {
  switch (action.name) {
    case 'update-selected-app-id':
      return action.value;
    case 'remove-app':
      if (selectedAppId === action.id) {
        return null;
      }
      return selectedAppId;
    default:
      return selectedAppId;
  }
}

function updateSubAppToParentAppId(
    subAppToParentAppId: Record<string, string>,
    action: AppManagementActions): Record<string, string> {
  switch (action.name) {
    case 'update-sub-app-to-parent-app-id':
      if (action.parent) {
        return {...subAppToParentAppId, [action.subApp]: action.parent};
      }
      delete subAppToParentAppId[action.subApp];
      return {...subAppToParentAppId};
    default:
      return subAppToParentAppId;
  }
}

/**
 * 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.
 */
export function reduceAction(
    state: AppManagementPageState,
    action: AppManagementActions): AppManagementPageState {
  return {
    apps: updateApps(state.apps, action),
    selectedAppId: updateSelectedAppId(state.selectedAppId, action),
    subAppToParentAppId:
        updateSubAppToParentAppId(state.subAppToParentAppId, action),
  };
}