Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import * as projectV0 from 'reducers/projectV0.js'; |
| 6 | import {combineReducers} from 'redux'; |
| 7 | import {createReducer, createRequestReducer} from './redux-helpers.js'; |
| 8 | import {createSelector} from 'reselect'; |
| 9 | import {prpcClient} from 'prpc-client-instance.js'; |
| 10 | import {SITEWIDE_DEFAULT_CAN, parseColSpec} from 'shared/issue-fields.js'; |
| 11 | |
| 12 | // Actions |
| 13 | const SET_PAGE_TITLE = 'SET_PAGE_TITLE'; |
| 14 | const SET_HEADER_TITLE = 'SET_HEADER_TITLE'; |
| 15 | export const SET_QUERY_PARAMS = 'SET_QUERY_PARAMS'; |
| 16 | |
| 17 | // Async actions |
| 18 | const GET_SERVER_STATUS_FAILURE = 'GET_SERVER_STATUS_FAILURE'; |
| 19 | const GET_SERVER_STATUS_START = 'GET_SERVER_STATUS_START'; |
| 20 | const GET_SERVER_STATUS_SUCCESS = 'GET_SERVER_STATUS_SUCCESS'; |
| 21 | |
| 22 | /* State Shape |
| 23 | { |
| 24 | bannerMessage: String, |
| 25 | bannerTime: Number, |
| 26 | pageTitle: String, |
| 27 | headerTitle: String, |
| 28 | queryParams: Object, |
| 29 | readOnly: Boolean, |
| 30 | requests: { |
| 31 | serverStatus: Object, |
| 32 | }, |
| 33 | } |
| 34 | */ |
| 35 | |
| 36 | // Reducers |
| 37 | const bannerMessageReducer = createReducer('', { |
| 38 | [GET_SERVER_STATUS_SUCCESS]: |
| 39 | (_state, action) => action.serverStatus.bannerMessage || '', |
| 40 | }); |
| 41 | |
| 42 | const bannerTimeReducer = createReducer(0, { |
| 43 | [GET_SERVER_STATUS_SUCCESS]: |
| 44 | (_state, action) => action.serverStatus.bannerTime || 0, |
| 45 | }); |
| 46 | |
| 47 | /** |
| 48 | * Handle state for the current document title. |
| 49 | */ |
| 50 | const pageTitleReducer = createReducer('', { |
| 51 | [SET_PAGE_TITLE]: (_state, {title}) => title, |
| 52 | }); |
| 53 | |
| 54 | const headerTitleReducer = createReducer('', { |
| 55 | [SET_HEADER_TITLE]: (_state, {title}) => title, |
| 56 | }); |
| 57 | |
| 58 | const queryParamsReducer = createReducer({}, { |
| 59 | [SET_QUERY_PARAMS]: (_state, {queryParams}) => queryParams || {}, |
| 60 | }); |
| 61 | |
| 62 | const readOnlyReducer = createReducer(false, { |
| 63 | [GET_SERVER_STATUS_SUCCESS]: |
| 64 | (_state, action) => action.serverStatus.readOnly || false, |
| 65 | }); |
| 66 | |
| 67 | const requestsReducer = combineReducers({ |
| 68 | serverStatus: createRequestReducer( |
| 69 | GET_SERVER_STATUS_START, |
| 70 | GET_SERVER_STATUS_SUCCESS, |
| 71 | GET_SERVER_STATUS_FAILURE), |
| 72 | }); |
| 73 | |
| 74 | export const reducer = combineReducers({ |
| 75 | bannerMessage: bannerMessageReducer, |
| 76 | bannerTime: bannerTimeReducer, |
| 77 | readOnly: readOnlyReducer, |
| 78 | queryParams: queryParamsReducer, |
| 79 | pageTitle: pageTitleReducer, |
| 80 | headerTitle: headerTitleReducer, |
| 81 | |
| 82 | requests: requestsReducer, |
| 83 | }); |
| 84 | |
| 85 | // Selectors |
| 86 | export const sitewide = (state) => state.sitewide || {}; |
| 87 | export const bannerMessage = |
| 88 | createSelector(sitewide, (sitewide) => sitewide.bannerMessage); |
| 89 | export const bannerTime = |
| 90 | createSelector(sitewide, (sitewide) => sitewide.bannerTime); |
| 91 | export const queryParams = |
| 92 | createSelector(sitewide, (sitewide) => sitewide.queryParams || {}); |
| 93 | export const pageTitle = createSelector( |
| 94 | sitewide, projectV0.viewedConfig, |
| 95 | (sitewide, projectConfig) => { |
| 96 | const titlePieces = []; |
| 97 | |
| 98 | // If a specific page specifies its own page title, add that |
| 99 | // to the beginning of the title. |
| 100 | if (sitewide.pageTitle) { |
| 101 | titlePieces.push(sitewide.pageTitle); |
| 102 | } |
| 103 | |
| 104 | // If the user is viewing a project, add the project data. |
| 105 | if (projectConfig && projectConfig.projectName) { |
| 106 | titlePieces.push(projectConfig.projectName); |
| 107 | } |
| 108 | |
| 109 | return titlePieces.join(' - ') || 'Monorail'; |
| 110 | }); |
| 111 | export const headerTitle = |
| 112 | createSelector(sitewide, (sitewide) => sitewide.headerTitle); |
| 113 | export const readOnly = |
| 114 | createSelector(sitewide, (sitewide) => sitewide.readOnly); |
| 115 | |
| 116 | /** |
| 117 | * Computes the issue list columns from the URL parameters. |
| 118 | */ |
| 119 | export const currentColumns = createSelector( |
| 120 | queryParams, |
| 121 | (params = {}) => params.colspec ? parseColSpec(params.colspec) : null); |
| 122 | |
| 123 | /** |
| 124 | * Get the default canned query for the currently viewed project. |
| 125 | * Note: Projects cannot configure a per-project default canned query, |
| 126 | * so there is only a sitewide default. |
| 127 | */ |
| 128 | export const currentCan = createSelector(queryParams, |
| 129 | (params) => params.can || SITEWIDE_DEFAULT_CAN); |
| 130 | |
| 131 | /** |
| 132 | * Compute the current issue search query that the user has |
| 133 | * entered for a project, based on queryParams and the default |
| 134 | * project search. |
| 135 | */ |
| 136 | export const currentQuery = createSelector( |
| 137 | projectV0.defaultQuery, |
| 138 | queryParams, |
| 139 | (defaultQuery, params = {}) => { |
| 140 | // Make sure entering an empty search still works. |
| 141 | if (params.q === '') return params.q; |
| 142 | return params.q || defaultQuery; |
| 143 | }); |
| 144 | |
| 145 | export const requests = createSelector(sitewide, |
| 146 | (sitewide) => sitewide.requests || {}); |
| 147 | |
| 148 | // Action Creators |
| 149 | export const setQueryParams = |
| 150 | (queryParams) => ({type: SET_QUERY_PARAMS, queryParams}); |
| 151 | |
| 152 | export const setPageTitle = (title) => ({type: SET_PAGE_TITLE, title}); |
| 153 | |
| 154 | export const setHeaderTitle = (title) => ({type: SET_HEADER_TITLE, title}); |
| 155 | |
| 156 | export const getServerStatus = () => async (dispatch) => { |
| 157 | dispatch({type: GET_SERVER_STATUS_START}); |
| 158 | |
| 159 | try { |
| 160 | const serverStatus = await prpcClient.call( |
| 161 | 'monorail.Sitewide', 'GetServerStatus', {}); |
| 162 | |
| 163 | dispatch({type: GET_SERVER_STATUS_SUCCESS, serverStatus}); |
| 164 | } catch (error) { |
| 165 | dispatch({type: GET_SERVER_STATUS_FAILURE, error}); |
| 166 | } |
| 167 | }; |