blob: f7e20d73f3a564733d81c32a906d63c9654e9df6 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001// 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
5import * as projectV0 from 'reducers/projectV0.js';
6import {combineReducers} from 'redux';
7import {createReducer, createRequestReducer} from './redux-helpers.js';
8import {createSelector} from 'reselect';
9import {prpcClient} from 'prpc-client-instance.js';
10import {SITEWIDE_DEFAULT_CAN, parseColSpec} from 'shared/issue-fields.js';
11
12// Actions
13const SET_PAGE_TITLE = 'SET_PAGE_TITLE';
14const SET_HEADER_TITLE = 'SET_HEADER_TITLE';
15export const SET_QUERY_PARAMS = 'SET_QUERY_PARAMS';
16
17// Async actions
18const GET_SERVER_STATUS_FAILURE = 'GET_SERVER_STATUS_FAILURE';
19const GET_SERVER_STATUS_START = 'GET_SERVER_STATUS_START';
20const 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
37const bannerMessageReducer = createReducer('', {
38 [GET_SERVER_STATUS_SUCCESS]:
39 (_state, action) => action.serverStatus.bannerMessage || '',
40});
41
42const 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 */
50const pageTitleReducer = createReducer('', {
51 [SET_PAGE_TITLE]: (_state, {title}) => title,
52});
53
54const headerTitleReducer = createReducer('', {
55 [SET_HEADER_TITLE]: (_state, {title}) => title,
56});
57
58const queryParamsReducer = createReducer({}, {
59 [SET_QUERY_PARAMS]: (_state, {queryParams}) => queryParams || {},
60});
61
62const readOnlyReducer = createReducer(false, {
63 [GET_SERVER_STATUS_SUCCESS]:
64 (_state, action) => action.serverStatus.readOnly || false,
65});
66
67const requestsReducer = combineReducers({
68 serverStatus: createRequestReducer(
69 GET_SERVER_STATUS_START,
70 GET_SERVER_STATUS_SUCCESS,
71 GET_SERVER_STATUS_FAILURE),
72});
73
74export 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
86export const sitewide = (state) => state.sitewide || {};
87export const bannerMessage =
88 createSelector(sitewide, (sitewide) => sitewide.bannerMessage);
89export const bannerTime =
90 createSelector(sitewide, (sitewide) => sitewide.bannerTime);
91export const queryParams =
92 createSelector(sitewide, (sitewide) => sitewide.queryParams || {});
93export 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 });
111export const headerTitle =
112 createSelector(sitewide, (sitewide) => sitewide.headerTitle);
113export const readOnly =
114 createSelector(sitewide, (sitewide) => sitewide.readOnly);
115
116/**
117 * Computes the issue list columns from the URL parameters.
118 */
119export 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*/
128export 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 */
136export 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
145export const requests = createSelector(sitewide,
146 (sitewide) => sitewide.requests || {});
147
148// Action Creators
149export const setQueryParams =
150 (queryParams) => ({type: SET_QUERY_PARAMS, queryParams});
151
152export const setPageTitle = (title) => ({type: SET_PAGE_TITLE, title});
153
154export const setHeaderTitle = (title) => ({type: SET_HEADER_TITLE, title});
155
156export 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};