reducer.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { createSlice, createEntityAdapter, Reducer, AnyAction, PayloadAction } from '@reduxjs/toolkit';
  2. import { PanelPlugin } from '@grafana/data';
  3. import { STATE_PREFIX } from '../constants';
  4. import { CatalogPlugin, PluginListDisplayMode, ReducerState, RequestStatus } from '../types';
  5. import { fetchAll, fetchDetails, install, uninstall, loadPluginDashboards, panelPluginLoaded } from './actions';
  6. export const pluginsAdapter = createEntityAdapter<CatalogPlugin>();
  7. const isPendingRequest = (action: AnyAction) => new RegExp(`${STATE_PREFIX}\/(.*)\/pending`).test(action.type);
  8. const isFulfilledRequest = (action: AnyAction) => new RegExp(`${STATE_PREFIX}\/(.*)\/fulfilled`).test(action.type);
  9. const isRejectedRequest = (action: AnyAction) => new RegExp(`${STATE_PREFIX}\/(.*)\/rejected`).test(action.type);
  10. // Extract the trailing '/pending', '/rejected', or '/fulfilled'
  11. const getOriginalActionType = (type: string) => {
  12. const separator = type.lastIndexOf('/');
  13. return type.substring(0, separator);
  14. };
  15. const slice = createSlice({
  16. name: 'plugins',
  17. initialState: {
  18. items: pluginsAdapter.getInitialState(),
  19. requests: {},
  20. settings: {
  21. displayMode: PluginListDisplayMode.Grid,
  22. },
  23. // Backwards compatibility
  24. // (we need to have the following fields in the store as well to be backwards compatible with other parts of Grafana)
  25. // TODO<remove once the "plugin_admin_enabled" feature flag is removed>
  26. plugins: [],
  27. errors: [],
  28. searchQuery: '',
  29. hasFetched: false,
  30. dashboards: [],
  31. isLoadingPluginDashboards: false,
  32. panels: {},
  33. } as ReducerState,
  34. reducers: {
  35. setDisplayMode(state, action: PayloadAction<PluginListDisplayMode>) {
  36. state.settings.displayMode = action.payload;
  37. },
  38. },
  39. extraReducers: (builder) =>
  40. builder
  41. // Fetch All
  42. .addCase(fetchAll.fulfilled, (state, action) => {
  43. pluginsAdapter.upsertMany(state.items, action.payload);
  44. })
  45. // Fetch Details
  46. .addCase(fetchDetails.fulfilled, (state, action) => {
  47. pluginsAdapter.updateOne(state.items, action.payload);
  48. })
  49. // Install
  50. .addCase(install.fulfilled, (state, action) => {
  51. pluginsAdapter.updateOne(state.items, action.payload);
  52. })
  53. // Uninstall
  54. .addCase(uninstall.fulfilled, (state, action) => {
  55. pluginsAdapter.updateOne(state.items, action.payload);
  56. })
  57. // Load a panel plugin (backward-compatibility)
  58. // TODO<remove once the "plugin_admin_enabled" feature flag is removed>
  59. .addCase(panelPluginLoaded, (state, action: PayloadAction<PanelPlugin>) => {
  60. state.panels[action.payload.meta.id] = action.payload;
  61. })
  62. // Start loading panel dashboards (backward-compatibility)
  63. // TODO<remove once the "plugin_admin_enabled" feature flag is removed>
  64. .addCase(loadPluginDashboards.pending, (state, action) => {
  65. state.isLoadingPluginDashboards = true;
  66. state.dashboards = [];
  67. })
  68. // Load panel dashboards (backward-compatibility)
  69. // TODO<remove once the "plugin_admin_enabled" feature flag is removed>
  70. .addCase(loadPluginDashboards.fulfilled, (state, action) => {
  71. state.isLoadingPluginDashboards = false;
  72. state.dashboards = action.payload;
  73. })
  74. .addMatcher(isPendingRequest, (state, action) => {
  75. state.requests[getOriginalActionType(action.type)] = {
  76. status: RequestStatus.Pending,
  77. };
  78. })
  79. .addMatcher(isFulfilledRequest, (state, action) => {
  80. state.requests[getOriginalActionType(action.type)] = {
  81. status: RequestStatus.Fulfilled,
  82. };
  83. })
  84. .addMatcher(isRejectedRequest, (state, action) => {
  85. state.requests[getOriginalActionType(action.type)] = {
  86. status: RequestStatus.Rejected,
  87. error: action.payload,
  88. };
  89. }),
  90. });
  91. export const { setDisplayMode } = slice.actions;
  92. export const reducer: Reducer<ReducerState, AnyAction> = slice.reducer;