Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static_src/reducers/stars.js b/static_src/reducers/stars.js
new file mode 100644
index 0000000..b67ff9d
--- /dev/null
+++ b/static_src/reducers/stars.js
@@ -0,0 +1,172 @@
+// Copyright 2020 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 Star actions, selectors, and reducers organized into
+ * a single Redux "Duck" that manages updating and retrieving star state
+ * on the frontend.
+ *
+ * Reference: https://github.com/erikras/ducks-modular-redux
+ */
+
+import {combineReducers} from 'redux';
+import {createReducer, createRequestReducer,
+ createKeyedRequestReducer} from './redux-helpers.js';
+import {prpcClient} from 'prpc-client-instance.js';
+import {projectAndUserToStarName} from 'shared/converters.js';
+import 'shared/typedef.js';
+
+/** @typedef {import('redux').AnyAction} AnyAction */
+
+// Actions
+export const LIST_PROJECTS_START = 'stars/LIST_PROJECTS_START';
+export const LIST_PROJECTS_SUCCESS = 'stars/LIST_PROJECTS_SUCCESS';
+export const LIST_PROJECTS_FAILURE = 'stars/LIST_PROJECTS_FAILURE';
+
+export const STAR_PROJECT_START = 'stars/STAR_PROJECT_START';
+export const STAR_PROJECT_SUCCESS = 'stars/STAR_PROJECT_SUCCESS';
+export const STAR_PROJECT_FAILURE = 'stars/STAR_PROJECT_FAILURE';
+
+export const UNSTAR_PROJECT_START = 'stars/UNSTAR_PROJECT_START';
+export const UNSTAR_PROJECT_SUCCESS = 'stars/UNSTAR_PROJECT_SUCCESS';
+export const UNSTAR_PROJECT_FAILURE = 'stars/UNSTAR_PROJECT_FAILURE';
+
+/* State Shape
+{
+ byName: Object<StarName, Star>,
+
+ requests: {
+ listProjects: ReduxRequestState,
+ },
+}
+*/
+
+/**
+ * All star data indexed by resource name.
+ * @param {Object<ProjectName, Star>} state Existing Project data.
+ * @param {AnyAction} action
+ * @param {Array<Star>} action.star The Stars that were fetched.
+ * @param {ProjectStar} action.projectStar A single ProjectStar that was
+ * created.
+ * @param {StarName} action.starName The StarName that was mutated.
+ * @return {Object<ProjectName, Star>}
+ */
+export const byNameReducer = createReducer({}, {
+ [LIST_PROJECTS_SUCCESS]: (state, {stars}) => {
+ const newStars = {};
+ stars.forEach((star) => {
+ newStars[star.name] = star;
+ });
+ return {...state, ...newStars};
+ },
+ [STAR_PROJECT_SUCCESS]: (state, {projectStar}) => {
+ return {...state, [projectStar.name]: projectStar};
+ },
+ [UNSTAR_PROJECT_SUCCESS]: (state, {starName}) => {
+ const newState = {...state};
+ delete newState[starName];
+ return newState;
+ },
+});
+
+
+const requestsReducer = combineReducers({
+ listProjects: createRequestReducer(LIST_PROJECTS_START,
+ LIST_PROJECTS_SUCCESS, LIST_PROJECTS_FAILURE),
+ starProject: createKeyedRequestReducer(STAR_PROJECT_START,
+ STAR_PROJECT_SUCCESS, STAR_PROJECT_FAILURE),
+ unstarProject: createKeyedRequestReducer(UNSTAR_PROJECT_START,
+ UNSTAR_PROJECT_SUCCESS, UNSTAR_PROJECT_FAILURE),
+});
+
+
+export const reducer = combineReducers({
+ byName: byNameReducer,
+ requests: requestsReducer,
+});
+
+
+/**
+ * Returns normalized star data by name.
+ * @param {any} state
+ * @return {Object<StarName, Star>}
+ * @private
+ */
+export const byName = (state) => state.stars.byName;
+
+/**
+ * Returns star requests.
+ * @param {any} state
+ * @return {Object<string, ReduxRequestState>}
+ */
+export const requests = (state) => state.stars.requests;
+
+/**
+ * Retrieves the starred projects for a given user.
+ * @param {UserName} user The resource name of the user to fetch
+ * starred projects for.
+ * @return {function(function): Promise<void>}
+ */
+export const listProjects = (user) => async (dispatch) => {
+ dispatch({type: LIST_PROJECTS_START});
+
+ try {
+ const {projectStars} = await prpcClient.call(
+ 'monorail.v3.Users', 'ListProjectStars', {parent: user});
+ dispatch({type: LIST_PROJECTS_SUCCESS, stars: projectStars});
+ } catch (error) {
+ dispatch({type: LIST_PROJECTS_FAILURE, error});
+ };
+};
+
+/**
+ * Stars a given project.
+ * @param {ProjectName} project The resource name of the project to star.
+ * @param {UserName} user The resource name of the user who is starring
+ * the issue. This will always be the currently logged in user.
+ * @return {function(function): Promise<void>}
+ */
+export const starProject = (project, user) => async (dispatch) => {
+ const requestKey = projectAndUserToStarName(project, user);
+ dispatch({type: STAR_PROJECT_START, requestKey});
+ try {
+ const projectStar = await prpcClient.call(
+ 'monorail.v3.Users', 'StarProject', {project});
+ dispatch({type: STAR_PROJECT_SUCCESS, requestKey, projectStar});
+ } catch (error) {
+ dispatch({type: STAR_PROJECT_FAILURE, requestKey, error});
+ };
+};
+
+/**
+ * Unstars a given project.
+ * @param {ProjectName} project The resource name of the project to unstar.
+ * @param {UserName} user The resource name of the user who is unstarring
+ * the issue. This will always be the currently logged in user, but
+ * passing in the user's resource name is necessary to make it possible to
+ * generate the resource name of the removed star.
+ * @return {function(function): Promise<void>}
+ */
+export const unstarProject = (project, user) => async (dispatch) => {
+ const starName = projectAndUserToStarName(project, user);
+ const requestKey = starName;
+ dispatch({type: UNSTAR_PROJECT_START, requestKey});
+
+ try {
+ await prpcClient.call(
+ 'monorail.v3.Users', 'UnStarProject', {project});
+ dispatch({type: UNSTAR_PROJECT_SUCCESS, requestKey, starName});
+ } catch (error) {
+ dispatch({type: UNSTAR_PROJECT_FAILURE, requestKey, error});
+ };
+};
+
+export const stars = {
+ reducer,
+ byName,
+ requests,
+ listProjects,
+ starProject,
+ unstarProject,
+};